DrawTests.cs 39 KB

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