TabViewTests.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  1. using System.Globalization;
  2. using Xunit.Abstractions;
  3. namespace Terminal.Gui.ViewsTests;
  4. public class TabViewTests
  5. {
  6. private readonly ITestOutputHelper _output;
  7. public TabViewTests (ITestOutputHelper output) { _output = output; }
  8. [Fact]
  9. public void AddTab_SameTabMoreThanOnce ()
  10. {
  11. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  12. Assert.Equal (2, tv.Tabs.Count);
  13. // Tab is already part of the control so shouldn't result in duplication
  14. tv.AddTab (tab1, false);
  15. tv.AddTab (tab1, false);
  16. tv.AddTab (tab1, false);
  17. tv.AddTab (tab1, false);
  18. Assert.Equal (2, tv.Tabs.Count);
  19. // Shutdown must be called to safely clean up Application if Init has been called
  20. Application.Shutdown ();
  21. }
  22. [Fact]
  23. public void AddTwoTabs_SecondIsSelected ()
  24. {
  25. InitFakeDriver ();
  26. var tv = new TabView ();
  27. Tab tab1;
  28. Tab tab2;
  29. tv.AddTab (tab1 = new Tab { DisplayText = "Tab1", View = new TextField { Text = "hi" } }, false);
  30. tv.AddTab (tab2 = new Tab { DisplayText = "Tab1", View = new Label { Text = "hi2" } }, true);
  31. Assert.Equal (2, tv.Tabs.Count);
  32. Assert.Equal (tab2, tv.SelectedTab);
  33. Application.Shutdown ();
  34. }
  35. [Fact]
  36. public void EnsureSelectedTabVisible_MustScroll ()
  37. {
  38. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  39. // Make tab width small to force only one tab visible at once
  40. tv.Width = 4;
  41. tv.SelectedTab = tab1;
  42. Assert.Equal (0, tv.TabScrollOffset);
  43. tv.EnsureSelectedTabIsVisible ();
  44. Assert.Equal (0, tv.TabScrollOffset);
  45. // Asking to show tab2 should automatically move scroll offset accordingly
  46. tv.SelectedTab = tab2;
  47. Assert.Equal (1, tv.TabScrollOffset);
  48. // Shutdown must be called to safely clean up Application if Init has been called
  49. Application.Shutdown ();
  50. }
  51. [Fact]
  52. public void EnsureSelectedTabVisible_NullSelect ()
  53. {
  54. TabView tv = GetTabView ();
  55. tv.SelectedTab = null;
  56. Assert.Null (tv.SelectedTab);
  57. Assert.Equal (0, tv.TabScrollOffset);
  58. tv.EnsureSelectedTabIsVisible ();
  59. Assert.Null (tv.SelectedTab);
  60. Assert.Equal (0, tv.TabScrollOffset);
  61. Application.Shutdown ();
  62. }
  63. [Fact]
  64. public void EnsureValidScrollOffsets_TabScrollOffset ()
  65. {
  66. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  67. // Make tab width small to force only one tab visible at once
  68. tv.Width = 4;
  69. tv.SelectedTab = tab1;
  70. Assert.Equal (0, tv.TabScrollOffset);
  71. tv.TabScrollOffset = 10;
  72. tv.SelectedTab = tab2;
  73. Assert.Equal (1, tv.TabScrollOffset);
  74. tv.TabScrollOffset = -1;
  75. tv.SelectedTab = tab1;
  76. Assert.Equal (0, tv.TabScrollOffset);
  77. // Shutdown must be called to safely clean up Application if Init has been called
  78. Application.Shutdown ();
  79. }
  80. [Fact]
  81. [AutoInitShutdown]
  82. public void MouseClick_ChangesTab ()
  83. {
  84. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  85. tv.Width = 20;
  86. tv.Height = 5;
  87. tv.LayoutSubviews ();
  88. tv.Draw ();
  89. View tabRow = tv.Subviews [0];
  90. Assert.Equal ("TabRowView", tabRow.GetType ().Name);
  91. TestHelpers.AssertDriverContentsAre (
  92. @"
  93. ╭────┬────╮
  94. │Tab1│Tab2│
  95. │ ╰────┴────────╮
  96. │hi │
  97. └──────────────────┘
  98. ",
  99. _output
  100. );
  101. Tab clicked = null;
  102. tv.TabClicked += (s, e) => { clicked = e.Tab; };
  103. Application.Top.Add (tv);
  104. Application.Begin (Application.Top);
  105. MouseEventEventArgs args;
  106. // Waving mouse around does not trigger click
  107. for (var i = 0; i < 100; i++)
  108. {
  109. args = new MouseEventEventArgs (
  110. new MouseEvent { X = i, Y = 1, Flags = MouseFlags.ReportMousePosition }
  111. );
  112. Application.OnMouseEvent (args);
  113. Application.Refresh ();
  114. Assert.Null (clicked);
  115. Assert.Equal (tab1, tv.SelectedTab);
  116. }
  117. args = new MouseEventEventArgs (
  118. new MouseEvent { X = 3, Y = 1, Flags = MouseFlags.Button1Clicked }
  119. );
  120. Application.OnMouseEvent (args);
  121. Application.Refresh ();
  122. Assert.Equal (tab1, clicked);
  123. Assert.Equal (tab1, tv.SelectedTab);
  124. // Click to tab2
  125. args = new MouseEventEventArgs (
  126. new MouseEvent { X = 6, Y = 1, Flags = MouseFlags.Button1Clicked }
  127. );
  128. Application.OnMouseEvent (args);
  129. Application.Refresh ();
  130. Assert.Equal (tab2, clicked);
  131. Assert.Equal (tab2, tv.SelectedTab);
  132. // cancel navigation
  133. tv.TabClicked += (s, e) =>
  134. {
  135. clicked = e.Tab;
  136. e.MouseEvent.Handled = true;
  137. };
  138. args = new MouseEventEventArgs (
  139. new MouseEvent { X = 3, Y = 1, Flags = MouseFlags.Button1Clicked }
  140. );
  141. Application.OnMouseEvent (args);
  142. Application.Refresh ();
  143. // Tab 1 was clicked but event handler blocked navigation
  144. Assert.Equal (tab1, clicked);
  145. Assert.Equal (tab2, tv.SelectedTab);
  146. args = new MouseEventEventArgs (
  147. new MouseEvent { X = 12, Y = 1, Flags = MouseFlags.Button1Clicked }
  148. );
  149. Application.OnMouseEvent (args);
  150. Application.Refresh ();
  151. // Clicking beyond last tab should raise event with null Tab
  152. Assert.Null (clicked);
  153. Assert.Equal (tab2, tv.SelectedTab);
  154. }
  155. [Fact]
  156. [AutoInitShutdown]
  157. public void MouseClick_Right_Left_Arrows_ChangesTab ()
  158. {
  159. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  160. tv.Width = 7;
  161. tv.Height = 5;
  162. tv.LayoutSubviews ();
  163. tv.Draw ();
  164. View tabRow = tv.Subviews [0];
  165. Assert.Equal ("TabRowView", tabRow.GetType ().Name);
  166. TestHelpers.AssertDriverContentsAre (
  167. @"
  168. ╭────╮
  169. │Tab1│
  170. │ ╰►
  171. │hi │
  172. └─────┘
  173. ",
  174. _output
  175. );
  176. Tab clicked = null;
  177. tv.TabClicked += (s, e) => { clicked = e.Tab; };
  178. Tab oldChanged = null;
  179. Tab newChanged = null;
  180. tv.SelectedTabChanged += (s, e) =>
  181. {
  182. oldChanged = e.OldTab;
  183. newChanged = e.NewTab;
  184. };
  185. Application.Top.Add (tv);
  186. Application.Begin (Application.Top);
  187. // Click the right arrow
  188. var args = new MouseEventEventArgs (
  189. new MouseEvent { X = 6, Y = 2, Flags = MouseFlags.Button1Clicked }
  190. );
  191. Application.OnMouseEvent (args);
  192. Application.Refresh ();
  193. Assert.Null (clicked);
  194. Assert.Equal (tab1, oldChanged);
  195. Assert.Equal (tab2, newChanged);
  196. Assert.Equal (tab2, tv.SelectedTab);
  197. TestHelpers.AssertDriverContentsAre (
  198. @"
  199. ╭────╮
  200. │Tab2│
  201. ◄ ╰╮
  202. │hi2 │
  203. └─────┘
  204. ",
  205. _output
  206. );
  207. // Click the left arrow
  208. args = new MouseEventEventArgs (
  209. new MouseEvent { X = 0, Y = 2, Flags = MouseFlags.Button1Clicked }
  210. );
  211. Application.OnMouseEvent (args);
  212. Application.Refresh ();
  213. Assert.Null (clicked);
  214. Assert.Equal (tab2, oldChanged);
  215. Assert.Equal (tab1, newChanged);
  216. Assert.Equal (tab1, tv.SelectedTab);
  217. TestHelpers.AssertDriverContentsAre (
  218. @"
  219. ╭────╮
  220. │Tab1│
  221. │ ╰►
  222. │hi │
  223. └─────┘
  224. ",
  225. _output
  226. );
  227. }
  228. [Fact]
  229. [AutoInitShutdown]
  230. public void MouseClick_Right_Left_Arrows_ChangesTab_With_Border ()
  231. {
  232. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  233. tv.Width = 9;
  234. tv.Height = 7;
  235. Assert.Equal (LineStyle.None, tv.BorderStyle);
  236. tv.BorderStyle = LineStyle.Single;
  237. tv.LayoutSubviews ();
  238. tv.Draw ();
  239. View tabRow = tv.Subviews [0];
  240. Assert.Equal ("TabRowView", tabRow.GetType ().Name);
  241. TestHelpers.AssertDriverContentsAre (
  242. @"
  243. ┌───────┐
  244. │╭────╮ │
  245. ││Tab1│ │
  246. ││ ╰►│
  247. ││hi ││
  248. │└─────┘│
  249. └───────┘
  250. ",
  251. _output
  252. );
  253. Tab clicked = null;
  254. tv.TabClicked += (s, e) => { clicked = e.Tab; };
  255. Tab oldChanged = null;
  256. Tab newChanged = null;
  257. tv.SelectedTabChanged += (s, e) =>
  258. {
  259. oldChanged = e.OldTab;
  260. newChanged = e.NewTab;
  261. };
  262. Application.Top.Add (tv);
  263. Application.Begin (Application.Top);
  264. // Click the right arrow
  265. var args = new MouseEventEventArgs (
  266. new MouseEvent { X = 7, Y = 3, Flags = MouseFlags.Button1Clicked }
  267. );
  268. Application.OnMouseEvent (args);
  269. Application.Refresh ();
  270. Assert.Null (clicked);
  271. Assert.Equal (tab1, oldChanged);
  272. Assert.Equal (tab2, newChanged);
  273. Assert.Equal (tab2, tv.SelectedTab);
  274. TestHelpers.AssertDriverContentsAre (
  275. @"
  276. ┌───────┐
  277. │╭────╮ │
  278. ││Tab2│ │
  279. │◄ ╰╮│
  280. ││hi2 ││
  281. │└─────┘│
  282. └───────┘
  283. ",
  284. _output
  285. );
  286. // Click the left arrow
  287. args = new MouseEventEventArgs (
  288. new MouseEvent { X = 1, Y = 3, Flags = MouseFlags.Button1Clicked }
  289. );
  290. Application.OnMouseEvent (args);
  291. Application.Refresh ();
  292. Assert.Null (clicked);
  293. Assert.Equal (tab2, oldChanged);
  294. Assert.Equal (tab1, newChanged);
  295. Assert.Equal (tab1, tv.SelectedTab);
  296. TestHelpers.AssertDriverContentsAre (
  297. @"
  298. ┌───────┐
  299. │╭────╮ │
  300. ││Tab1│ │
  301. ││ ╰►│
  302. ││hi ││
  303. │└─────┘│
  304. └───────┘
  305. ",
  306. _output
  307. );
  308. }
  309. [Fact]
  310. [AutoInitShutdown]
  311. public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp ()
  312. {
  313. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  314. tv.Width = 7;
  315. tv.Height = 5;
  316. var btn = new Button
  317. {
  318. Y = Pos.Bottom (tv) + 1,
  319. AutoSize = false,
  320. Height = 1,
  321. Width = 7,
  322. Text = "Ok"
  323. };
  324. Toplevel top = Application.Top;
  325. top.Add (tv, btn);
  326. Application.Begin (top);
  327. // Is the selected tab view hosting focused
  328. Assert.Equal (tab1, tv.SelectedTab);
  329. Assert.Equal (tv, top.Focused);
  330. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  331. Assert.Equal (tv.SelectedTab.View, top.Focused.MostFocused);
  332. // Press the cursor up key to focus the selected tab
  333. var args = new Key (Key.CursorUp);
  334. Application.OnKeyDown (args);
  335. Application.Refresh ();
  336. // Is the selected tab focused
  337. Assert.Equal (tab1, tv.SelectedTab);
  338. Assert.Equal (tv, top.Focused);
  339. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  340. Tab oldChanged = null;
  341. Tab newChanged = null;
  342. tv.SelectedTabChanged += (s, e) =>
  343. {
  344. oldChanged = e.OldTab;
  345. newChanged = e.NewTab;
  346. };
  347. // Press the cursor right key to select the next tab
  348. args = new Key (Key.CursorRight);
  349. Application.OnKeyDown (args);
  350. Application.Refresh ();
  351. Assert.Equal (tab1, oldChanged);
  352. Assert.Equal (tab2, newChanged);
  353. Assert.Equal (tab2, tv.SelectedTab);
  354. Assert.Equal (tv, top.Focused);
  355. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  356. // Press the cursor down key to focus the selected tab view hosting
  357. args = new Key (Key.CursorDown);
  358. Application.OnKeyDown (args);
  359. Application.Refresh ();
  360. Assert.Equal (tab2, tv.SelectedTab);
  361. Assert.Equal (tv, top.Focused);
  362. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  363. // The tab view hosting is a label which can't be focused
  364. // and the View container is the focused one
  365. Assert.Equal (tv.Subviews [1], top.Focused.MostFocused);
  366. // Press the cursor down key again will focus next view in the toplevel
  367. Application.OnKeyDown (args);
  368. Application.Refresh ();
  369. Assert.Equal (tab2, tv.SelectedTab);
  370. Assert.Equal (btn, top.Focused);
  371. Assert.Null (tv.MostFocused);
  372. Assert.Null (top.Focused.MostFocused);
  373. // Press the cursor up key to focus the selected tab view hosting again
  374. args = new Key (Key.CursorUp);
  375. Application.OnKeyDown (args);
  376. Application.Refresh ();
  377. Assert.Equal (tab2, tv.SelectedTab);
  378. Assert.Equal (tv, top.Focused);
  379. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  380. // Press the cursor up key to focus the selected tab
  381. args = new Key (Key.CursorUp);
  382. Application.OnKeyDown (args);
  383. Application.Refresh ();
  384. // Is the selected tab focused
  385. Assert.Equal (tab2, tv.SelectedTab);
  386. Assert.Equal (tv, top.Focused);
  387. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  388. // Press the cursor left key to select the previous tab
  389. args = new Key (Key.CursorLeft);
  390. Application.OnKeyDown (args);
  391. Application.Refresh ();
  392. Assert.Equal (tab2, oldChanged);
  393. Assert.Equal (tab1, newChanged);
  394. Assert.Equal (tab1, tv.SelectedTab);
  395. Assert.Equal (tv, top.Focused);
  396. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  397. // Press the end key to select the last tab
  398. args = new Key (Key.End);
  399. Application.OnKeyDown (args);
  400. Application.Refresh ();
  401. Assert.Equal (tab1, oldChanged);
  402. Assert.Equal (tab2, newChanged);
  403. Assert.Equal (tab2, tv.SelectedTab);
  404. Assert.Equal (tv, top.Focused);
  405. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  406. // Press the home key to select the first tab
  407. args = new Key (Key.Home);
  408. Application.OnKeyDown (args);
  409. Application.Refresh ();
  410. Assert.Equal (tab2, oldChanged);
  411. Assert.Equal (tab1, newChanged);
  412. Assert.Equal (tab1, tv.SelectedTab);
  413. Assert.Equal (tv, top.Focused);
  414. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  415. // Press the page down key to select the next set of tabs
  416. args = new Key (Key.PageDown);
  417. Application.OnKeyDown (args);
  418. Application.Refresh ();
  419. Assert.Equal (tab1, oldChanged);
  420. Assert.Equal (tab2, newChanged);
  421. Assert.Equal (tab2, tv.SelectedTab);
  422. Assert.Equal (tv, top.Focused);
  423. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  424. // Press the page up key to select the previous set of tabs
  425. args = new Key (Key.PageUp);
  426. Application.OnKeyDown (args);
  427. Application.Refresh ();
  428. Assert.Equal (tab2, oldChanged);
  429. Assert.Equal (tab1, newChanged);
  430. Assert.Equal (tab1, tv.SelectedTab);
  431. Assert.Equal (tv, top.Focused);
  432. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  433. }
  434. [Fact]
  435. public void RemoveAllTabs_ClearsSelection ()
  436. {
  437. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  438. tv.SelectedTab = tab1;
  439. tv.RemoveTab (tab1);
  440. tv.RemoveTab (tab2);
  441. Assert.Null (tv.SelectedTab);
  442. // Shutdown must be called to safely clean up Application if Init has been called
  443. Application.Shutdown ();
  444. }
  445. [Fact]
  446. public void RemoveTab_ChangesSelection ()
  447. {
  448. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  449. tv.SelectedTab = tab1;
  450. tv.RemoveTab (tab1);
  451. Assert.Equal (tab2, tv.SelectedTab);
  452. // Shutdown must be called to safely clean up Application if Init has been called
  453. Application.Shutdown ();
  454. }
  455. [Fact]
  456. public void RemoveTab_MultipleCalls_NotAnError ()
  457. {
  458. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  459. tv.SelectedTab = tab1;
  460. // Repeated calls to remove a tab that is not part of
  461. // the collection should be ignored
  462. tv.RemoveTab (tab1);
  463. tv.RemoveTab (tab1);
  464. tv.RemoveTab (tab1);
  465. tv.RemoveTab (tab1);
  466. Assert.Equal (tab2, tv.SelectedTab);
  467. // Shutdown must be called to safely clean up Application if Init has been called
  468. Application.Shutdown ();
  469. }
  470. [Fact]
  471. public void SelectedTabChanged_Called ()
  472. {
  473. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  474. tv.SelectedTab = tab1;
  475. Tab oldTab = null;
  476. Tab newTab = null;
  477. var called = 0;
  478. tv.SelectedTabChanged += (s, e) =>
  479. {
  480. oldTab = e.OldTab;
  481. newTab = e.NewTab;
  482. called++;
  483. };
  484. tv.SelectedTab = tab2;
  485. Assert.Equal (1, called);
  486. Assert.Equal (tab1, oldTab);
  487. Assert.Equal (tab2, newTab);
  488. // Shutdown must be called to safely clean up Application if Init has been called
  489. Application.Shutdown ();
  490. }
  491. [Fact]
  492. [AutoInitShutdown]
  493. public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 ()
  494. {
  495. TabView tv = GetTabView (out _, out _, false);
  496. tv.Width = 3;
  497. tv.Height = 5;
  498. tv.Style = new TabStyle { ShowTopLine = false };
  499. tv.ApplyStyleChanges ();
  500. tv.LayoutSubviews ();
  501. tv.Draw ();
  502. TestHelpers.AssertDriverContentsWithFrameAre (
  503. @"
  504. ││
  505. │╰►
  506. │h│
  507. │ │
  508. └─┘",
  509. _output
  510. );
  511. }
  512. [Fact]
  513. [AutoInitShutdown]
  514. public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 ()
  515. {
  516. TabView tv = GetTabView (out _, out _, false);
  517. tv.Width = 4;
  518. tv.Height = 5;
  519. tv.Style = new TabStyle { ShowTopLine = false };
  520. tv.ApplyStyleChanges ();
  521. tv.LayoutSubviews ();
  522. tv.Draw ();
  523. TestHelpers.AssertDriverContentsWithFrameAre (
  524. @"
  525. │T│
  526. │ ╰►
  527. │hi│
  528. │ │
  529. └──┘",
  530. _output
  531. );
  532. }
  533. [Fact]
  534. [AutoInitShutdown]
  535. public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ()
  536. {
  537. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  538. tv.Width = 10;
  539. tv.Height = 5;
  540. tv.Style = new TabStyle { ShowTopLine = false };
  541. tv.ApplyStyleChanges ();
  542. // Ensures that the tab bar subview gets the bounds of the parent TabView
  543. tv.LayoutSubviews ();
  544. // Test two tab names that fit
  545. tab1.DisplayText = "12";
  546. tab2.DisplayText = "13";
  547. tv.Draw ();
  548. TestHelpers.AssertDriverContentsWithFrameAre (
  549. @"
  550. │12│13│
  551. │ ╰──┴──╮
  552. │hi │
  553. │ │
  554. └────────┘",
  555. _output
  556. );
  557. tv.SelectedTab = tab2;
  558. tv.Draw ();
  559. TestHelpers.AssertDriverContentsWithFrameAre (
  560. @"
  561. │12│13│
  562. ├──╯ ╰──╮
  563. │hi2 │
  564. │ │
  565. └────────┘",
  566. _output
  567. );
  568. tv.SelectedTab = tab1;
  569. // Test first tab name too long
  570. tab1.DisplayText = "12345678910";
  571. tab2.DisplayText = "13";
  572. tv.Draw ();
  573. TestHelpers.AssertDriverContentsWithFrameAre (
  574. @"
  575. │1234567│
  576. │ ╰►
  577. │hi │
  578. │ │
  579. └────────┘",
  580. _output
  581. );
  582. //switch to tab2
  583. tv.SelectedTab = tab2;
  584. tv.Draw ();
  585. TestHelpers.AssertDriverContentsWithFrameAre (
  586. @"
  587. │13│
  588. ◄ ╰─────╮
  589. │hi2 │
  590. │ │
  591. └────────┘",
  592. _output
  593. );
  594. // now make both tabs too long
  595. tab1.DisplayText = "12345678910";
  596. tab2.DisplayText = "abcdefghijklmnopq";
  597. tv.Draw ();
  598. TestHelpers.AssertDriverContentsWithFrameAre (
  599. @"
  600. │abcdefg│
  601. ◄ ╰╮
  602. │hi2 │
  603. │ │
  604. └────────┘",
  605. _output
  606. );
  607. }
  608. [Fact]
  609. [AutoInitShutdown]
  610. public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 ()
  611. {
  612. TabView tv = GetTabView (out _, out _, false);
  613. tv.Width = 3;
  614. tv.Height = 5;
  615. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  616. tv.ApplyStyleChanges ();
  617. tv.LayoutSubviews ();
  618. tv.Draw ();
  619. TestHelpers.AssertDriverContentsWithFrameAre (
  620. @"
  621. ┌─┐
  622. │h│
  623. │ │
  624. │╭►
  625. ││ ",
  626. _output
  627. );
  628. }
  629. [Fact]
  630. [AutoInitShutdown]
  631. public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 ()
  632. {
  633. TabView tv = GetTabView (out _, out _, false);
  634. tv.Width = 4;
  635. tv.Height = 5;
  636. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  637. tv.ApplyStyleChanges ();
  638. tv.LayoutSubviews ();
  639. tv.Draw ();
  640. TestHelpers.AssertDriverContentsWithFrameAre (
  641. @"
  642. ┌──┐
  643. │hi│
  644. │ │
  645. │ ╭►
  646. │T│ ",
  647. _output
  648. );
  649. }
  650. [Fact]
  651. [AutoInitShutdown]
  652. public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames ()
  653. {
  654. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  655. tv.Width = 10;
  656. tv.Height = 5;
  657. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  658. tv.ApplyStyleChanges ();
  659. // Ensures that the tab bar subview gets the bounds of the parent TabView
  660. tv.LayoutSubviews ();
  661. // Test two tab names that fit
  662. tab1.DisplayText = "12";
  663. tab2.DisplayText = "13";
  664. tv.Draw ();
  665. TestHelpers.AssertDriverContentsWithFrameAre (
  666. @"
  667. ┌────────┐
  668. │hi │
  669. │ │
  670. │ ╭──┬──╯
  671. │12│13│ ",
  672. _output
  673. );
  674. tv.SelectedTab = tab2;
  675. tv.Draw ();
  676. TestHelpers.AssertDriverContentsWithFrameAre (
  677. @"
  678. ┌────────┐
  679. │hi2 │
  680. │ │
  681. ├──╮ ╭──╯
  682. │12│13│ ",
  683. _output
  684. );
  685. tv.SelectedTab = tab1;
  686. // Test first tab name too long
  687. tab1.DisplayText = "12345678910";
  688. tab2.DisplayText = "13";
  689. tv.Draw ();
  690. TestHelpers.AssertDriverContentsWithFrameAre (
  691. @"
  692. ┌────────┐
  693. │hi │
  694. │ │
  695. │ ╭►
  696. │1234567│ ",
  697. _output
  698. );
  699. //switch to tab2
  700. tv.SelectedTab = tab2;
  701. tv.Draw ();
  702. TestHelpers.AssertDriverContentsWithFrameAre (
  703. @"
  704. ┌────────┐
  705. │hi2 │
  706. │ │
  707. ◄ ╭─────╯
  708. │13│ ",
  709. _output
  710. );
  711. // now make both tabs too long
  712. tab1.DisplayText = "12345678910";
  713. tab2.DisplayText = "abcdefghijklmnopq";
  714. tv.Draw ();
  715. TestHelpers.AssertDriverContentsWithFrameAre (
  716. @"
  717. ┌────────┐
  718. │hi2 │
  719. │ │
  720. ◄ ╭╯
  721. │abcdefg│ ",
  722. _output
  723. );
  724. }
  725. [Fact]
  726. [AutoInitShutdown]
  727. public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 ()
  728. {
  729. TabView tv = GetTabView (out _, out _, false);
  730. tv.Width = 3;
  731. tv.Height = 5;
  732. tv.LayoutSubviews ();
  733. tv.Draw ();
  734. TestHelpers.AssertDriverContentsWithFrameAre (
  735. @"
  736. ╭╮
  737. ││
  738. │╰►
  739. │h│
  740. └─┘",
  741. _output
  742. );
  743. }
  744. [Fact]
  745. [AutoInitShutdown]
  746. public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 ()
  747. {
  748. TabView tv = GetTabView (out _, out _, false);
  749. tv.Width = 4;
  750. tv.Height = 5;
  751. tv.LayoutSubviews ();
  752. tv.Draw ();
  753. TestHelpers.AssertDriverContentsWithFrameAre (
  754. @"
  755. ╭─╮
  756. │T│
  757. │ ╰►
  758. │hi│
  759. └──┘",
  760. _output
  761. );
  762. }
  763. [Fact]
  764. [AutoInitShutdown]
  765. public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames ()
  766. {
  767. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  768. tv.Width = 10;
  769. tv.Height = 5;
  770. // Ensures that the tab bar subview gets the bounds of the parent TabView
  771. tv.LayoutSubviews ();
  772. // Test two tab names that fit
  773. tab1.DisplayText = "12";
  774. tab2.DisplayText = "13";
  775. tv.Draw ();
  776. TestHelpers.AssertDriverContentsWithFrameAre (
  777. @"
  778. ╭──┬──╮
  779. │12│13│
  780. │ ╰──┴──╮
  781. │hi │
  782. └────────┘",
  783. _output
  784. );
  785. tv.SelectedTab = tab2;
  786. tv.Draw ();
  787. TestHelpers.AssertDriverContentsWithFrameAre (
  788. @"
  789. ╭──┬──╮
  790. │12│13│
  791. ├──╯ ╰──╮
  792. │hi2 │
  793. └────────┘",
  794. _output
  795. );
  796. tv.SelectedTab = tab1;
  797. // Test first tab name too long
  798. tab1.DisplayText = "12345678910";
  799. tab2.DisplayText = "13";
  800. tv.Draw ();
  801. TestHelpers.AssertDriverContentsWithFrameAre (
  802. @"
  803. ╭───────╮
  804. │1234567│
  805. │ ╰►
  806. │hi │
  807. └────────┘",
  808. _output
  809. );
  810. //switch to tab2
  811. tv.SelectedTab = tab2;
  812. tv.Draw ();
  813. TestHelpers.AssertDriverContentsWithFrameAre (
  814. @"
  815. ╭──╮
  816. │13│
  817. ◄ ╰─────╮
  818. │hi2 │
  819. └────────┘",
  820. _output
  821. );
  822. // now make both tabs too long
  823. tab1.DisplayText = "12345678910";
  824. tab2.DisplayText = "abcdefghijklmnopq";
  825. tv.Draw ();
  826. TestHelpers.AssertDriverContentsWithFrameAre (
  827. @"
  828. ╭───────╮
  829. │abcdefg│
  830. ◄ ╰╮
  831. │hi2 │
  832. └────────┘",
  833. _output
  834. );
  835. }
  836. [Fact]
  837. [AutoInitShutdown]
  838. public void ShowTopLine_True_TabsOnBottom_False_With_Unicode ()
  839. {
  840. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  841. tv.Width = 20;
  842. tv.Height = 5;
  843. tv.LayoutSubviews ();
  844. tab1.DisplayText = "Tab0";
  845. tab2.DisplayText = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  846. tv.Draw ();
  847. TestHelpers.AssertDriverContentsWithFrameAre (
  848. @"
  849. ╭────╮
  850. │Tab0│
  851. │ ╰─────────────►
  852. │hi │
  853. └──────────────────┘",
  854. _output
  855. );
  856. tv.SelectedTab = tab2;
  857. tv.Draw ();
  858. TestHelpers.AssertDriverContentsWithFrameAre (
  859. @"
  860. ╭──────────────╮
  861. │Les Misérables│
  862. ◄ ╰───╮
  863. │hi2 │
  864. └──────────────────┘",
  865. _output
  866. );
  867. }
  868. [Fact]
  869. [AutoInitShutdown]
  870. public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 ()
  871. {
  872. TabView tv = GetTabView (out _, out _, false);
  873. tv.Width = 3;
  874. tv.Height = 5;
  875. tv.Style = new TabStyle { TabsOnBottom = true };
  876. tv.ApplyStyleChanges ();
  877. tv.LayoutSubviews ();
  878. tv.Draw ();
  879. TestHelpers.AssertDriverContentsWithFrameAre (
  880. @"
  881. ┌─┐
  882. │h│
  883. │╭►
  884. ││
  885. ╰╯ ",
  886. _output
  887. );
  888. }
  889. [Fact]
  890. [AutoInitShutdown]
  891. public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 ()
  892. {
  893. TabView tv = GetTabView (out _, out _, false);
  894. tv.Width = 4;
  895. tv.Height = 5;
  896. tv.Style = new TabStyle { TabsOnBottom = true };
  897. tv.ApplyStyleChanges ();
  898. tv.LayoutSubviews ();
  899. tv.Draw ();
  900. TestHelpers.AssertDriverContentsWithFrameAre (
  901. @"
  902. ┌──┐
  903. │hi│
  904. │ ╭►
  905. │T│
  906. ╰─╯ ",
  907. _output
  908. );
  909. }
  910. [Fact]
  911. [AutoInitShutdown]
  912. public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames ()
  913. {
  914. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  915. tv.Width = 10;
  916. tv.Height = 5;
  917. tv.Style = new TabStyle { TabsOnBottom = true };
  918. tv.ApplyStyleChanges ();
  919. // Ensures that the tab bar subview gets the bounds of the parent TabView
  920. tv.LayoutSubviews ();
  921. // Test two tab names that fit
  922. tab1.DisplayText = "12";
  923. tab2.DisplayText = "13";
  924. tv.Draw ();
  925. TestHelpers.AssertDriverContentsWithFrameAre (
  926. @"
  927. ┌────────┐
  928. │hi │
  929. │ ╭──┬──╯
  930. │12│13│
  931. ╰──┴──╯ ",
  932. _output
  933. );
  934. // Test first tab name too long
  935. tab1.DisplayText = "12345678910";
  936. tab2.DisplayText = "13";
  937. tv.Draw ();
  938. TestHelpers.AssertDriverContentsWithFrameAre (
  939. @"
  940. ┌────────┐
  941. │hi │
  942. │ ╭►
  943. │1234567│
  944. ╰───────╯ ",
  945. _output
  946. );
  947. //switch to tab2
  948. tv.SelectedTab = tab2;
  949. tv.Draw ();
  950. TestHelpers.AssertDriverContentsWithFrameAre (
  951. @"
  952. ┌────────┐
  953. │hi2 │
  954. ◄ ╭─────╯
  955. │13│
  956. ╰──╯ ",
  957. _output
  958. );
  959. // now make both tabs too long
  960. tab1.DisplayText = "12345678910";
  961. tab2.DisplayText = "abcdefghijklmnopq";
  962. tv.Draw ();
  963. TestHelpers.AssertDriverContentsWithFrameAre (
  964. @"
  965. ┌────────┐
  966. │hi2 │
  967. ◄ ╭╯
  968. │abcdefg│
  969. ╰───────╯ ",
  970. _output
  971. );
  972. }
  973. [Fact]
  974. [AutoInitShutdown]
  975. public void ShowTopLine_True_TabsOnBottom_True_With_Unicode ()
  976. {
  977. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  978. tv.Width = 20;
  979. tv.Height = 5;
  980. tv.Style = new TabStyle { TabsOnBottom = true };
  981. tv.ApplyStyleChanges ();
  982. tv.LayoutSubviews ();
  983. tab1.DisplayText = "Tab0";
  984. tab2.DisplayText = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  985. tv.Draw ();
  986. TestHelpers.AssertDriverContentsWithFrameAre (
  987. @"
  988. ┌──────────────────┐
  989. │hi │
  990. │ ╭─────────────►
  991. │Tab0│
  992. ╰────╯ ",
  993. _output
  994. );
  995. tv.SelectedTab = tab2;
  996. tv.Draw ();
  997. TestHelpers.AssertDriverContentsWithFrameAre (
  998. @"
  999. ┌──────────────────┐
  1000. │hi2 │
  1001. ◄ ╭───╯
  1002. │Les Misérables│
  1003. ╰──────────────╯ ",
  1004. _output
  1005. );
  1006. }
  1007. [Fact]
  1008. public void SwitchTabBy_NormalUsage ()
  1009. {
  1010. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  1011. Tab tab3;
  1012. Tab tab4;
  1013. Tab tab5;
  1014. tv.AddTab (tab3 = new Tab (), false);
  1015. tv.AddTab (tab4 = new Tab (), false);
  1016. tv.AddTab (tab5 = new Tab (), false);
  1017. tv.SelectedTab = tab1;
  1018. var called = 0;
  1019. tv.SelectedTabChanged += (s, e) => { called++; };
  1020. tv.SwitchTabBy (1);
  1021. Assert.Equal (1, called);
  1022. Assert.Equal (tab2, tv.SelectedTab);
  1023. //reset called counter
  1024. called = 0;
  1025. // go right 2
  1026. tv.SwitchTabBy (2);
  1027. // even though we go right 2 indexes the event should only be called once
  1028. Assert.Equal (1, called);
  1029. Assert.Equal (tab4, tv.SelectedTab);
  1030. // Shutdown must be called to safely clean up Application if Init has been called
  1031. Application.Shutdown ();
  1032. }
  1033. [Fact]
  1034. public void SwitchTabBy_OutOfTabsRange ()
  1035. {
  1036. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  1037. tv.SelectedTab = tab1;
  1038. tv.SwitchTabBy (500);
  1039. Assert.Equal (tab2, tv.SelectedTab);
  1040. tv.SwitchTabBy (-500);
  1041. Assert.Equal (tab1, tv.SelectedTab);
  1042. // Shutdown must be called to safely clean up Application if Init has been called
  1043. Application.Shutdown ();
  1044. }
  1045. private TabView GetTabView () { return GetTabView (out _, out _); }
  1046. private TabView GetTabView (out Tab tab1, out Tab tab2, bool initFakeDriver = true)
  1047. {
  1048. if (initFakeDriver)
  1049. {
  1050. InitFakeDriver ();
  1051. }
  1052. var tv = new TabView ();
  1053. tv.BeginInit ();
  1054. tv.EndInit ();
  1055. tv.ColorScheme = new ColorScheme ();
  1056. tv.AddTab (
  1057. tab1 = new Tab { DisplayText = "Tab1", View = new TextField { Width = 2, Text = "hi" } },
  1058. false
  1059. );
  1060. tv.AddTab (tab2 = new Tab { DisplayText = "Tab2", View = new Label { Text = "hi2" } }, false);
  1061. return tv;
  1062. }
  1063. private void InitFakeDriver ()
  1064. {
  1065. var driver = new FakeDriver ();
  1066. Application.Init (driver);
  1067. driver.Init ();
  1068. }
  1069. }