DrawTests.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. #nullable enable
  2. using System.Text;
  3. using Xunit.Abstractions;
  4. namespace Terminal.Gui.ViewTests;
  5. [Trait ("Category", "Output")]
  6. public class DrawTests (ITestOutputHelper _output)
  7. {
  8. [Fact]
  9. [SetupFakeDriver]
  10. public void Move_Is_Constrained_To_Viewport ()
  11. {
  12. var view = new View
  13. {
  14. X = 1,
  15. Y = 1,
  16. Width = 3, Height = 3
  17. };
  18. view.Margin.Thickness = new (1);
  19. // Only valid location w/in Viewport is 0, 0 (view) - 2, 2 (screen)
  20. view.Move (0, 0);
  21. Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row));
  22. view.Move (-1, -1);
  23. Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row));
  24. view.Move (1, 1);
  25. Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row));
  26. }
  27. [Fact]
  28. [SetupFakeDriver]
  29. public void AddRune_Is_Constrained_To_Viewport ()
  30. {
  31. var view = new View
  32. {
  33. X = 1,
  34. Y = 1,
  35. Width = 3, Height = 3
  36. };
  37. view.Padding.Thickness = new (1);
  38. view.Padding.Diagnostics = ViewDiagnosticFlags.Thickness;
  39. view.BeginInit ();
  40. view.EndInit ();
  41. view.Draw ();
  42. // Only valid location w/in Viewport is 0, 0 (view) - 2, 2 (screen)
  43. Assert.Equal ((Rune)' ', Application.Driver?.Contents! [2, 2].Rune);
  44. // When we exit Draw, the view is excluded from the clip. So drawing at 0,0, is not valid and is clipped.
  45. view.AddRune (0, 0, Rune.ReplacementChar);
  46. Assert.Equal ((Rune)' ', Application.Driver?.Contents! [2, 2].Rune);
  47. view.AddRune (-1, -1, Rune.ReplacementChar);
  48. Assert.Equal ((Rune)'P', Application.Driver?.Contents! [1, 1].Rune);
  49. view.AddRune (1, 1, Rune.ReplacementChar);
  50. Assert.Equal ((Rune)'P', Application.Driver?.Contents! [3, 3].Rune);
  51. }
  52. [Theory]
  53. [InlineData (0, 0, 1, 1)]
  54. [InlineData (0, 0, 2, 2)]
  55. [InlineData (-1, -1, 2, 2)]
  56. [SetupFakeDriver]
  57. public void FillRect_Fills_HonorsClip (int x, int y, int width, int height)
  58. {
  59. var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
  60. var view = new View
  61. {
  62. Text = "X",
  63. X = 1, Y = 1,
  64. Width = 3, Height = 3,
  65. BorderStyle = LineStyle.Single
  66. };
  67. superView.Add (view);
  68. superView.BeginInit ();
  69. superView.EndInit ();
  70. superView.LayoutSubviews ();
  71. superView.Draw ();
  72. TestHelpers.AssertDriverContentsWithFrameAre (
  73. @"
  74. ┌─┐
  75. │X│
  76. └─┘",
  77. _output);
  78. Rectangle toFill = new (x, y, width, height);
  79. View.SetClipToScreen ();
  80. view.FillRect (toFill);
  81. TestHelpers.AssertDriverContentsWithFrameAre (
  82. @"
  83. ┌─┐
  84. │ │
  85. └─┘",
  86. _output);
  87. // Now try to clear beyond Viewport (invalid; clipping should prevent)
  88. superView.SetNeedsDraw ();
  89. superView.Draw ();
  90. TestHelpers.AssertDriverContentsWithFrameAre (
  91. @"
  92. ┌─┐
  93. │X│
  94. └─┘",
  95. _output);
  96. toFill = new (-width, -height, width, height);
  97. view.FillRect (toFill);
  98. TestHelpers.AssertDriverContentsWithFrameAre (
  99. @"
  100. ┌─┐
  101. │X│
  102. └─┘",
  103. _output);
  104. // Now try to clear beyond Viewport (valid)
  105. superView.SetNeedsDraw ();
  106. superView.Draw ();
  107. TestHelpers.AssertDriverContentsWithFrameAre (
  108. @"
  109. ┌─┐
  110. │X│
  111. └─┘",
  112. _output);
  113. toFill = new (-1, -1, width + 1, height + 1);
  114. View.SetClipToScreen ();
  115. view.FillRect (toFill);
  116. TestHelpers.AssertDriverContentsWithFrameAre (
  117. @"
  118. ┌─┐
  119. │ │
  120. └─┘",
  121. _output);
  122. // Now clear too much size
  123. superView.SetNeedsDraw ();
  124. superView.Draw ();
  125. TestHelpers.AssertDriverContentsWithFrameAre (
  126. @"
  127. ┌─┐
  128. │X│
  129. └─┘",
  130. _output);
  131. toFill = new (0, 0, width * 2, height * 2);
  132. View.SetClipToScreen ();
  133. view.FillRect (toFill);
  134. TestHelpers.AssertDriverContentsWithFrameAre (
  135. @"
  136. ┌─┐
  137. │ │
  138. └─┘",
  139. _output);
  140. }
  141. [Fact]
  142. [SetupFakeDriver]
  143. public void Clear_ClearsEntireViewport ()
  144. {
  145. var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
  146. var view = new View
  147. {
  148. Text = "X",
  149. X = 1, Y = 1,
  150. Width = 3, Height = 3,
  151. BorderStyle = LineStyle.Single
  152. };
  153. superView.Add (view);
  154. superView.BeginInit ();
  155. superView.EndInit ();
  156. superView.LayoutSubviews ();
  157. superView.Draw ();
  158. TestHelpers.AssertDriverContentsWithFrameAre (
  159. @"
  160. ┌─┐
  161. │X│
  162. └─┘",
  163. _output);
  164. // On Draw exit the view is excluded from the clip, so this will do nothing.
  165. view.ClearViewport ();
  166. TestHelpers.AssertDriverContentsWithFrameAre (
  167. @"
  168. ┌─┐
  169. │X│
  170. └─┘",
  171. _output);
  172. View.SetClipToScreen ();
  173. view.ClearViewport ();
  174. TestHelpers.AssertDriverContentsWithFrameAre (
  175. @"
  176. ┌─┐
  177. │ │
  178. └─┘",
  179. _output);
  180. }
  181. [Fact]
  182. [SetupFakeDriver]
  183. public void Clear_WithClearVisibleContentOnly_ClearsVisibleContentOnly ()
  184. {
  185. var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
  186. var view = new View
  187. {
  188. Text = "X",
  189. X = 1, Y = 1,
  190. Width = 3, Height = 3,
  191. BorderStyle = LineStyle.Single,
  192. ViewportSettings = ViewportSettings.ClearContentOnly
  193. };
  194. superView.Add (view);
  195. superView.BeginInit ();
  196. superView.EndInit ();
  197. superView.LayoutSubviews ();
  198. superView.Draw ();
  199. TestHelpers.AssertDriverContentsWithFrameAre (
  200. @"
  201. ┌─┐
  202. │X│
  203. └─┘",
  204. _output);
  205. View.SetClipToScreen ();
  206. view.ClearViewport ();
  207. TestHelpers.AssertDriverContentsWithFrameAre (
  208. @"
  209. ┌─┐
  210. │ │
  211. └─┘",
  212. _output);
  213. }
  214. [Fact]
  215. [AutoInitShutdown]
  216. [Trait ("Category", "Unicode")]
  217. public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two ()
  218. {
  219. const string us = "\U0000f900";
  220. var r = (Rune)0xf900;
  221. Assert.Equal ("豈", us);
  222. Assert.Equal ("豈", r.ToString ());
  223. Assert.Equal (us, r.ToString ());
  224. Assert.Equal (2, us.GetColumns ());
  225. Assert.Equal (2, r.GetColumns ());
  226. var win = new Window { Title = us };
  227. var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () };
  228. var tf = new TextField { Text = us, Y = 1, Width = 3 };
  229. win.Add (view, tf);
  230. Toplevel top = new ();
  231. top.Add (win);
  232. Application.Begin (top);
  233. ((FakeDriver)Application.Driver!).SetBufferSize (10, 4);
  234. const string expectedOutput = """
  235. ┌┤豈├────┐
  236. │豈 │
  237. │豈 │
  238. └────────┘
  239. """;
  240. TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  241. TestHelpers.AssertDriverContentsAre (expectedOutput, _output);
  242. // This test has nothing to do with color - removing as it is not relevant and fragile
  243. top.Dispose ();
  244. }
  245. // TODO: Simplify this test to just use AddRune directly
  246. [Fact]
  247. [SetupFakeDriver]
  248. [Trait ("Category", "Unicode")]
  249. public void Clipping_Wide_Runes ()
  250. {
  251. ((FakeDriver)Application.Driver!).SetBufferSize (30, 1);
  252. var top = new View ()
  253. {
  254. Id = "top",
  255. Width = Dim.Fill (),
  256. Height = Dim.Fill (),
  257. };
  258. var frameView = new View ()
  259. {
  260. Id = "frameView",
  261. Width = Dim.Fill (),
  262. Height = Dim.Fill (),
  263. Text = """
  264. これは広いルーンラインです。
  265. """,
  266. };
  267. frameView.Border.LineStyle = LineStyle.Single;
  268. frameView.Border.Thickness = new (1, 0, 0, 0);
  269. top.Add (frameView);
  270. View.SetClipToScreen ();
  271. top.Layout ();
  272. top.Draw ();
  273. string expectedOutput = """
  274. │これは広いルーンラインです。
  275. """;
  276. TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  277. var view = new View
  278. {
  279. Text = "0123456789",
  280. //Text = "ワイドルー。",
  281. X = 2,
  282. Height = Dim.Auto (),
  283. Width = Dim.Auto (),
  284. BorderStyle = LineStyle.Single
  285. };
  286. view.Border.Thickness = new (1, 0, 1, 0);
  287. top.Add (view);
  288. top.Layout ();
  289. View.SetClipToScreen ();
  290. top.Draw ();
  291. // 012345678901234567890123456789012345678
  292. // 012 34 56 78 90 12 34 56 78 90 12 34 56 78
  293. // │こ れ は 広 い ル ー ン ラ イ ン で す 。
  294. // 01 2345678901234 56 78 90 12 34 56
  295. // │� |0123456989│� ン ラ イ ン で す 。
  296. expectedOutput = """
  297. │�│0123456789│�ンラインです。
  298. """;
  299. TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  300. }
  301. // TODO: Add more AddRune tests to cover all the cases where wide runes are clipped
  302. [Fact]
  303. [AutoInitShutdown]
  304. [Trait ("Category", "Output")]
  305. public void Colors_On_TextAlignment_Right_And_Bottom ()
  306. {
  307. var viewRight = new View
  308. {
  309. Text = "Test",
  310. Width = 6,
  311. Height = 1,
  312. TextAlignment = Alignment.End,
  313. ColorScheme = Colors.ColorSchemes ["Base"]
  314. };
  315. var viewBottom = new View
  316. {
  317. Text = "Test",
  318. TextDirection = TextDirection.TopBottom_LeftRight,
  319. Y = 1,
  320. Width = 1,
  321. Height = 6,
  322. VerticalTextAlignment = Alignment.End,
  323. ColorScheme = Colors.ColorSchemes ["Base"]
  324. };
  325. Toplevel top = new ();
  326. top.Add (viewRight, viewBottom);
  327. var rs = Application.Begin (top);
  328. ((FakeDriver)Application.Driver!).SetBufferSize (7, 7);
  329. Application.RunIteration (ref rs);
  330. TestHelpers.AssertDriverContentsWithFrameAre (
  331. """
  332. Test
  333. T
  334. e
  335. s
  336. t
  337. """,
  338. _output
  339. );
  340. TestHelpers.AssertDriverAttributesAre (
  341. """
  342. 000000
  343. 0
  344. 0
  345. 0
  346. 0
  347. 0
  348. 0
  349. """,
  350. _output,
  351. Application.Driver,
  352. Colors.ColorSchemes ["Base"]!.Normal
  353. );
  354. top.Dispose ();
  355. }
  356. [Fact]
  357. [SetupFakeDriver]
  358. public void Draw_Minimum_Full_Border_With_Empty_Viewport ()
  359. {
  360. var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
  361. Assert.True (view.NeedsLayout);
  362. Assert.True (view.NeedsDraw);
  363. view.Layout ();
  364. Assert.Equal (new (0, 0, 2, 2), view.Frame);
  365. Assert.Equal (Rectangle.Empty, view.Viewport);
  366. Assert.True (view.NeedsDraw);
  367. view.Draw ();
  368. TestHelpers.AssertDriverContentsWithFrameAre (
  369. """
  370. ┌┐
  371. └┘
  372. """,
  373. _output
  374. );
  375. }
  376. [Fact]
  377. [SetupFakeDriver]
  378. public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Bottom ()
  379. {
  380. var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
  381. view.Border.Thickness = new (1, 1, 1, 0);
  382. view.BeginInit ();
  383. view.EndInit ();
  384. view.SetRelativeLayout (Application.Screen.Size);
  385. Assert.Equal (new (0, 0, 2, 1), view.Frame);
  386. Assert.Equal (Rectangle.Empty, view.Viewport);
  387. view.Draw ();
  388. TestHelpers.AssertDriverContentsWithFrameAre ("──", _output);
  389. }
  390. [Fact]
  391. [SetupFakeDriver]
  392. public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Left ()
  393. {
  394. var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
  395. view.Border.Thickness = new (0, 1, 1, 1);
  396. view.BeginInit ();
  397. view.EndInit ();
  398. view.SetRelativeLayout (Application.Screen.Size);
  399. Assert.Equal (new (0, 0, 1, 2), view.Frame);
  400. Assert.Equal (Rectangle.Empty, view.Viewport);
  401. view.Draw ();
  402. TestHelpers.AssertDriverContentsWithFrameAre (
  403. """
  404. """,
  405. _output
  406. );
  407. }
  408. [Fact]
  409. [SetupFakeDriver]
  410. public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Right ()
  411. {
  412. var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
  413. view.Border.Thickness = new (1, 1, 0, 1);
  414. view.BeginInit ();
  415. view.EndInit ();
  416. view.SetRelativeLayout (Application.Screen.Size);
  417. Assert.Equal (new (0, 0, 1, 2), view.Frame);
  418. Assert.Equal (Rectangle.Empty, view.Viewport);
  419. view.Draw ();
  420. TestHelpers.AssertDriverContentsWithFrameAre (
  421. """
  422. """,
  423. _output
  424. );
  425. }
  426. [Fact]
  427. [SetupFakeDriver]
  428. public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Top ()
  429. {
  430. var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
  431. view.Border.Thickness = new (1, 0, 1, 1);
  432. view.BeginInit ();
  433. view.EndInit ();
  434. view.SetRelativeLayout (Application.Screen.Size);
  435. Assert.Equal (new (0, 0, 2, 1), view.Frame);
  436. Assert.Equal (Rectangle.Empty, view.Viewport);
  437. view.Draw ();
  438. TestHelpers.AssertDriverContentsWithFrameAre (
  439. "││",
  440. _output
  441. );
  442. }
  443. [Fact]
  444. [AutoInitShutdown]
  445. public void Draw_Negative_Viewport_Horizontal_With_New_Lines ()
  446. {
  447. var subView = new View
  448. {
  449. Id = "subView",
  450. X = 1,
  451. Width = 1,
  452. Height = 7,
  453. Text = """
  454. s
  455. u
  456. b
  457. V
  458. i
  459. e
  460. w
  461. """
  462. };
  463. var view = new View
  464. {
  465. Id = "view", Width = 2, Height = 20, Text = """
  466. 0
  467. 1
  468. 2
  469. 3
  470. 4
  471. 5
  472. 6
  473. 7
  474. 8
  475. 9
  476. 0
  477. 1
  478. 2
  479. 3
  480. 4
  481. 5
  482. 6
  483. 7
  484. 8
  485. 9
  486. """
  487. };
  488. view.Add (subView);
  489. var content = new View { Id = "content", Width = 20, Height = 20 };
  490. content.Add (view);
  491. var container = new View
  492. {
  493. Id = "container",
  494. X = 1,
  495. Y = 1,
  496. Width = 5,
  497. Height = 5
  498. };
  499. container.Add (content);
  500. Toplevel top = new ();
  501. top.Add (container);
  502. var rs = Application.Begin (top);
  503. top.Draw ();
  504. TestHelpers.AssertDriverContentsWithFrameAre (
  505. """
  506. 0s
  507. 1u
  508. 2b
  509. 3V
  510. 4i
  511. """,
  512. _output
  513. );
  514. content.X = -1;
  515. Application.LayoutAndDraw ();
  516. TestHelpers.AssertDriverContentsWithFrameAre (
  517. """
  518. s
  519. u
  520. b
  521. V
  522. i
  523. """,
  524. _output
  525. );
  526. content.X = -2;
  527. Application.LayoutAndDraw ();
  528. TestHelpers.AssertDriverContentsWithFrameAre (@"", _output);
  529. content.X = 0;
  530. content.Y = -1;
  531. Application.LayoutAndDraw ();
  532. TestHelpers.AssertDriverContentsWithFrameAre (
  533. """
  534. 1u
  535. 2b
  536. 3V
  537. 4i
  538. 5e
  539. """,
  540. _output
  541. );
  542. content.Y = -6;
  543. Application.LayoutAndDraw ();
  544. TestHelpers.AssertDriverContentsWithFrameAre (
  545. """
  546. 6w
  547. 7
  548. 8
  549. 9
  550. 0
  551. """,
  552. _output
  553. );
  554. content.Y = -19;
  555. Application.LayoutAndDraw ();
  556. TestHelpers.AssertDriverContentsWithFrameAre (
  557. """
  558. 9
  559. """,
  560. _output
  561. );
  562. content.Y = -20;
  563. Application.LayoutAndDraw ();
  564. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  565. content.X = -2;
  566. content.Y = 0;
  567. Application.LayoutAndDraw ();
  568. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  569. top.Dispose ();
  570. }
  571. [Fact]
  572. [AutoInitShutdown]
  573. public void Draw_Negative_Viewport_Horizontal_Without_New_Lines ()
  574. {
  575. // BUGBUG: This previously assumed the default height of a View was 1.
  576. var subView = new View
  577. {
  578. Id = "subView",
  579. Y = 1,
  580. Width = 7,
  581. Height = 1,
  582. Text = "subView"
  583. };
  584. var view = new View { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" };
  585. view.Add (subView);
  586. var content = new View { Id = "content", Width = 20, Height = 20 };
  587. content.Add (view);
  588. var container = new View
  589. {
  590. Id = "container",
  591. X = 1,
  592. Y = 1,
  593. Width = 5,
  594. Height = 5
  595. };
  596. container.Add (content);
  597. Toplevel top = new ();
  598. top.Add (container);
  599. // BUGBUG: v2 - it's bogus to reference .Frame before BeginInit. And why is the clip being set anyway???
  600. top.SubviewsLaidOut += Top_LayoutComplete;
  601. Application.Begin (top);
  602. Application.LayoutAndDraw ();
  603. TestHelpers.AssertDriverContentsWithFrameAre (
  604. """
  605. 01234
  606. subVi
  607. """,
  608. _output
  609. );
  610. content.X = -1;
  611. Application.LayoutAndDraw ();
  612. TestHelpers.AssertDriverContentsWithFrameAre (
  613. """
  614. 12345
  615. ubVie
  616. """,
  617. _output
  618. );
  619. content.Y = -1;
  620. Application.LayoutAndDraw ();
  621. TestHelpers.AssertDriverContentsWithFrameAre (
  622. """
  623. ubVie
  624. """,
  625. _output
  626. );
  627. content.Y = -2;
  628. Application.LayoutAndDraw ();
  629. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  630. content.X = -20;
  631. content.Y = 0;
  632. Application.LayoutAndDraw ();
  633. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  634. top.Dispose ();
  635. return;
  636. void Top_LayoutComplete (object? sender, LayoutEventArgs e) { Application.Driver!.Clip = new (container.Frame); }
  637. }
  638. [Fact]
  639. [AutoInitShutdown]
  640. public void Draw_Negative_Viewport_Vertical ()
  641. {
  642. var subView = new View
  643. {
  644. Id = "subView",
  645. X = 1,
  646. Width = 1,
  647. Height = 7,
  648. Text = "subView",
  649. TextDirection = TextDirection.TopBottom_LeftRight
  650. };
  651. var view = new View
  652. {
  653. Id = "view",
  654. Width = 2,
  655. Height = 20,
  656. Text = "01234567890123456789",
  657. TextDirection = TextDirection.TopBottom_LeftRight
  658. };
  659. view.Add (subView);
  660. var content = new View { Id = "content", Width = 20, Height = 20 };
  661. content.Add (view);
  662. var container = new View
  663. {
  664. Id = "container",
  665. X = 1,
  666. Y = 1,
  667. Width = 5,
  668. Height = 5
  669. };
  670. container.Add (content);
  671. Toplevel top = new ();
  672. top.Add (container);
  673. Application.Begin (top);
  674. Application.LayoutAndDraw ();
  675. TestHelpers.AssertDriverContentsWithFrameAre (
  676. """
  677. 0s
  678. 1u
  679. 2b
  680. 3V
  681. 4i
  682. """,
  683. _output
  684. );
  685. content.X = -1;
  686. Application.LayoutAndDraw ();
  687. TestHelpers.AssertDriverContentsWithFrameAre (
  688. """
  689. s
  690. u
  691. b
  692. V
  693. i
  694. """,
  695. _output
  696. );
  697. content.X = -2;
  698. Application.LayoutAndDraw ();
  699. TestHelpers.AssertDriverContentsWithFrameAre (@"", _output);
  700. content.X = 0;
  701. content.Y = -1;
  702. Application.LayoutAndDraw ();
  703. TestHelpers.AssertDriverContentsWithFrameAre (
  704. """
  705. 1u
  706. 2b
  707. 3V
  708. 4i
  709. 5e
  710. """,
  711. _output
  712. );
  713. content.Y = -6;
  714. Application.LayoutAndDraw ();
  715. TestHelpers.AssertDriverContentsWithFrameAre (
  716. """
  717. 6w
  718. 7
  719. 8
  720. 9
  721. 0
  722. """,
  723. _output
  724. );
  725. content.Y = -19;
  726. Application.LayoutAndDraw ();
  727. TestHelpers.AssertDriverContentsWithFrameAre (
  728. """
  729. 9
  730. """,
  731. _output
  732. );
  733. content.Y = -20;
  734. Application.LayoutAndDraw ();
  735. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  736. content.X = -2;
  737. content.Y = 0;
  738. Application.LayoutAndDraw ();
  739. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  740. top.Dispose ();
  741. }
  742. [Theory]
  743. [SetupFakeDriver]
  744. [InlineData ("𝔽𝕆𝕆𝔹𝔸R")]
  745. [InlineData ("a𐐀b")]
  746. public void DrawHotString_NonBmp (string expected)
  747. {
  748. var view = new View { Width = 10, Height = 1 };
  749. view.DrawHotString (expected, Attribute.Default, Attribute.Default);
  750. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  751. }
  752. // TODO: The tests below that use Label should use View instead.
  753. [Fact]
  754. [AutoInitShutdown]
  755. public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two ()
  756. {
  757. var us = "\U0001d539";
  758. var r = (Rune)0x1d539;
  759. Assert.Equal ("𝔹", us);
  760. Assert.Equal ("𝔹", r.ToString ());
  761. Assert.Equal (us, r.ToString ());
  762. Assert.Equal (1, us.GetColumns ());
  763. Assert.Equal (1, r.GetColumns ());
  764. var win = new Window { Title = us };
  765. var view = new Label { Text = r.ToString () };
  766. var tf = new TextField { Text = us, Y = 1, Width = 3 };
  767. win.Add (view, tf);
  768. Toplevel top = new ();
  769. top.Add (win);
  770. Application.Begin (top);
  771. ((FakeDriver)Application.Driver!).SetBufferSize (10, 4);
  772. var expected = """
  773. ┌┤𝔹├─────┐
  774. │𝔹 │
  775. │𝔹 │
  776. └────────┘
  777. """;
  778. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  779. TestHelpers.AssertDriverContentsAre (expected, _output);
  780. top.Dispose ();
  781. // This test has nothing to do with color - removing as it is not relevant and fragile
  782. }
  783. [Fact]
  784. [SetupFakeDriver]
  785. public void SetClip_ClipVisibleContentOnly_VisibleContentIsClipped ()
  786. {
  787. // Screen is 25x25
  788. // View is 25x25
  789. // Viewport is (0, 0, 23, 23)
  790. // ContentSize is (10, 10)
  791. // ViewportToScreen is (1, 1, 23, 23)
  792. // Visible content is (1, 1, 10, 10)
  793. // Expected clip is (1, 1, 10, 10) - same as visible content
  794. Rectangle expectedClip = new (1, 1, 10, 10);
  795. // Arrange
  796. var view = new View
  797. {
  798. Width = Dim.Fill (),
  799. Height = Dim.Fill (),
  800. ViewportSettings = ViewportSettings.ClipContentOnly
  801. };
  802. view.SetContentSize (new Size (10, 10));
  803. view.Border.Thickness = new (1);
  804. view.BeginInit ();
  805. view.EndInit ();
  806. Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
  807. // Act
  808. view.ClipViewport ();
  809. // Assert
  810. Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
  811. view.Dispose ();
  812. }
  813. [Fact]
  814. [SetupFakeDriver]
  815. public void SetClip_Default_ClipsToViewport ()
  816. {
  817. // Screen is 25x25
  818. // View is 25x25
  819. // Viewport is (0, 0, 23, 23)
  820. // ContentSize is (10, 10)
  821. // ViewportToScreen is (1, 1, 23, 23)
  822. // Visible content is (1, 1, 10, 10)
  823. // Expected clip is (1, 1, 23, 23) - same as Viewport
  824. Rectangle expectedClip = new (1, 1, 23, 23);
  825. // Arrange
  826. var view = new View
  827. {
  828. Width = Dim.Fill (),
  829. Height = Dim.Fill ()
  830. };
  831. view.SetContentSize (new Size (10, 10));
  832. view.Border.Thickness = new (1);
  833. view.BeginInit ();
  834. view.EndInit ();
  835. Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
  836. view.Viewport = view.Viewport with { X = 1, Y = 1 };
  837. // Act
  838. view.ClipViewport ();
  839. // Assert
  840. Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
  841. view.Dispose ();
  842. }
  843. [Fact]
  844. [TestRespondersDisposed]
  845. public void Draw_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
  846. {
  847. Application.Init (new FakeDriver ());
  848. Toplevel top = new ();
  849. var view = new View { X = -2, Text = "view" };
  850. top.Add (view);
  851. Application.Iteration += (s, a) =>
  852. {
  853. Assert.Equal (-2, view.X);
  854. Application.RequestStop ();
  855. };
  856. try
  857. {
  858. Application.Run (top);
  859. }
  860. catch (IndexOutOfRangeException ex)
  861. {
  862. // After the fix this exception will not be caught.
  863. Assert.IsType<IndexOutOfRangeException> (ex);
  864. }
  865. top.Dispose ();
  866. // Shutdown must be called to safely clean up Application if Init has been called
  867. Application.Shutdown ();
  868. }
  869. }