TabViewTests.cs 38 KB

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