TabViewTests.cs 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  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 { X = i, Y = 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 { X = 3, Y = 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 { X = 6, Y = 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 { X = 3, Y = 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 { X = 12, Y = 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 { X = 6, Y = 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 { X = 0, Y = 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 { X = 7, Y = 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 { X = 1, Y = 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. AutoSize = false,
  305. Height = 1,
  306. Width = 7,
  307. Text = "Ok"
  308. };
  309. Toplevel top = new ();
  310. top.Add (tv, btn);
  311. Application.Begin (top);
  312. // Is the selected tab view hosting focused
  313. Assert.Equal (tab1, tv.SelectedTab);
  314. Assert.Equal (tv, top.Focused);
  315. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  316. Assert.Equal (tv.SelectedTab.View, top.Focused.MostFocused);
  317. // Press the cursor up key to focus the selected tab
  318. var args = new Key (Key.CursorUp);
  319. Application.OnKeyDown (args);
  320. Application.Refresh ();
  321. // Is the selected tab focused
  322. Assert.Equal (tab1, tv.SelectedTab);
  323. Assert.Equal (tv, top.Focused);
  324. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  325. Tab oldChanged = null;
  326. Tab newChanged = null;
  327. tv.SelectedTabChanged += (s, e) =>
  328. {
  329. oldChanged = e.OldTab;
  330. newChanged = e.NewTab;
  331. };
  332. // Press the cursor right key to select the next tab
  333. args = new Key (Key.CursorRight);
  334. Application.OnKeyDown (args);
  335. Application.Refresh ();
  336. Assert.Equal (tab1, oldChanged);
  337. Assert.Equal (tab2, newChanged);
  338. Assert.Equal (tab2, tv.SelectedTab);
  339. Assert.Equal (tv, top.Focused);
  340. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  341. // Press the cursor down key to focus the selected tab view hosting
  342. args = new Key (Key.CursorDown);
  343. Application.OnKeyDown (args);
  344. Application.Refresh ();
  345. Assert.Equal (tab2, tv.SelectedTab);
  346. Assert.Equal (tv, top.Focused);
  347. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  348. // The tab view hosting is a label which can't be focused
  349. // and the View container is the focused one
  350. Assert.Equal (tv.Subviews [1], top.Focused.MostFocused);
  351. // Press the cursor down key again will focus next view in the toplevel
  352. Application.OnKeyDown (args);
  353. Application.Refresh ();
  354. Assert.Equal (tab2, tv.SelectedTab);
  355. Assert.Equal (btn, top.Focused);
  356. Assert.Null (tv.MostFocused);
  357. Assert.Null (top.Focused.MostFocused);
  358. // Press the cursor up key to focus the selected tab view hosting again
  359. args = new Key (Key.CursorUp);
  360. Application.OnKeyDown (args);
  361. Application.Refresh ();
  362. Assert.Equal (tab2, tv.SelectedTab);
  363. Assert.Equal (tv, top.Focused);
  364. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  365. // Press the cursor up key to focus the selected tab
  366. args = new Key (Key.CursorUp);
  367. Application.OnKeyDown (args);
  368. Application.Refresh ();
  369. // Is the selected tab focused
  370. Assert.Equal (tab2, tv.SelectedTab);
  371. Assert.Equal (tv, top.Focused);
  372. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  373. // Press the cursor left key to select the previous tab
  374. args = new Key (Key.CursorLeft);
  375. Application.OnKeyDown (args);
  376. Application.Refresh ();
  377. Assert.Equal (tab2, oldChanged);
  378. Assert.Equal (tab1, newChanged);
  379. Assert.Equal (tab1, tv.SelectedTab);
  380. Assert.Equal (tv, top.Focused);
  381. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  382. // Press the end key to select the last tab
  383. args = new Key (Key.End);
  384. Application.OnKeyDown (args);
  385. Application.Refresh ();
  386. Assert.Equal (tab1, oldChanged);
  387. Assert.Equal (tab2, newChanged);
  388. Assert.Equal (tab2, tv.SelectedTab);
  389. Assert.Equal (tv, top.Focused);
  390. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  391. // Press the home key to select the first tab
  392. args = new Key (Key.Home);
  393. Application.OnKeyDown (args);
  394. Application.Refresh ();
  395. Assert.Equal (tab2, oldChanged);
  396. Assert.Equal (tab1, newChanged);
  397. Assert.Equal (tab1, tv.SelectedTab);
  398. Assert.Equal (tv, top.Focused);
  399. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  400. // Press the page down key to select the next set of tabs
  401. args = new Key (Key.PageDown);
  402. Application.OnKeyDown (args);
  403. Application.Refresh ();
  404. Assert.Equal (tab1, oldChanged);
  405. Assert.Equal (tab2, newChanged);
  406. Assert.Equal (tab2, tv.SelectedTab);
  407. Assert.Equal (tv, top.Focused);
  408. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  409. // Press the page up key to select the previous set of tabs
  410. args = new Key (Key.PageUp);
  411. Application.OnKeyDown (args);
  412. Application.Refresh ();
  413. Assert.Equal (tab2, oldChanged);
  414. Assert.Equal (tab1, newChanged);
  415. Assert.Equal (tab1, tv.SelectedTab);
  416. Assert.Equal (tv, top.Focused);
  417. Assert.Equal (tv.MostFocused, top.Focused.MostFocused);
  418. }
  419. [Fact]
  420. public void RemoveAllTabs_ClearsSelection ()
  421. {
  422. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  423. tv.SelectedTab = tab1;
  424. tv.RemoveTab (tab1);
  425. tv.RemoveTab (tab2);
  426. Assert.Null (tv.SelectedTab);
  427. // Shutdown must be called to safely clean up Application if Init has been called
  428. Application.Shutdown ();
  429. }
  430. [Fact]
  431. public void RemoveTab_ChangesSelection ()
  432. {
  433. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  434. tv.SelectedTab = tab1;
  435. tv.RemoveTab (tab1);
  436. Assert.Equal (tab2, tv.SelectedTab);
  437. // Shutdown must be called to safely clean up Application if Init has been called
  438. Application.Shutdown ();
  439. }
  440. [Fact]
  441. public void RemoveTab_MultipleCalls_NotAnError ()
  442. {
  443. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  444. tv.SelectedTab = tab1;
  445. // Repeated calls to remove a tab that is not part of
  446. // the collection should be ignored
  447. tv.RemoveTab (tab1);
  448. tv.RemoveTab (tab1);
  449. tv.RemoveTab (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 SelectedTabChanged_Called ()
  457. {
  458. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  459. tv.SelectedTab = tab1;
  460. Tab oldTab = null;
  461. Tab newTab = null;
  462. var called = 0;
  463. tv.SelectedTabChanged += (s, e) =>
  464. {
  465. oldTab = e.OldTab;
  466. newTab = e.NewTab;
  467. called++;
  468. };
  469. tv.SelectedTab = tab2;
  470. Assert.Equal (1, called);
  471. Assert.Equal (tab1, oldTab);
  472. Assert.Equal (tab2, newTab);
  473. // Shutdown must be called to safely clean up Application if Init has been called
  474. Application.Shutdown ();
  475. }
  476. [Fact]
  477. [AutoInitShutdown]
  478. public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 ()
  479. {
  480. TabView tv = GetTabView (out _, out _, false);
  481. tv.Width = 3;
  482. tv.Height = 5;
  483. tv.Style = new TabStyle { ShowTopLine = false };
  484. tv.ApplyStyleChanges ();
  485. tv.LayoutSubviews ();
  486. tv.Draw ();
  487. TestHelpers.AssertDriverContentsWithFrameAre (
  488. @"
  489. ││
  490. │╰►
  491. │h│
  492. │ │
  493. └─┘",
  494. _output
  495. );
  496. }
  497. [Fact]
  498. [AutoInitShutdown]
  499. public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 ()
  500. {
  501. TabView tv = GetTabView (out _, out _, false);
  502. tv.Width = 4;
  503. tv.Height = 5;
  504. tv.Style = new TabStyle { ShowTopLine = false };
  505. tv.ApplyStyleChanges ();
  506. tv.LayoutSubviews ();
  507. tv.Draw ();
  508. TestHelpers.AssertDriverContentsWithFrameAre (
  509. @"
  510. │T│
  511. │ ╰►
  512. │hi│
  513. │ │
  514. └──┘",
  515. _output
  516. );
  517. }
  518. [Fact]
  519. [AutoInitShutdown]
  520. public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ()
  521. {
  522. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  523. tv.Width = 10;
  524. tv.Height = 5;
  525. tv.Style = new TabStyle { ShowTopLine = false };
  526. tv.ApplyStyleChanges ();
  527. // Ensures that the tab bar subview gets the bounds of the parent TabView
  528. tv.LayoutSubviews ();
  529. // Test two tab names that fit
  530. tab1.DisplayText = "12";
  531. tab2.DisplayText = "13";
  532. tv.Draw ();
  533. TestHelpers.AssertDriverContentsWithFrameAre (
  534. @"
  535. │12│13│
  536. │ ╰──┴──╮
  537. │hi │
  538. │ │
  539. └────────┘",
  540. _output
  541. );
  542. tv.SelectedTab = tab2;
  543. tv.Draw ();
  544. TestHelpers.AssertDriverContentsWithFrameAre (
  545. @"
  546. │12│13│
  547. ├──╯ ╰──╮
  548. │hi2 │
  549. │ │
  550. └────────┘",
  551. _output
  552. );
  553. tv.SelectedTab = tab1;
  554. // Test first tab name too long
  555. tab1.DisplayText = "12345678910";
  556. tab2.DisplayText = "13";
  557. tv.Draw ();
  558. TestHelpers.AssertDriverContentsWithFrameAre (
  559. @"
  560. │1234567│
  561. │ ╰►
  562. │hi │
  563. │ │
  564. └────────┘",
  565. _output
  566. );
  567. //switch to tab2
  568. tv.SelectedTab = tab2;
  569. tv.Draw ();
  570. TestHelpers.AssertDriverContentsWithFrameAre (
  571. @"
  572. │13│
  573. ◄ ╰─────╮
  574. │hi2 │
  575. │ │
  576. └────────┘",
  577. _output
  578. );
  579. // now make both tabs too long
  580. tab1.DisplayText = "12345678910";
  581. tab2.DisplayText = "abcdefghijklmnopq";
  582. tv.Draw ();
  583. TestHelpers.AssertDriverContentsWithFrameAre (
  584. @"
  585. │abcdefg│
  586. ◄ ╰╮
  587. │hi2 │
  588. │ │
  589. └────────┘",
  590. _output
  591. );
  592. }
  593. [Fact]
  594. [AutoInitShutdown]
  595. public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 ()
  596. {
  597. TabView tv = GetTabView (out _, out _, false);
  598. tv.Width = 3;
  599. tv.Height = 5;
  600. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  601. tv.ApplyStyleChanges ();
  602. tv.LayoutSubviews ();
  603. tv.Draw ();
  604. TestHelpers.AssertDriverContentsWithFrameAre (
  605. @"
  606. ┌─┐
  607. │h│
  608. │ │
  609. │╭►
  610. ││ ",
  611. _output
  612. );
  613. }
  614. [Fact]
  615. [AutoInitShutdown]
  616. public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 ()
  617. {
  618. TabView tv = GetTabView (out _, out _, false);
  619. tv.Width = 4;
  620. tv.Height = 5;
  621. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  622. tv.ApplyStyleChanges ();
  623. tv.LayoutSubviews ();
  624. tv.Draw ();
  625. TestHelpers.AssertDriverContentsWithFrameAre (
  626. @"
  627. ┌──┐
  628. │hi│
  629. │ │
  630. │ ╭►
  631. │T│ ",
  632. _output
  633. );
  634. }
  635. [Fact]
  636. [AutoInitShutdown]
  637. public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames ()
  638. {
  639. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  640. tv.Width = 10;
  641. tv.Height = 5;
  642. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  643. tv.ApplyStyleChanges ();
  644. // Ensures that the tab bar subview gets the bounds of the parent TabView
  645. tv.LayoutSubviews ();
  646. // Test two tab names that fit
  647. tab1.DisplayText = "12";
  648. tab2.DisplayText = "13";
  649. tv.Draw ();
  650. TestHelpers.AssertDriverContentsWithFrameAre (
  651. @"
  652. ┌────────┐
  653. │hi │
  654. │ │
  655. │ ╭──┬──╯
  656. │12│13│ ",
  657. _output
  658. );
  659. tv.SelectedTab = tab2;
  660. tv.Draw ();
  661. TestHelpers.AssertDriverContentsWithFrameAre (
  662. @"
  663. ┌────────┐
  664. │hi2 │
  665. │ │
  666. ├──╮ ╭──╯
  667. │12│13│ ",
  668. _output
  669. );
  670. tv.SelectedTab = tab1;
  671. // Test first tab name too long
  672. tab1.DisplayText = "12345678910";
  673. tab2.DisplayText = "13";
  674. tv.Draw ();
  675. TestHelpers.AssertDriverContentsWithFrameAre (
  676. @"
  677. ┌────────┐
  678. │hi │
  679. │ │
  680. │ ╭►
  681. │1234567│ ",
  682. _output
  683. );
  684. //switch to tab2
  685. tv.SelectedTab = tab2;
  686. tv.Draw ();
  687. TestHelpers.AssertDriverContentsWithFrameAre (
  688. @"
  689. ┌────────┐
  690. │hi2 │
  691. │ │
  692. ◄ ╭─────╯
  693. │13│ ",
  694. _output
  695. );
  696. // now make both tabs too long
  697. tab1.DisplayText = "12345678910";
  698. tab2.DisplayText = "abcdefghijklmnopq";
  699. tv.Draw ();
  700. TestHelpers.AssertDriverContentsWithFrameAre (
  701. @"
  702. ┌────────┐
  703. │hi2 │
  704. │ │
  705. ◄ ╭╯
  706. │abcdefg│ ",
  707. _output
  708. );
  709. }
  710. [Fact]
  711. [AutoInitShutdown]
  712. public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 ()
  713. {
  714. TabView tv = GetTabView (out _, out _, false);
  715. tv.Width = 3;
  716. tv.Height = 5;
  717. tv.LayoutSubviews ();
  718. tv.Draw ();
  719. TestHelpers.AssertDriverContentsWithFrameAre (
  720. @"
  721. ╭╮
  722. ││
  723. │╰►
  724. │h│
  725. └─┘",
  726. _output
  727. );
  728. }
  729. [Fact]
  730. [AutoInitShutdown]
  731. public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 ()
  732. {
  733. TabView tv = GetTabView (out _, out _, false);
  734. tv.Width = 4;
  735. tv.Height = 5;
  736. tv.LayoutSubviews ();
  737. tv.Draw ();
  738. TestHelpers.AssertDriverContentsWithFrameAre (
  739. @"
  740. ╭─╮
  741. │T│
  742. │ ╰►
  743. │hi│
  744. └──┘",
  745. _output
  746. );
  747. }
  748. [Fact]
  749. [AutoInitShutdown]
  750. public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames ()
  751. {
  752. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  753. tv.Width = 10;
  754. tv.Height = 5;
  755. // Ensures that the tab bar subview gets the bounds of the parent TabView
  756. tv.LayoutSubviews ();
  757. // Test two tab names that fit
  758. tab1.DisplayText = "12";
  759. tab2.DisplayText = "13";
  760. tv.Draw ();
  761. TestHelpers.AssertDriverContentsWithFrameAre (
  762. @"
  763. ╭──┬──╮
  764. │12│13│
  765. │ ╰──┴──╮
  766. │hi │
  767. └────────┘",
  768. _output
  769. );
  770. tv.SelectedTab = tab2;
  771. tv.Draw ();
  772. TestHelpers.AssertDriverContentsWithFrameAre (
  773. @"
  774. ╭──┬──╮
  775. │12│13│
  776. ├──╯ ╰──╮
  777. │hi2 │
  778. └────────┘",
  779. _output
  780. );
  781. tv.SelectedTab = tab1;
  782. // Test first tab name too long
  783. tab1.DisplayText = "12345678910";
  784. tab2.DisplayText = "13";
  785. tv.Draw ();
  786. TestHelpers.AssertDriverContentsWithFrameAre (
  787. @"
  788. ╭───────╮
  789. │1234567│
  790. │ ╰►
  791. │hi │
  792. └────────┘",
  793. _output
  794. );
  795. //switch to tab2
  796. tv.SelectedTab = tab2;
  797. tv.Draw ();
  798. TestHelpers.AssertDriverContentsWithFrameAre (
  799. @"
  800. ╭──╮
  801. │13│
  802. ◄ ╰─────╮
  803. │hi2 │
  804. └────────┘",
  805. _output
  806. );
  807. // now make both tabs too long
  808. tab1.DisplayText = "12345678910";
  809. tab2.DisplayText = "abcdefghijklmnopq";
  810. tv.Draw ();
  811. TestHelpers.AssertDriverContentsWithFrameAre (
  812. @"
  813. ╭───────╮
  814. │abcdefg│
  815. ◄ ╰╮
  816. │hi2 │
  817. └────────┘",
  818. _output
  819. );
  820. }
  821. [Fact]
  822. [AutoInitShutdown]
  823. public void ShowTopLine_True_TabsOnBottom_False_With_Unicode ()
  824. {
  825. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  826. tv.Width = 20;
  827. tv.Height = 5;
  828. tv.LayoutSubviews ();
  829. tab1.DisplayText = "Tab0";
  830. tab2.DisplayText = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  831. tv.Draw ();
  832. TestHelpers.AssertDriverContentsWithFrameAre (
  833. @"
  834. ╭────╮
  835. │Tab0│
  836. │ ╰─────────────►
  837. │hi │
  838. └──────────────────┘",
  839. _output
  840. );
  841. tv.SelectedTab = tab2;
  842. tv.Draw ();
  843. TestHelpers.AssertDriverContentsWithFrameAre (
  844. @"
  845. ╭──────────────╮
  846. │Les Misérables│
  847. ◄ ╰───╮
  848. │hi2 │
  849. └──────────────────┘",
  850. _output
  851. );
  852. }
  853. [Fact]
  854. [AutoInitShutdown]
  855. public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 ()
  856. {
  857. TabView tv = GetTabView (out _, out _, false);
  858. tv.Width = 3;
  859. tv.Height = 5;
  860. tv.Style = new TabStyle { TabsOnBottom = true };
  861. tv.ApplyStyleChanges ();
  862. tv.LayoutSubviews ();
  863. tv.Draw ();
  864. TestHelpers.AssertDriverContentsWithFrameAre (
  865. @"
  866. ┌─┐
  867. │h│
  868. │╭►
  869. ││
  870. ╰╯ ",
  871. _output
  872. );
  873. }
  874. [Fact]
  875. [AutoInitShutdown]
  876. public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 ()
  877. {
  878. TabView tv = GetTabView (out _, out _, false);
  879. tv.Width = 4;
  880. tv.Height = 5;
  881. tv.Style = new TabStyle { TabsOnBottom = true };
  882. tv.ApplyStyleChanges ();
  883. tv.LayoutSubviews ();
  884. tv.Draw ();
  885. TestHelpers.AssertDriverContentsWithFrameAre (
  886. @"
  887. ┌──┐
  888. │hi│
  889. │ ╭►
  890. │T│
  891. ╰─╯ ",
  892. _output
  893. );
  894. }
  895. [Fact]
  896. [AutoInitShutdown]
  897. public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames ()
  898. {
  899. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  900. tv.Width = 10;
  901. tv.Height = 5;
  902. tv.Style = new TabStyle { TabsOnBottom = true };
  903. tv.ApplyStyleChanges ();
  904. // Ensures that the tab bar subview gets the bounds of the parent TabView
  905. tv.LayoutSubviews ();
  906. // Test two tab names that fit
  907. tab1.DisplayText = "12";
  908. tab2.DisplayText = "13";
  909. tv.Draw ();
  910. TestHelpers.AssertDriverContentsWithFrameAre (
  911. @"
  912. ┌────────┐
  913. │hi │
  914. │ ╭──┬──╯
  915. │12│13│
  916. ╰──┴──╯ ",
  917. _output
  918. );
  919. // Test first tab name too long
  920. tab1.DisplayText = "12345678910";
  921. tab2.DisplayText = "13";
  922. tv.Draw ();
  923. TestHelpers.AssertDriverContentsWithFrameAre (
  924. @"
  925. ┌────────┐
  926. │hi │
  927. │ ╭►
  928. │1234567│
  929. ╰───────╯ ",
  930. _output
  931. );
  932. //switch to tab2
  933. tv.SelectedTab = tab2;
  934. tv.Draw ();
  935. TestHelpers.AssertDriverContentsWithFrameAre (
  936. @"
  937. ┌────────┐
  938. │hi2 │
  939. ◄ ╭─────╯
  940. │13│
  941. ╰──╯ ",
  942. _output
  943. );
  944. // now make both tabs too long
  945. tab1.DisplayText = "12345678910";
  946. tab2.DisplayText = "abcdefghijklmnopq";
  947. tv.Draw ();
  948. TestHelpers.AssertDriverContentsWithFrameAre (
  949. @"
  950. ┌────────┐
  951. │hi2 │
  952. ◄ ╭╯
  953. │abcdefg│
  954. ╰───────╯ ",
  955. _output
  956. );
  957. }
  958. [Fact]
  959. [AutoInitShutdown]
  960. public void ShowTopLine_True_TabsOnBottom_True_With_Unicode ()
  961. {
  962. TabView tv = GetTabView (out Tab tab1, out Tab tab2, false);
  963. tv.Width = 20;
  964. tv.Height = 5;
  965. tv.Style = new TabStyle { TabsOnBottom = true };
  966. tv.ApplyStyleChanges ();
  967. tv.LayoutSubviews ();
  968. tab1.DisplayText = "Tab0";
  969. tab2.DisplayText = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  970. tv.Draw ();
  971. TestHelpers.AssertDriverContentsWithFrameAre (
  972. @"
  973. ┌──────────────────┐
  974. │hi │
  975. │ ╭─────────────►
  976. │Tab0│
  977. ╰────╯ ",
  978. _output
  979. );
  980. tv.SelectedTab = tab2;
  981. tv.Draw ();
  982. TestHelpers.AssertDriverContentsWithFrameAre (
  983. @"
  984. ┌──────────────────┐
  985. │hi2 │
  986. ◄ ╭───╯
  987. │Les Misérables│
  988. ╰──────────────╯ ",
  989. _output
  990. );
  991. }
  992. [Fact]
  993. public void SwitchTabBy_NormalUsage ()
  994. {
  995. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  996. Tab tab3;
  997. Tab tab4;
  998. Tab tab5;
  999. tv.AddTab (tab3 = new Tab (), false);
  1000. tv.AddTab (tab4 = new Tab (), false);
  1001. tv.AddTab (tab5 = new Tab (), false);
  1002. tv.SelectedTab = tab1;
  1003. var called = 0;
  1004. tv.SelectedTabChanged += (s, e) => { called++; };
  1005. tv.SwitchTabBy (1);
  1006. Assert.Equal (1, called);
  1007. Assert.Equal (tab2, tv.SelectedTab);
  1008. //reset called counter
  1009. called = 0;
  1010. // go right 2
  1011. tv.SwitchTabBy (2);
  1012. // even though we go right 2 indexes the event should only be called once
  1013. Assert.Equal (1, called);
  1014. Assert.Equal (tab4, tv.SelectedTab);
  1015. // Shutdown must be called to safely clean up Application if Init has been called
  1016. Application.Shutdown ();
  1017. }
  1018. [Fact]
  1019. public void SwitchTabBy_OutOfTabsRange ()
  1020. {
  1021. TabView tv = GetTabView (out Tab tab1, out Tab tab2);
  1022. tv.SelectedTab = tab1;
  1023. tv.SwitchTabBy (500);
  1024. Assert.Equal (tab2, tv.SelectedTab);
  1025. tv.SwitchTabBy (-500);
  1026. Assert.Equal (tab1, tv.SelectedTab);
  1027. // Shutdown must be called to safely clean up Application if Init has been called
  1028. Application.Shutdown ();
  1029. }
  1030. [Fact]
  1031. public void RemoveTab_ThatHasFocus ()
  1032. {
  1033. TabView tv = GetTabView (out Tab _, out Tab tab2);
  1034. tv.SelectedTab = tab2;
  1035. tab2.HasFocus = true;
  1036. Assert.Equal (2, tv.Tabs.Count);
  1037. foreach (var t in tv.Tabs.ToArray ())
  1038. {
  1039. tv.RemoveTab (t);
  1040. }
  1041. Assert.Empty (tv.Tabs);
  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. }