DrawTests.cs 35 KB

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