TabViewTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. using System;
  2. using System.Globalization;
  3. using Xunit;
  4. using Xunit.Abstractions;
  5. namespace Terminal.Gui.ViewsTests;
  6. public class TabViewTests {
  7. readonly ITestOutputHelper output;
  8. public TabViewTests (ITestOutputHelper output) => this.output = output;
  9. TabView GetTabView () => GetTabView (out _, out _);
  10. TabView GetTabView (out Tab tab1, out Tab tab2, bool initFakeDriver = true)
  11. {
  12. if (initFakeDriver) {
  13. InitFakeDriver ();
  14. }
  15. var tv = new TabView ();
  16. tv.BeginInit ();
  17. tv.EndInit ();
  18. tv.ColorScheme = new ColorScheme ();
  19. tv.AddTab (tab1 = new Tab ("Tab1", new TextField ("hi") { Width = 2 }), false);
  20. tv.AddTab (tab2 = new Tab ("Tab2", new Label ("hi2")), false);
  21. return tv;
  22. }
  23. [Fact]
  24. public void AddTwoTabs_SecondIsSelected ()
  25. {
  26. InitFakeDriver ();
  27. var tv = new TabView ();
  28. Tab tab1;
  29. Tab tab2;
  30. tv.AddTab (tab1 = new Tab ("Tab1", new TextField ("hi")), false);
  31. tv.AddTab (tab2 = new Tab ("Tab1", new Label ("hi2")), true);
  32. Assert.Equal (2, tv.Tabs.Count);
  33. Assert.Equal (tab2, tv.SelectedTab);
  34. Application.Shutdown ();
  35. }
  36. [Fact]
  37. public void EnsureSelectedTabVisible_NullSelect ()
  38. {
  39. var tv = GetTabView ();
  40. tv.SelectedTab = null;
  41. Assert.Null (tv.SelectedTab);
  42. Assert.Equal (0, tv.TabScrollOffset);
  43. tv.EnsureSelectedTabIsVisible ();
  44. Assert.Null (tv.SelectedTab);
  45. Assert.Equal (0, tv.TabScrollOffset);
  46. Application.Shutdown ();
  47. }
  48. [Fact]
  49. public void EnsureSelectedTabVisible_MustScroll ()
  50. {
  51. var tv = GetTabView (out var tab1, out var tab2);
  52. // Make tab width small to force only one tab visible at once
  53. tv.Width = 4;
  54. tv.SelectedTab = tab1;
  55. Assert.Equal (0, tv.TabScrollOffset);
  56. tv.EnsureSelectedTabIsVisible ();
  57. Assert.Equal (0, tv.TabScrollOffset);
  58. // Asking to show tab2 should automatically move scroll offset accordingly
  59. tv.SelectedTab = tab2;
  60. Assert.Equal (1, tv.TabScrollOffset);
  61. // Shutdown must be called to safely clean up Application if Init has been called
  62. Application.Shutdown ();
  63. }
  64. [Fact]
  65. public void SelectedTabChanged_Called ()
  66. {
  67. var tv = GetTabView (out var tab1, out var tab2);
  68. tv.SelectedTab = tab1;
  69. Tab oldTab = null;
  70. Tab newTab = null;
  71. var called = 0;
  72. tv.SelectedTabChanged += (s, e) => {
  73. oldTab = e.OldTab;
  74. newTab = e.NewTab;
  75. called++;
  76. };
  77. tv.SelectedTab = tab2;
  78. Assert.Equal (1, called);
  79. Assert.Equal (tab1, oldTab);
  80. Assert.Equal (tab2, newTab);
  81. // Shutdown must be called to safely clean up Application if Init has been called
  82. Application.Shutdown ();
  83. }
  84. [Fact]
  85. public void RemoveTab_ChangesSelection ()
  86. {
  87. var tv = GetTabView (out var tab1, out var tab2);
  88. tv.SelectedTab = tab1;
  89. tv.RemoveTab (tab1);
  90. Assert.Equal (tab2, tv.SelectedTab);
  91. // Shutdown must be called to safely clean up Application if Init has been called
  92. Application.Shutdown ();
  93. }
  94. [Fact]
  95. public void RemoveTab_MultipleCalls_NotAnError ()
  96. {
  97. var tv = GetTabView (out var tab1, out var tab2);
  98. tv.SelectedTab = tab1;
  99. // Repeated calls to remove a tab that is not part of
  100. // the collection should be ignored
  101. tv.RemoveTab (tab1);
  102. tv.RemoveTab (tab1);
  103. tv.RemoveTab (tab1);
  104. tv.RemoveTab (tab1);
  105. Assert.Equal (tab2, tv.SelectedTab);
  106. // Shutdown must be called to safely clean up Application if Init has been called
  107. Application.Shutdown ();
  108. }
  109. [Fact]
  110. public void RemoveAllTabs_ClearsSelection ()
  111. {
  112. var tv = GetTabView (out var tab1, out var tab2);
  113. tv.SelectedTab = tab1;
  114. tv.RemoveTab (tab1);
  115. tv.RemoveTab (tab2);
  116. Assert.Null (tv.SelectedTab);
  117. // Shutdown must be called to safely clean up Application if Init has been called
  118. Application.Shutdown ();
  119. }
  120. [Fact]
  121. public void SwitchTabBy_NormalUsage ()
  122. {
  123. var tv = GetTabView (out var tab1, out var tab2);
  124. Tab tab3;
  125. Tab tab4;
  126. Tab tab5;
  127. tv.AddTab (tab3 = new Tab (), false);
  128. tv.AddTab (tab4 = new Tab (), false);
  129. tv.AddTab (tab5 = new Tab (), false);
  130. tv.SelectedTab = tab1;
  131. var called = 0;
  132. tv.SelectedTabChanged += (s, e) => { called++; };
  133. tv.SwitchTabBy (1);
  134. Assert.Equal (1, called);
  135. Assert.Equal (tab2, tv.SelectedTab);
  136. //reset called counter
  137. called = 0;
  138. // go right 2
  139. tv.SwitchTabBy (2);
  140. // even though we go right 2 indexes the event should only be called once
  141. Assert.Equal (1, called);
  142. Assert.Equal (tab4, tv.SelectedTab);
  143. // Shutdown must be called to safely clean up Application if Init has been called
  144. Application.Shutdown ();
  145. }
  146. [Fact]
  147. public void AddTab_SameTabMoreThanOnce ()
  148. {
  149. var tv = GetTabView (out var tab1, out var tab2);
  150. Assert.Equal (2, tv.Tabs.Count);
  151. // Tab is already part of the control so shouldn't result in duplication
  152. tv.AddTab (tab1, false);
  153. tv.AddTab (tab1, false);
  154. tv.AddTab (tab1, false);
  155. tv.AddTab (tab1, false);
  156. Assert.Equal (2, tv.Tabs.Count);
  157. // Shutdown must be called to safely clean up Application if Init has been called
  158. Application.Shutdown ();
  159. }
  160. [Fact]
  161. public void SwitchTabBy_OutOfTabsRange ()
  162. {
  163. var tv = GetTabView (out var tab1, out var tab2);
  164. tv.SelectedTab = tab1;
  165. tv.SwitchTabBy (500);
  166. Assert.Equal (tab2, tv.SelectedTab);
  167. tv.SwitchTabBy (-500);
  168. Assert.Equal (tab1, tv.SelectedTab);
  169. // Shutdown must be called to safely clean up Application if Init has been called
  170. Application.Shutdown ();
  171. }
  172. [Fact]
  173. [AutoInitShutdown]
  174. public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames ()
  175. {
  176. var tv = GetTabView (out var tab1, out var tab2, false);
  177. tv.Width = 10;
  178. tv.Height = 5;
  179. // Ensures that the tab bar subview gets the bounds of the parent TabView
  180. tv.LayoutSubviews ();
  181. // Test two tab names that fit
  182. tab1.Text = "12";
  183. tab2.Text = "13";
  184. tv.Draw ();
  185. TestHelpers.AssertDriverContentsWithFrameAre (@"
  186. ┌──┐
  187. │12│13
  188. │ └─────┐
  189. │hi │
  190. └────────┘", output);
  191. tv.SelectedTab = tab2;
  192. tv.Draw ();
  193. TestHelpers.AssertDriverContentsWithFrameAre (@"
  194. ┌──┐
  195. 12│13│
  196. ┌──┘ └──┐
  197. │hi2 │
  198. └────────┘", output);
  199. tv.SelectedTab = tab1;
  200. // Test first tab name too long
  201. tab1.Text = "12345678910";
  202. tab2.Text = "13";
  203. tv.Draw ();
  204. TestHelpers.AssertDriverContentsWithFrameAre (@"
  205. ┌───────┐
  206. │1234567│
  207. │ └►
  208. │hi │
  209. └────────┘", output);
  210. //switch to tab2
  211. tv.SelectedTab = tab2;
  212. tv.Draw ();
  213. TestHelpers.AssertDriverContentsWithFrameAre (@"
  214. ┌──┐
  215. │13│
  216. ◄ └─────┐
  217. │hi2 │
  218. └────────┘", output);
  219. // now make both tabs too long
  220. tab1.Text = "12345678910";
  221. tab2.Text = "abcdefghijklmnopq";
  222. tv.Draw ();
  223. TestHelpers.AssertDriverContentsWithFrameAre (@"
  224. ┌───────┐
  225. │abcdefg│
  226. ◄ └┐
  227. │hi2 │
  228. └────────┘", output);
  229. }
  230. [Fact]
  231. [AutoInitShutdown]
  232. public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ()
  233. {
  234. var tv = GetTabView (out var tab1, out var tab2, false);
  235. tv.Width = 10;
  236. tv.Height = 5;
  237. tv.Style = new TabStyle { ShowTopLine = false };
  238. tv.ApplyStyleChanges ();
  239. // Ensures that the tab bar subview gets the bounds of the parent TabView
  240. tv.LayoutSubviews ();
  241. // Test two tab names that fit
  242. tab1.Text = "12";
  243. tab2.Text = "13";
  244. tv.Draw ();
  245. TestHelpers.AssertDriverContentsWithFrameAre (@"
  246. │12│13
  247. │ └─────┐
  248. │hi │
  249. │ │
  250. └────────┘", output);
  251. tv.SelectedTab = tab2;
  252. tv.Draw ();
  253. TestHelpers.AssertDriverContentsWithFrameAre (@"
  254. 12│13│
  255. ┌──┘ └──┐
  256. │hi2 │
  257. │ │
  258. └────────┘", output);
  259. tv.SelectedTab = tab1;
  260. // Test first tab name too long
  261. tab1.Text = "12345678910";
  262. tab2.Text = "13";
  263. tv.Draw ();
  264. TestHelpers.AssertDriverContentsWithFrameAre (@"
  265. │1234567│
  266. │ └►
  267. │hi │
  268. │ │
  269. └────────┘", output);
  270. //switch to tab2
  271. tv.SelectedTab = tab2;
  272. tv.Draw ();
  273. TestHelpers.AssertDriverContentsWithFrameAre (@"
  274. │13│
  275. ◄ └─────┐
  276. │hi2 │
  277. │ │
  278. └────────┘", output);
  279. // now make both tabs too long
  280. tab1.Text = "12345678910";
  281. tab2.Text = "abcdefghijklmnopq";
  282. tv.Draw ();
  283. TestHelpers.AssertDriverContentsWithFrameAre (@"
  284. │abcdefg│
  285. ◄ └┐
  286. │hi2 │
  287. │ │
  288. └────────┘", output);
  289. }
  290. [Fact]
  291. [AutoInitShutdown]
  292. public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 ()
  293. {
  294. var tv = GetTabView (out _, out _, false);
  295. tv.Width = 4;
  296. tv.Height = 5;
  297. tv.LayoutSubviews ();
  298. tv.Draw ();
  299. TestHelpers.AssertDriverContentsWithFrameAre (@"
  300. ┌─┐
  301. │T│
  302. │ └►
  303. │hi│
  304. └──┘", output);
  305. }
  306. [Fact]
  307. [AutoInitShutdown]
  308. public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 ()
  309. {
  310. var tv = GetTabView (out _, out _, false);
  311. tv.Width = 4;
  312. tv.Height = 5;
  313. tv.Style = new TabStyle { ShowTopLine = false };
  314. tv.ApplyStyleChanges ();
  315. tv.LayoutSubviews ();
  316. tv.Draw ();
  317. TestHelpers.AssertDriverContentsWithFrameAre (@"
  318. │T│
  319. │ └►
  320. │hi│
  321. │ │
  322. └──┘", output);
  323. }
  324. [Fact]
  325. [AutoInitShutdown]
  326. public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 ()
  327. {
  328. var tv = GetTabView (out _, out _, false);
  329. tv.Width = 3;
  330. tv.Height = 5;
  331. tv.LayoutSubviews ();
  332. tv.Draw ();
  333. TestHelpers.AssertDriverContentsWithFrameAre (@"
  334. ┌┐
  335. ││
  336. │└►
  337. │h│
  338. └─┘", output);
  339. }
  340. [Fact]
  341. [AutoInitShutdown]
  342. public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 ()
  343. {
  344. var tv = GetTabView (out _, out _, false);
  345. tv.Width = 3;
  346. tv.Height = 5;
  347. tv.Style = new TabStyle { ShowTopLine = false };
  348. tv.ApplyStyleChanges ();
  349. tv.LayoutSubviews ();
  350. tv.Draw ();
  351. TestHelpers.AssertDriverContentsWithFrameAre (@"
  352. ││
  353. │└►
  354. │h│
  355. │ │
  356. └─┘", output);
  357. }
  358. [Fact]
  359. [AutoInitShutdown]
  360. public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames ()
  361. {
  362. var tv = GetTabView (out var tab1, out var tab2, false);
  363. tv.Width = 10;
  364. tv.Height = 5;
  365. tv.Style = new TabStyle { TabsOnBottom = true };
  366. tv.ApplyStyleChanges ();
  367. // Ensures that the tab bar subview gets the bounds of the parent TabView
  368. tv.LayoutSubviews ();
  369. // Test two tab names that fit
  370. tab1.Text = "12";
  371. tab2.Text = "13";
  372. tv.Draw ();
  373. TestHelpers.AssertDriverContentsWithFrameAre (@"
  374. ┌────────┐
  375. │hi │
  376. │ ┌─────┘
  377. │12│13
  378. └──┘ ", output);
  379. // Test first tab name too long
  380. tab1.Text = "12345678910";
  381. tab2.Text = "13";
  382. tv.Draw ();
  383. TestHelpers.AssertDriverContentsWithFrameAre (@"
  384. ┌────────┐
  385. │hi │
  386. │ ┌►
  387. │1234567│
  388. └───────┘ ", output);
  389. //switch to tab2
  390. tv.SelectedTab = tab2;
  391. tv.Draw ();
  392. TestHelpers.AssertDriverContentsWithFrameAre (@"
  393. ┌────────┐
  394. │hi2 │
  395. ◄ ┌─────┘
  396. │13│
  397. └──┘ ", output);
  398. // now make both tabs too long
  399. tab1.Text = "12345678910";
  400. tab2.Text = "abcdefghijklmnopq";
  401. tv.Draw ();
  402. TestHelpers.AssertDriverContentsWithFrameAre (@"
  403. ┌────────┐
  404. │hi2 │
  405. ◄ ┌┘
  406. │abcdefg│
  407. └───────┘ ", output);
  408. }
  409. [Fact]
  410. [AutoInitShutdown]
  411. public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames ()
  412. {
  413. var tv = GetTabView (out var tab1, out var tab2, false);
  414. tv.Width = 10;
  415. tv.Height = 5;
  416. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  417. tv.ApplyStyleChanges ();
  418. // Ensures that the tab bar subview gets the bounds of the parent TabView
  419. tv.LayoutSubviews ();
  420. // Test two tab names that fit
  421. tab1.Text = "12";
  422. tab2.Text = "13";
  423. tv.Draw ();
  424. TestHelpers.AssertDriverContentsWithFrameAre (@"
  425. ┌────────┐
  426. │hi │
  427. │ │
  428. │ ┌─────┘
  429. │12│13 ", output);
  430. tv.SelectedTab = tab2;
  431. tv.Draw ();
  432. TestHelpers.AssertDriverContentsWithFrameAre (@"
  433. ┌────────┐
  434. │hi2 │
  435. │ │
  436. └──┐ ┌──┘
  437. 12│13│ ", output);
  438. tv.SelectedTab = tab1;
  439. // Test first tab name too long
  440. tab1.Text = "12345678910";
  441. tab2.Text = "13";
  442. tv.Draw ();
  443. TestHelpers.AssertDriverContentsWithFrameAre (@"
  444. ┌────────┐
  445. │hi │
  446. │ │
  447. │ ┌►
  448. │1234567│ ", output);
  449. //switch to tab2
  450. tv.SelectedTab = tab2;
  451. tv.Draw ();
  452. TestHelpers.AssertDriverContentsWithFrameAre (@"
  453. ┌────────┐
  454. │hi2 │
  455. │ │
  456. ◄ ┌─────┘
  457. │13│ ", output);
  458. // now make both tabs too long
  459. tab1.Text = "12345678910";
  460. tab2.Text = "abcdefghijklmnopq";
  461. tv.Draw ();
  462. TestHelpers.AssertDriverContentsWithFrameAre (@"
  463. ┌────────┐
  464. │hi2 │
  465. │ │
  466. ◄ ┌┘
  467. │abcdefg│ ", output);
  468. }
  469. [Fact]
  470. [AutoInitShutdown]
  471. public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 ()
  472. {
  473. var tv = GetTabView (out _, out _, false);
  474. tv.Width = 4;
  475. tv.Height = 5;
  476. tv.Style = new TabStyle { TabsOnBottom = true };
  477. tv.ApplyStyleChanges ();
  478. tv.LayoutSubviews ();
  479. tv.Draw ();
  480. TestHelpers.AssertDriverContentsWithFrameAre (@"
  481. ┌──┐
  482. │hi│
  483. │ ┌►
  484. │T│
  485. └─┘ ", output);
  486. }
  487. [Fact]
  488. [AutoInitShutdown]
  489. public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 ()
  490. {
  491. var tv = GetTabView (out _, out _, false);
  492. tv.Width = 4;
  493. tv.Height = 5;
  494. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  495. tv.ApplyStyleChanges ();
  496. tv.LayoutSubviews ();
  497. tv.Draw ();
  498. TestHelpers.AssertDriverContentsWithFrameAre (@"
  499. ┌──┐
  500. │hi│
  501. │ │
  502. │ ┌►
  503. │T│ ", output);
  504. }
  505. [Fact]
  506. [AutoInitShutdown]
  507. public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 ()
  508. {
  509. var tv = GetTabView (out _, out _, false);
  510. tv.Width = 3;
  511. tv.Height = 5;
  512. tv.Style = new TabStyle { TabsOnBottom = true };
  513. tv.ApplyStyleChanges ();
  514. tv.LayoutSubviews ();
  515. tv.Draw ();
  516. TestHelpers.AssertDriverContentsWithFrameAre (@"
  517. ┌─┐
  518. │h│
  519. │┌►
  520. ││
  521. └┘ ", output);
  522. }
  523. [Fact]
  524. [AutoInitShutdown]
  525. public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 ()
  526. {
  527. var tv = GetTabView (out _, out _, false);
  528. tv.Width = 3;
  529. tv.Height = 5;
  530. tv.Style = new TabStyle { ShowTopLine = false, TabsOnBottom = true };
  531. tv.ApplyStyleChanges ();
  532. tv.LayoutSubviews ();
  533. tv.Draw ();
  534. TestHelpers.AssertDriverContentsWithFrameAre (@"
  535. ┌─┐
  536. │h│
  537. │ │
  538. │┌►
  539. ││ ", output);
  540. }
  541. [Fact]
  542. [AutoInitShutdown]
  543. public void ShowTopLine_True_TabsOnBottom_False_With_Unicode ()
  544. {
  545. var tv = GetTabView (out var tab1, out var tab2, false);
  546. tv.Width = 20;
  547. tv.Height = 5;
  548. tv.LayoutSubviews ();
  549. tab1.Text = "Tab0";
  550. tab2.Text = "Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  551. tv.Draw ();
  552. TestHelpers.AssertDriverContentsWithFrameAre (@"
  553. ┌────┐
  554. │Tab0│
  555. │ └─────────────►
  556. │hi │
  557. └──────────────────┘", output);
  558. tv.SelectedTab = tab2;
  559. tv.Draw ();
  560. TestHelpers.AssertDriverContentsWithFrameAre (@"
  561. ┌──────────────┐
  562. │Les Misérables│
  563. ◄ └───┐
  564. │hi2 │
  565. └──────────────────┘", output);
  566. }
  567. [Fact]
  568. [AutoInitShutdown]
  569. public void ShowTopLine_True_TabsOnBottom_True_With_Unicode ()
  570. {
  571. var tv = GetTabView (out var tab1, out var tab2, false);
  572. tv.Width = 20;
  573. tv.Height = 5;
  574. tv.Style = new TabStyle { TabsOnBottom = true };
  575. tv.ApplyStyleChanges ();
  576. tv.LayoutSubviews ();
  577. tab1.Text = "Tab0";
  578. tab2.Text = "Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  579. tv.Draw ();
  580. TestHelpers.AssertDriverContentsWithFrameAre (@"
  581. ┌──────────────────┐
  582. │hi │
  583. │ ┌─────────────►
  584. │Tab0│
  585. └────┘ ", output);
  586. tv.SelectedTab = tab2;
  587. tv.Draw ();
  588. TestHelpers.AssertDriverContentsWithFrameAre (@"
  589. ┌──────────────────┐
  590. │hi2 │
  591. ◄ ┌───┘
  592. │Les Misérables│
  593. └──────────────┘ ", output);
  594. }
  595. [Fact]
  596. [AutoInitShutdown]
  597. public void MouseClick_ChangesTab ()
  598. {
  599. var tv = GetTabView (out var tab1, out var tab2, false);
  600. tv.Width = 20;
  601. tv.Height = 5;
  602. tv.LayoutSubviews ();
  603. tv.Draw ();
  604. var tabRow = tv.Subviews [0];
  605. Assert.Equal ("TabRowView", tabRow.GetType ().Name);
  606. TestHelpers.AssertDriverContentsAre (@"
  607. ┌────┐
  608. │Tab1│Tab2
  609. │ └─────────────┐
  610. │hi │
  611. └──────────────────┘
  612. ", output);
  613. Tab clicked = null;
  614. tv.TabClicked += (s, e) => {
  615. clicked = e.Tab;
  616. };
  617. // Waving mouse around does not trigger click
  618. for (var i = 0; i < 100; i++) {
  619. tabRow.MouseEvent (new MouseEvent {
  620. X = i,
  621. Y = 1,
  622. Flags = MouseFlags.ReportMousePosition
  623. });
  624. Assert.Null (clicked);
  625. Assert.Equal (tab1, tv.SelectedTab);
  626. }
  627. tabRow.MouseEvent (new MouseEvent {
  628. X = 3,
  629. Y = 1,
  630. Flags = MouseFlags.Button1Clicked
  631. });
  632. Assert.Equal (tab1, clicked);
  633. Assert.Equal (tab1, tv.SelectedTab);
  634. // Click to tab2
  635. tabRow.MouseEvent (new MouseEvent {
  636. X = 7,
  637. Y = 1,
  638. Flags = MouseFlags.Button1Clicked
  639. });
  640. Assert.Equal (tab2, clicked);
  641. Assert.Equal (tab2, tv.SelectedTab);
  642. // cancel navigation
  643. tv.TabClicked += (s, e) => {
  644. clicked = e.Tab;
  645. e.MouseEvent.Handled = true;
  646. };
  647. tabRow.MouseEvent (new MouseEvent {
  648. X = 3,
  649. Y = 1,
  650. Flags = MouseFlags.Button1Clicked
  651. });
  652. // Tab 1 was clicked but event handler blocked navigation
  653. Assert.Equal (tab1, clicked);
  654. Assert.Equal (tab2, tv.SelectedTab);
  655. tabRow.MouseEvent (new MouseEvent {
  656. X = 10,
  657. Y = 1,
  658. Flags = MouseFlags.Button1Clicked
  659. });
  660. // Clicking beyond last tab should raise event with null Tab
  661. Assert.Null (clicked);
  662. Assert.Equal (tab2, tv.SelectedTab);
  663. }
  664. void InitFakeDriver ()
  665. {
  666. var driver = new FakeDriver ();
  667. Application.Init (driver);
  668. driver.Init ();
  669. }
  670. }