LayoutTests.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. using Xunit.Abstractions;
  2. using static Unix.Terminal.Curses;
  3. // Alias Console to MockConsole so we don't accidentally use Console
  4. namespace Terminal.Gui.ViewTests;
  5. public class LayoutTests
  6. {
  7. private readonly ITestOutputHelper _output;
  8. public LayoutTests (ITestOutputHelper output) { _output = output; }
  9. // Tested in AbsoluteLayoutTests.cs
  10. // public void Pos_Dim_Are_Null_If_Not_Initialized_On_Constructor_IsAdded_False ()
  11. [Theory]
  12. [AutoInitShutdown]
  13. [InlineData (1)]
  14. [InlineData (2)]
  15. [InlineData (3)]
  16. [InlineData (4)]
  17. [InlineData (5)]
  18. [InlineData (6)]
  19. [InlineData (7)]
  20. [InlineData (8)]
  21. [InlineData (9)]
  22. [InlineData (10)]
  23. public void Dim_CenteredSubView_85_Percent_Height (int height)
  24. {
  25. var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
  26. var subview = new Window
  27. {
  28. X = Pos.Center (), Y = Pos.Center (), Width = Dim.Percent (85), Height = Dim.Percent (85)
  29. };
  30. win.Add (subview);
  31. RunState rs = Application.Begin (win);
  32. var firstIteration = false;
  33. ((FakeDriver)Application.Driver).SetBufferSize (20, height);
  34. Application.RunIteration (ref rs, ref firstIteration);
  35. var expected = string.Empty;
  36. switch (height)
  37. {
  38. case 1:
  39. //Assert.Equal (new (0, 0, 17, 0), subview.Frame);
  40. expected = @"
  41. ────────────────────";
  42. break;
  43. case 2:
  44. //Assert.Equal (new (0, 0, 17, 1), subview.Frame);
  45. expected = @"
  46. ┌──────────────────┐
  47. └──────────────────┘
  48. ";
  49. break;
  50. case 3:
  51. //Assert.Equal (new (0, 0, 17, 2), subview.Frame);
  52. expected = @"
  53. ┌──────────────────┐
  54. │ │
  55. └──────────────────┘
  56. ";
  57. break;
  58. case 4:
  59. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  60. expected = @"
  61. ┌──────────────────┐
  62. │ ─────────────── │
  63. │ │
  64. └──────────────────┘";
  65. break;
  66. case 5:
  67. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  68. expected = @"
  69. ┌──────────────────┐
  70. │ ┌─────────────┐ │
  71. │ └─────────────┘ │
  72. │ │
  73. └──────────────────┘";
  74. break;
  75. case 6:
  76. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  77. expected = @"
  78. ┌──────────────────┐
  79. │ ┌─────────────┐ │
  80. │ │ │ │
  81. │ └─────────────┘ │
  82. │ │
  83. └──────────────────┘";
  84. break;
  85. case 7:
  86. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  87. expected = @"
  88. ┌──────────────────┐
  89. │ ┌─────────────┐ │
  90. │ │ │ │
  91. │ │ │ │
  92. │ └─────────────┘ │
  93. │ │
  94. └──────────────────┘";
  95. break;
  96. case 8:
  97. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  98. expected = @"
  99. ┌──────────────────┐
  100. │ ┌─────────────┐ │
  101. │ │ │ │
  102. │ │ │ │
  103. │ │ │ │
  104. │ └─────────────┘ │
  105. │ │
  106. └──────────────────┘";
  107. break;
  108. case 9:
  109. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  110. expected = @"
  111. ┌──────────────────┐
  112. │ │
  113. │ ┌─────────────┐ │
  114. │ │ │ │
  115. │ │ │ │
  116. │ │ │ │
  117. │ └─────────────┘ │
  118. │ │
  119. └──────────────────┘";
  120. break;
  121. case 10:
  122. //Assert.Equal (new (0, 0, 17, 3), subview.Frame);
  123. expected = @"
  124. ┌──────────────────┐
  125. │ │
  126. │ ┌─────────────┐ │
  127. │ │ │ │
  128. │ │ │ │
  129. │ │ │ │
  130. │ │ │ │
  131. │ └─────────────┘ │
  132. │ │
  133. └──────────────────┘";
  134. break;
  135. }
  136. _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  137. Application.End (rs);
  138. }
  139. [Theory]
  140. [AutoInitShutdown]
  141. [InlineData (1)]
  142. [InlineData (2)]
  143. [InlineData (3)]
  144. [InlineData (4)]
  145. [InlineData (5)]
  146. [InlineData (6)]
  147. [InlineData (7)]
  148. [InlineData (8)]
  149. [InlineData (9)]
  150. [InlineData (10)]
  151. public void Dim_CenteredSubView_85_Percent_Width (int width)
  152. {
  153. var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
  154. var subview = new Window
  155. {
  156. X = Pos.Center (), Y = Pos.Center (), Width = Dim.Percent (85), Height = Dim.Percent (85)
  157. };
  158. win.Add (subview);
  159. RunState rs = Application.Begin (win);
  160. var firstIteration = false;
  161. ((FakeDriver)Application.Driver).SetBufferSize (width, 7);
  162. Application.RunIteration (ref rs, ref firstIteration);
  163. var expected = string.Empty;
  164. switch (width)
  165. {
  166. case 1:
  167. Assert.Equal (new Rectangle (0, 0, 0, 4), subview.Frame);
  168. expected = @"
  169. │";
  170. break;
  171. case 2:
  172. Assert.Equal (new Rectangle (0, 0, 0, 4), subview.Frame);
  173. expected = @"
  174. ┌┐
  175. ││
  176. ││
  177. ││
  178. ││
  179. ││
  180. └┘";
  181. break;
  182. case 3:
  183. Assert.Equal (new Rectangle (0, 0, 0, 4), subview.Frame);
  184. expected = @"
  185. ┌─┐
  186. │ │
  187. │ │
  188. │ │
  189. │ │
  190. │ │
  191. └─┘";
  192. break;
  193. case 4:
  194. Assert.Equal (new Rectangle (0, 0, 1, 4), subview.Frame);
  195. expected = @"
  196. ┌──┐
  197. ││ │
  198. ││ │
  199. ││ │
  200. ││ │
  201. │ │
  202. └──┘";
  203. break;
  204. case 5:
  205. Assert.Equal (new Rectangle (0, 0, 2, 4), subview.Frame);
  206. expected = @"
  207. ┌───┐
  208. │┌┐ │
  209. │││ │
  210. │││ │
  211. │└┘ │
  212. │ │
  213. └───┘";
  214. break;
  215. case 6:
  216. Assert.Equal (new Rectangle (0, 0, 3, 4), subview.Frame);
  217. expected = @"
  218. ┌────┐
  219. │┌─┐ │
  220. ││ │ │
  221. ││ │ │
  222. │└─┘ │
  223. │ │
  224. └────┘";
  225. break;
  226. case 7:
  227. Assert.Equal (new Rectangle (0, 0, 4, 4), subview.Frame);
  228. expected = @"
  229. ┌─────┐
  230. │┌──┐ │
  231. ││ │ │
  232. ││ │ │
  233. │└──┘ │
  234. │ │
  235. └─────┘";
  236. break;
  237. case 8:
  238. Assert.Equal (new Rectangle (0, 0, 5, 4), subview.Frame);
  239. expected = @"
  240. ┌──────┐
  241. │┌───┐ │
  242. ││ │ │
  243. ││ │ │
  244. │└───┘ │
  245. │ │
  246. └──────┘";
  247. break;
  248. case 9:
  249. Assert.Equal (new Rectangle (1, 0, 5, 4), subview.Frame);
  250. expected = @"
  251. ┌───────┐
  252. │ ┌───┐ │
  253. │ │ │ │
  254. │ │ │ │
  255. │ └───┘ │
  256. │ │
  257. └───────┘";
  258. break;
  259. case 10:
  260. Assert.Equal (new Rectangle (1, 0, 6, 4), subview.Frame);
  261. expected = @"
  262. ┌────────┐
  263. │ ┌────┐ │
  264. │ │ │ │
  265. │ │ │ │
  266. │ └────┘ │
  267. │ │
  268. └────────┘";
  269. break;
  270. }
  271. _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  272. Application.End (rs);
  273. }
  274. [Fact]
  275. [AutoInitShutdown]
  276. public void DimFill_SizedCorrectly ()
  277. {
  278. var view = new View { Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single };
  279. var top = new Toplevel ();
  280. top.Add (view);
  281. RunState rs = Application.Begin (top);
  282. ((FakeDriver)Application.Driver).SetBufferSize (32, 5);
  283. //view.SetNeedsLayout ();
  284. top.LayoutSubviews ();
  285. //view.SetRelativeLayout (new (0, 0, 32, 5));
  286. Assert.Equal (32, view.Frame.Width);
  287. Assert.Equal (5, view.Frame.Height);
  288. }
  289. [Fact]
  290. [TestRespondersDisposed]
  291. public void Draw_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
  292. {
  293. Application.Init (new FakeDriver ());
  294. Toplevel top = new ();
  295. var view = new View { X = -2, Text = "view" };
  296. top.Add (view);
  297. Application.Iteration += (s, a) =>
  298. {
  299. Assert.Equal (-2, view.X);
  300. Application.RequestStop ();
  301. };
  302. try
  303. {
  304. Application.Run (top);
  305. }
  306. catch (IndexOutOfRangeException ex)
  307. {
  308. // After the fix this exception will not be caught.
  309. Assert.IsType<IndexOutOfRangeException> (ex);
  310. }
  311. top.Dispose ();
  312. // Shutdown must be called to safely clean up Application if Init has been called
  313. Application.Shutdown ();
  314. }
  315. [Fact]
  316. [TestRespondersDisposed]
  317. public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
  318. {
  319. Application.Init (new FakeDriver ());
  320. Toplevel top = new ();
  321. var view = new View { Y = -2, Height = 10, TextDirection = TextDirection.TopBottom_LeftRight, Text = "view" };
  322. top.Add (view);
  323. Application.Iteration += (s, a) =>
  324. {
  325. Assert.Equal (-2, view.Y);
  326. Application.RequestStop ();
  327. };
  328. try
  329. {
  330. Application.Run (top);
  331. }
  332. catch (IndexOutOfRangeException ex)
  333. {
  334. // After the fix this exception will not be caught.
  335. Assert.IsType<IndexOutOfRangeException> (ex);
  336. }
  337. top.Dispose ();
  338. // Shutdown must be called to safely clean up Application if Init has been called
  339. Application.Shutdown ();
  340. }
  341. [Fact]
  342. public void LayoutSubviews_No_SuperView ()
  343. {
  344. var root = new View ();
  345. var first = new View
  346. {
  347. Id = "first",
  348. X = 1,
  349. Y = 2,
  350. Height = 3,
  351. Width = 4
  352. };
  353. root.Add (first);
  354. var second = new View { Id = "second" };
  355. root.Add (second);
  356. second.X = Pos.Right (first) + 1;
  357. root.LayoutSubviews ();
  358. Assert.Equal (6, second.Frame.X);
  359. root.Dispose ();
  360. first.Dispose ();
  361. second.Dispose ();
  362. }
  363. [Fact]
  364. public void LayoutSubviews_RootHas_SuperView ()
  365. {
  366. var top = new View ();
  367. var root = new View ();
  368. top.Add (root);
  369. var first = new View
  370. {
  371. Id = "first",
  372. X = 1,
  373. Y = 2,
  374. Height = 3,
  375. Width = 4
  376. };
  377. root.Add (first);
  378. var second = new View { Id = "second" };
  379. root.Add (second);
  380. second.X = Pos.Right (first) + 1;
  381. root.LayoutSubviews ();
  382. Assert.Equal (6, second.Frame.X);
  383. root.Dispose ();
  384. top.Dispose ();
  385. first.Dispose ();
  386. second.Dispose ();
  387. }
  388. [Fact]
  389. public void LayoutSubviews_ViewThatRefsSubView_Throws ()
  390. {
  391. var root = new View ();
  392. var super = new View ();
  393. root.Add (super);
  394. var sub = new View ();
  395. super.Add (sub);
  396. super.Width = Dim.Width (sub);
  397. Assert.Throws<InvalidOperationException> (() => root.LayoutSubviews ());
  398. root.Dispose ();
  399. super.Dispose ();
  400. }
  401. // Was named AutoSize_Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
  402. // but doesn't actually have anything to do with AutoSize.
  403. [Fact]
  404. public void
  405. Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
  406. {
  407. Application.Init (new FakeDriver ());
  408. Toplevel t = new ();
  409. var w = new Window { X = Pos.Left (t) + 2, Y = Pos.At (2) };
  410. var v = new View { X = Pos.Center (), Y = Pos.Percent (10) };
  411. w.Add (v);
  412. t.Add (w);
  413. t.Ready += (s, e) =>
  414. {
  415. v.Frame = new Rectangle (2, 2, 10, 10);
  416. Assert.Equal (2, v.X = 2);
  417. Assert.Equal (2, v.Y = 2);
  418. };
  419. Application.Iteration += (s, a) => Application.RequestStop ();
  420. Application.Run (t);
  421. t.Dispose ();
  422. Application.Shutdown ();
  423. }
  424. [Fact]
  425. [SetupFakeDriver]
  426. public void PosCombine_DimCombine_View_With_SubViews ()
  427. {
  428. var clicked = false;
  429. Toplevel top = new Toplevel() { Width = 80, Height = 25 };
  430. var win1 = new Window { Id = "win1", Width = 20, Height = 10 };
  431. var view1 = new View { Text = "view1", AutoSize = true }; // BUGBUG: AutoSize or Width must be set
  432. var win2 = new Window { Id = "win2", Y = Pos.Bottom (view1) + 1, Width = 10, Height = 3 };
  433. var view2 = new View { Id = "view2", Width = Dim.Fill (), Height = 1, CanFocus = true };
  434. view2.MouseClick += (sender, e) => clicked = true;
  435. var view3 = new View { Id = "view3", Width = Dim.Fill (1), Height = 1, CanFocus = true };
  436. view2.Add (view3);
  437. win2.Add (view2);
  438. win1.Add (view1, win2);
  439. top.Add (win1);
  440. top.BeginInit();
  441. top.EndInit();
  442. Assert.Equal (new Rectangle (0, 0, 80, 25), top.Frame);
  443. Assert.Equal (new Rectangle (0, 0, 5, 1), view1.Frame);
  444. Assert.Equal (new Rectangle (0, 0, 20, 10), win1.Frame);
  445. Assert.Equal (new Rectangle (0, 2, 10, 3), win2.Frame);
  446. Assert.Equal (new Rectangle (0, 0, 8, 1), view2.Frame);
  447. Assert.Equal (new Rectangle (0, 0, 7, 1), view3.Frame);
  448. var foundView = View.FindDeepestView (top, 9, 4);
  449. Assert.Equal (foundView, view2);
  450. }
  451. [Fact]
  452. public void PosCombine_Refs_SuperView_Throws ()
  453. {
  454. Application.Init (new FakeDriver ());
  455. var top = new Toplevel ();
  456. var w = new Window { X = Pos.Left (top) + 2, Y = Pos.Top (top) + 2 };
  457. var f = new FrameView ();
  458. var v1 = new View { X = Pos.Left (w) + 2, Y = Pos.Top (w) + 2 };
  459. var v2 = new View { X = Pos.Left (v1) + 2, Y = Pos.Top (v1) + 2 };
  460. f.Add (v1, v2);
  461. w.Add (f);
  462. top.Add (w);
  463. Application.Begin (top);
  464. f.X = Pos.X (Application.Top) + Pos.X (v2) - Pos.X (v1);
  465. f.Y = Pos.Y (Application.Top) + Pos.Y (v2) - Pos.Y (v1);
  466. Application.Top.LayoutComplete += (s, e) =>
  467. {
  468. Assert.Equal (0, Application.Top.Frame.X);
  469. Assert.Equal (0, Application.Top.Frame.Y);
  470. Assert.Equal (2, w.Frame.X);
  471. Assert.Equal (2, w.Frame.Y);
  472. Assert.Equal (2, f.Frame.X);
  473. Assert.Equal (2, f.Frame.Y);
  474. Assert.Equal (4, v1.Frame.X);
  475. Assert.Equal (4, v1.Frame.Y);
  476. Assert.Equal (6, v2.Frame.X);
  477. Assert.Equal (6, v2.Frame.Y);
  478. };
  479. Application.Iteration += (s, a) => Application.RequestStop ();
  480. Assert.Throws<InvalidOperationException> (() => Application.Run ());
  481. top.Dispose ();
  482. Application.Shutdown ();
  483. }
  484. [Fact]
  485. public void TopologicalSort_Missing_Add ()
  486. {
  487. var root = new View ();
  488. var sub1 = new View ();
  489. root.Add (sub1);
  490. var sub2 = new View ();
  491. sub1.Width = Dim.Width (sub2);
  492. Assert.Throws<InvalidOperationException> (() => root.LayoutSubviews ());
  493. sub2.Width = Dim.Width (sub1);
  494. Assert.Throws<InvalidOperationException> (() => root.LayoutSubviews ());
  495. root.Dispose ();
  496. sub1.Dispose ();
  497. sub2.Dispose ();
  498. }
  499. [Fact]
  500. public void TopologicalSort_Recursive_Ref ()
  501. {
  502. var root = new View ();
  503. var sub1 = new View ();
  504. root.Add (sub1);
  505. var sub2 = new View ();
  506. root.Add (sub2);
  507. sub2.Width = Dim.Width (sub2);
  508. Exception exception = Record.Exception (root.LayoutSubviews);
  509. Assert.Null (exception);
  510. root.Dispose ();
  511. sub1.Dispose ();
  512. sub2.Dispose ();
  513. }
  514. [Fact]
  515. [AutoInitShutdown]
  516. public void TrySetHeight_ForceValidatePosDim ()
  517. {
  518. var top = new View { X = 0, Y = 0, Height = 20 };
  519. var v = new View { Height = Dim.Fill (), ValidatePosDim = true };
  520. top.Add (v);
  521. Assert.False (v.TrySetHeight (10, out int rHeight));
  522. Assert.Equal (10, rHeight);
  523. v.Height = Dim.Fill (1);
  524. Assert.False (v.TrySetHeight (10, out rHeight));
  525. Assert.Equal (9, rHeight);
  526. v.Height = 0;
  527. Assert.True (v.TrySetHeight (10, out rHeight));
  528. Assert.Equal (10, rHeight);
  529. Assert.False (v.IsInitialized);
  530. var toplevel = new Toplevel ();
  531. toplevel.Add (top);
  532. Application.Begin (toplevel);
  533. Assert.True (v.IsInitialized);
  534. v.Height = 15;
  535. Assert.True (v.TrySetHeight (5, out rHeight));
  536. Assert.Equal (5, rHeight);
  537. }
  538. [Fact]
  539. [AutoInitShutdown]
  540. public void TrySetWidth_ForceValidatePosDim ()
  541. {
  542. var top = new View { X = 0, Y = 0, Width = 80 };
  543. var v = new View { Width = Dim.Fill (), ValidatePosDim = true };
  544. top.Add (v);
  545. Assert.False (v.TrySetWidth (70, out int rWidth));
  546. Assert.Equal (70, rWidth);
  547. v.Width = Dim.Fill (1);
  548. Assert.False (v.TrySetWidth (70, out rWidth));
  549. Assert.Equal (69, rWidth);
  550. v.Width = 0;
  551. Assert.True (v.TrySetWidth (70, out rWidth));
  552. Assert.Equal (70, rWidth);
  553. Assert.False (v.IsInitialized);
  554. var toplevel = new Toplevel ();
  555. toplevel.Add (top);
  556. Application.Begin (toplevel);
  557. Assert.True (v.IsInitialized);
  558. v.Width = 75;
  559. Assert.True (v.TrySetWidth (60, out rWidth));
  560. Assert.Equal (60, rWidth);
  561. }
  562. }