TabViewTests.cs 39 KB

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