TabViewTests.cs 39 KB

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