DrawTests.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. #nullable enable
  2. using System.Text;
  3. using Xunit.Abstractions;
  4. namespace Terminal.Gui.ViewTests;
  5. [Trait("Category","Output")]
  6. public class DrawTests
  7. {
  8. private readonly ITestOutputHelper _output;
  9. public DrawTests (ITestOutputHelper output) { _output = output; }
  10. [Theory]
  11. [InlineData (0, 0, 1, 1)]
  12. [InlineData (0, 0, 2, 2)]
  13. [InlineData (-1, -1, 2, 2)]
  14. [SetupFakeDriver]
  15. public void Clear_Bounds_Clears_Only_Bounds (int x, int y, int width, int height)
  16. {
  17. var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
  18. var view = new View
  19. {
  20. Text = "X",
  21. X = 1, Y = 1,
  22. Width = 3, Height = 3,
  23. BorderStyle = LineStyle.Single
  24. };
  25. superView.Add (view);
  26. superView.BeginInit ();
  27. superView.EndInit ();
  28. superView.LayoutSubviews ();
  29. superView.Draw ();
  30. TestHelpers.AssertDriverContentsWithFrameAre (
  31. @"
  32. ┌─┐
  33. │X│
  34. └─┘",
  35. _output);
  36. Rectangle boundsToClear = new (x, y, width, height);
  37. view.Clear (boundsToClear);
  38. TestHelpers.AssertDriverContentsWithFrameAre (
  39. @"
  40. ┌─┐
  41. │ │
  42. └─┘",
  43. _output);
  44. }
  45. [Fact]
  46. [AutoInitShutdown]
  47. [Trait("Category","Unicode")]
  48. public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two ()
  49. {
  50. const string us = "\U0000f900";
  51. var r = (Rune)0xf900;
  52. Assert.Equal ("豈", us);
  53. Assert.Equal ("豈", r.ToString ());
  54. Assert.Equal (us, r.ToString ());
  55. Assert.Equal (2, us.GetColumns ());
  56. Assert.Equal (2, r.GetColumns ());
  57. var win = new Window { Title = us };
  58. var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () };
  59. var tf = new TextField { Text = us, Y = 1, Width = 3 };
  60. win.Add (view, tf);
  61. Toplevel top = Application.Top;
  62. top.Add (win);
  63. Application.Begin (top);
  64. ((FakeDriver)Application.Driver).SetBufferSize (10, 4);
  65. const string expectedOutput = """
  66. ┌┤豈├────┐
  67. │豈 │
  68. │豈 │
  69. └────────┘
  70. """;
  71. TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  72. TestHelpers.AssertDriverContentsAre (expectedOutput, _output);
  73. Attribute [] expectedColors =
  74. {
  75. // 0
  76. Colors.ColorSchemes ["Base"].Normal,
  77. // 1
  78. Colors.ColorSchemes ["Base"].Focus,
  79. // 2
  80. Colors.ColorSchemes ["Base"].HotNormal
  81. };
  82. TestHelpers.AssertDriverAttributesAre (
  83. """
  84. 0011000000
  85. 0000000000
  86. 0111000000
  87. 0000000000
  88. """,
  89. Application.Driver,
  90. expectedColors
  91. );
  92. }
  93. // TODO: Refactor this test to not depend on TextView etc... Make it as primitive as possible
  94. [Fact]
  95. [AutoInitShutdown]
  96. [Trait("Category","Unicode")]
  97. public void Clipping_AddRune_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space ()
  98. {
  99. var tv = new TextView
  100. {
  101. Width = Dim.Fill (),
  102. Height = Dim.Fill (),
  103. Text = """
  104. これは広いルーンラインです。
  105. これは広いルーンラインです。
  106. これは広いルーンラインです。
  107. これは広いルーンラインです。
  108. これは広いルーンラインです。
  109. これは広いルーンラインです。
  110. これは広いルーンラインです。
  111. これは広いルーンラインです。
  112. """
  113. };
  114. var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
  115. win.Add (tv);
  116. Application.Top.Add (win);
  117. // Don't use Label. It sets AutoSize = true which is not what we're testing here.
  118. var view = new View { Text = "ワイドルーン。", Height = Dim.Fill (), Width = Dim.Fill () };
  119. // Don't have unit tests use things that aren't absolutely critical for the test, like Dialog
  120. var dg = new Window { X = 2, Y = 2, Width = 14, Height = 3 };
  121. dg.Add (view);
  122. Application.Begin (Application.Top);
  123. Application.Begin (dg);
  124. ((FakeDriver)Application.Driver).SetBufferSize (30, 10);
  125. const string expectedOutput = """
  126. ┌────────────────────────────┐
  127. │これは広いルーンラインです。│
  128. │�┌────────────┐�ラインです。│
  129. │�│ワイドルーン│�ラインです。│
  130. │�└────────────┘�ラインです。│
  131. │これは広いルーンラインです。│
  132. │これは広いルーンラインです。│
  133. │これは広いルーンラインです。│
  134. │これは広いルーンラインです。│
  135. └────────────────────────────┘
  136. """;
  137. Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  138. Assert.Equal (new Rectangle (0, 0, 30, 10), pos);
  139. }
  140. [Fact]
  141. [AutoInitShutdown]
  142. [Trait("Category","Output")]
  143. public void Colors_On_TextAlignment_Right_And_Bottom ()
  144. {
  145. var viewRight = new View
  146. {
  147. Text = "Test",
  148. Width = 6,
  149. Height = 1,
  150. TextAlignment = TextAlignment.Right,
  151. ColorScheme = Colors.ColorSchemes ["Base"]
  152. };
  153. var viewBottom = new View
  154. {
  155. Text = "Test",
  156. TextDirection = TextDirection.TopBottom_LeftRight,
  157. Y = 1,
  158. Width = 1,
  159. Height = 6,
  160. VerticalTextAlignment = VerticalTextAlignment.Bottom,
  161. ColorScheme = Colors.ColorSchemes ["Base"]
  162. };
  163. Toplevel top = Application.Top;
  164. top.Add (viewRight, viewBottom);
  165. Application.Begin (top);
  166. ((FakeDriver)Application.Driver).SetBufferSize (7, 7);
  167. TestHelpers.AssertDriverContentsWithFrameAre (
  168. """
  169. Test
  170. T
  171. e
  172. s
  173. t
  174. """,
  175. _output
  176. );
  177. TestHelpers.AssertDriverAttributesAre (
  178. """
  179. 000000
  180. 0
  181. 0
  182. 0
  183. 0
  184. 0
  185. 0
  186. """,
  187. Application.Driver,
  188. Colors.ColorSchemes ["Base"].Normal
  189. );
  190. }
  191. [Fact]
  192. [SetupFakeDriver]
  193. public void Draw_Minimum_Full_Border_With_Empty_Bounds ()
  194. {
  195. var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
  196. view.BeginInit ();
  197. view.EndInit ();
  198. view.SetRelativeLayout (Application.Driver.Bounds);
  199. Assert.Equal (new (0,0,2,2), view.Frame);
  200. Assert.Equal (Rectangle.Empty, view.Bounds);
  201. view.Draw ();
  202. TestHelpers.AssertDriverContentsWithFrameAre (
  203. """
  204. ┌┐
  205. └┘
  206. """,
  207. _output
  208. );
  209. }
  210. [Fact]
  211. [SetupFakeDriver]
  212. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Bottom ()
  213. {
  214. var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
  215. view.Border.Thickness = new Thickness (1, 1, 1, 0);
  216. view.BeginInit ();
  217. view.EndInit ();
  218. view.SetRelativeLayout (Application.Driver.Bounds);
  219. Assert.Equal (new (0,0,2,1), view.Frame);
  220. Assert.Equal (Rectangle.Empty, view.Bounds);
  221. view.Draw ();
  222. TestHelpers.AssertDriverContentsWithFrameAre (string.Empty, _output);
  223. }
  224. [Fact]
  225. [SetupFakeDriver]
  226. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Left ()
  227. {
  228. var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
  229. view.Border.Thickness = new Thickness (0, 1, 1, 1);
  230. view.BeginInit ();
  231. view.EndInit ();
  232. view.SetRelativeLayout (Application.Driver.Bounds);
  233. Assert.Equal (new (0,0,1,2), view.Frame);
  234. Assert.Equal (Rectangle.Empty, view.Bounds);
  235. view.Draw ();
  236. TestHelpers.AssertDriverContentsWithFrameAre (
  237. """
  238. """,
  239. _output
  240. );
  241. }
  242. [Fact]
  243. [SetupFakeDriver]
  244. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Right ()
  245. {
  246. var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
  247. view.Border.Thickness = new Thickness (1, 1, 0, 1);
  248. view.BeginInit ();
  249. view.EndInit ();
  250. view.SetRelativeLayout (Application.Driver.Bounds);
  251. Assert.Equal (new (0,0,1,2), view.Frame);
  252. Assert.Equal (Rectangle.Empty, view.Bounds);
  253. view.Draw ();
  254. TestHelpers.AssertDriverContentsWithFrameAre (
  255. """
  256. """,
  257. _output
  258. );
  259. }
  260. [Fact]
  261. [SetupFakeDriver]
  262. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Top ()
  263. {
  264. var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
  265. view.Border.Thickness = new Thickness (1, 0, 1, 1);
  266. view.BeginInit ();
  267. view.EndInit ();
  268. view.SetRelativeLayout (Application.Driver.Bounds);
  269. Assert.Equal (new (0,0,2,1), view.Frame);
  270. Assert.Equal (Rectangle.Empty, view.Bounds);
  271. view.Draw ();
  272. // BUGBUG: Wha? Is this right? Shouldn't it be "└┘"???
  273. TestHelpers.AssertDriverContentsWithFrameAre (
  274. """
  275. ┌┐
  276. """,
  277. _output
  278. );
  279. }
  280. [Fact]
  281. [AutoInitShutdown]
  282. public void Draw_Negative_Bounds_Horizontal_With_New_Lines ()
  283. {
  284. var subView = new View
  285. {
  286. Id = "subView",
  287. X = 1,
  288. Width = 1,
  289. Height = 7,
  290. Text = """
  291. s
  292. u
  293. b
  294. V
  295. i
  296. e
  297. w
  298. """
  299. };
  300. var view = new View
  301. {
  302. Id = "view", Width = 2, Height = 20, Text = """
  303. 0
  304. 1
  305. 2
  306. 3
  307. 4
  308. 5
  309. 6
  310. 7
  311. 8
  312. 9
  313. 0
  314. 1
  315. 2
  316. 3
  317. 4
  318. 5
  319. 6
  320. 7
  321. 8
  322. 9
  323. """
  324. };
  325. view.Add (subView);
  326. var content = new View { Id = "content", Width = 20, Height = 20 };
  327. content.Add (view);
  328. var container = new View
  329. {
  330. Id = "container",
  331. X = 1,
  332. Y = 1,
  333. Width = 5,
  334. Height = 5
  335. };
  336. container.Add (content);
  337. Toplevel top = Application.Top;
  338. top.Add (container);
  339. Application.Driver.Clip = container.Frame;
  340. Application.Begin (top);
  341. TestHelpers.AssertDriverContentsWithFrameAre (
  342. """
  343. 0s
  344. 1u
  345. 2b
  346. 3V
  347. 4i
  348. """,
  349. _output
  350. );
  351. content.X = -1;
  352. Application.Refresh ();
  353. TestHelpers.AssertDriverContentsWithFrameAre (
  354. """
  355. s
  356. u
  357. b
  358. V
  359. i
  360. """,
  361. _output
  362. );
  363. content.X = -2;
  364. Application.Refresh ();
  365. TestHelpers.AssertDriverContentsWithFrameAre (@"", _output);
  366. content.X = 0;
  367. content.Y = -1;
  368. Application.Refresh ();
  369. TestHelpers.AssertDriverContentsWithFrameAre (
  370. """
  371. 1u
  372. 2b
  373. 3V
  374. 4i
  375. 5e
  376. """,
  377. _output
  378. );
  379. content.Y = -6;
  380. Application.Refresh ();
  381. TestHelpers.AssertDriverContentsWithFrameAre (
  382. """
  383. 6w
  384. 7
  385. 8
  386. 9
  387. 0
  388. """,
  389. _output
  390. );
  391. content.Y = -19;
  392. Application.Refresh ();
  393. TestHelpers.AssertDriverContentsWithFrameAre (
  394. """
  395. 9
  396. """,
  397. _output
  398. );
  399. content.Y = -20;
  400. Application.Refresh ();
  401. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  402. content.X = -2;
  403. content.Y = 0;
  404. Application.Refresh ();
  405. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  406. }
  407. [Fact]
  408. [AutoInitShutdown]
  409. public void Draw_Negative_Bounds_Horizontal_Without_New_Lines ()
  410. {
  411. // BUGBUG: This previously assumed the default height of a View was 1.
  412. var subView = new View
  413. {
  414. Id = "subView",
  415. Y = 1,
  416. Width = 7,
  417. Height = 1,
  418. Text = "subView"
  419. };
  420. var view = new View { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" };
  421. view.Add (subView);
  422. var content = new View { Id = "content", Width = 20, Height = 20 };
  423. content.Add (view);
  424. var container = new View
  425. {
  426. Id = "container",
  427. X = 1,
  428. Y = 1,
  429. Width = 5,
  430. Height = 5
  431. };
  432. container.Add (content);
  433. Toplevel top = Application.Top;
  434. top.Add (container);
  435. // BUGBUG: v2 - it's bogus to reference .Frame before BeginInit. And why is the clip being set anyway???
  436. void Top_LayoutComplete (object sender, LayoutEventArgs e) { Application.Driver.Clip = container.Frame; }
  437. top.LayoutComplete += Top_LayoutComplete;
  438. Application.Begin (top);
  439. TestHelpers.AssertDriverContentsWithFrameAre (
  440. """
  441. 01234
  442. subVi
  443. """,
  444. _output
  445. );
  446. content.X = -1;
  447. Application.Refresh ();
  448. TestHelpers.AssertDriverContentsWithFrameAre (
  449. """
  450. 12345
  451. ubVie
  452. """,
  453. _output
  454. );
  455. content.Y = -1;
  456. Application.Refresh ();
  457. TestHelpers.AssertDriverContentsWithFrameAre (
  458. """
  459. ubVie
  460. """,
  461. _output
  462. );
  463. content.Y = -2;
  464. Application.Refresh ();
  465. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  466. content.X = -20;
  467. content.Y = 0;
  468. Application.Refresh ();
  469. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  470. }
  471. [Fact]
  472. [AutoInitShutdown]
  473. public void Draw_Negative_Bounds_Vertical ()
  474. {
  475. var subView = new View
  476. {
  477. Id = "subView",
  478. X = 1,
  479. Width = 1,
  480. Height = 7,
  481. Text = "subView",
  482. TextDirection = TextDirection.TopBottom_LeftRight
  483. };
  484. var view = new View
  485. {
  486. Id = "view",
  487. Width = 2,
  488. Height = 20,
  489. Text = "01234567890123456789",
  490. TextDirection = TextDirection.TopBottom_LeftRight
  491. };
  492. view.Add (subView);
  493. var content = new View { Id = "content", Width = 20, Height = 20 };
  494. content.Add (view);
  495. var container = new View
  496. {
  497. Id = "container",
  498. X = 1,
  499. Y = 1,
  500. Width = 5,
  501. Height = 5
  502. };
  503. container.Add (content);
  504. Toplevel top = Application.Top;
  505. top.Add (container);
  506. Application.Driver.Clip = container.Frame;
  507. Application.Begin (top);
  508. TestHelpers.AssertDriverContentsWithFrameAre (
  509. """
  510. 0s
  511. 1u
  512. 2b
  513. 3V
  514. 4i
  515. """,
  516. _output
  517. );
  518. content.X = -1;
  519. Application.Refresh ();
  520. TestHelpers.AssertDriverContentsWithFrameAre (
  521. """
  522. s
  523. u
  524. b
  525. V
  526. i
  527. """,
  528. _output
  529. );
  530. content.X = -2;
  531. Application.Refresh ();
  532. TestHelpers.AssertDriverContentsWithFrameAre (@"", _output);
  533. content.X = 0;
  534. content.Y = -1;
  535. Application.Refresh ();
  536. TestHelpers.AssertDriverContentsWithFrameAre (
  537. """
  538. 1u
  539. 2b
  540. 3V
  541. 4i
  542. 5e
  543. """,
  544. _output
  545. );
  546. content.Y = -6;
  547. Application.Refresh ();
  548. TestHelpers.AssertDriverContentsWithFrameAre (
  549. """
  550. 6w
  551. 7
  552. 8
  553. 9
  554. 0
  555. """,
  556. _output
  557. );
  558. content.Y = -19;
  559. Application.Refresh ();
  560. TestHelpers.AssertDriverContentsWithFrameAre (
  561. """
  562. 9
  563. """,
  564. _output
  565. );
  566. content.Y = -20;
  567. Application.Refresh ();
  568. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  569. content.X = -2;
  570. content.Y = 0;
  571. Application.Refresh ();
  572. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  573. }
  574. [Theory]
  575. [SetupFakeDriver]
  576. [InlineData ("𝔽𝕆𝕆𝔹𝔸R")]
  577. [InlineData ("a𐐀b")]
  578. public void DrawHotString_NonBmp (string expected)
  579. {
  580. var view = new View { Width = 10, Height = 1 };
  581. view.DrawHotString (expected, Attribute.Default, Attribute.Default);
  582. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  583. }
  584. // TODO: The tests below that use Label should use View instead.
  585. [Fact]
  586. [AutoInitShutdown]
  587. public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two ()
  588. {
  589. var us = "\U0001d539";
  590. var r = (Rune)0x1d539;
  591. Assert.Equal ("𝔹", us);
  592. Assert.Equal ("𝔹", r.ToString ());
  593. Assert.Equal (us, r.ToString ());
  594. Assert.Equal (1, us.GetColumns ());
  595. Assert.Equal (1, r.GetColumns ());
  596. var win = new Window { Title = us };
  597. var view = new Label { Text = r.ToString () };
  598. var tf = new TextField { Text = us, Y = 1, Width = 3 };
  599. win.Add (view, tf);
  600. Toplevel top = Application.Top;
  601. top.Add (win);
  602. Application.Begin (top);
  603. ((FakeDriver)Application.Driver).SetBufferSize (10, 4);
  604. var expected = """
  605. ┌┤𝔹├─────┐
  606. │𝔹 │
  607. │𝔹 │
  608. └────────┘
  609. """;
  610. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  611. TestHelpers.AssertDriverContentsAre (expected, _output);
  612. Attribute [] expectedColors =
  613. {
  614. // 0
  615. Colors.ColorSchemes ["Base"].Normal,
  616. // 1
  617. Colors.ColorSchemes ["Base"].Focus,
  618. // 2
  619. Colors.ColorSchemes ["Base"].HotNormal
  620. };
  621. TestHelpers.AssertDriverAttributesAre (
  622. """
  623. 0010000000
  624. 0000000000
  625. 0111000000
  626. 0000000000
  627. """,
  628. Application.Driver,
  629. expectedColors
  630. );
  631. }
  632. }