ClipTests.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #nullable enable
  2. using System.Text;
  3. using UnitTests;
  4. using Xunit.Abstractions;
  5. namespace UnitTests.ViewTests;
  6. [Trait ("Category", "Output")]
  7. public class ClipTests (ITestOutputHelper _output)
  8. {
  9. [Fact]
  10. [SetupFakeApplication]
  11. public void Move_Is_Not_Constrained_To_Viewport ()
  12. {
  13. var view = new View
  14. {
  15. App = ApplicationImpl.Instance,
  16. X = 1,
  17. Y = 1,
  18. Width = 3, Height = 3
  19. };
  20. view.Margin!.Thickness = new (1);
  21. view.Move (0, 0);
  22. Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row));
  23. view.Move (-1, -1);
  24. Assert.Equal (new (1, 1), new Point (Application.Driver!.Col, Application.Driver!.Row));
  25. view.Move (1, 1);
  26. Assert.Equal (new (3, 3), new Point (Application.Driver!.Col, Application.Driver!.Row));
  27. }
  28. [Fact]
  29. [SetupFakeApplication]
  30. public void AddRune_Is_Constrained_To_Viewport ()
  31. {
  32. var view = new View
  33. {
  34. App = ApplicationImpl.Instance,
  35. X = 1,
  36. Y = 1,
  37. Width = 3, Height = 3
  38. };
  39. view.Padding!.Thickness = new (1);
  40. view.Padding.Diagnostics = ViewDiagnosticFlags.Thickness;
  41. view.BeginInit ();
  42. view.EndInit ();
  43. view.Draw ();
  44. // Only valid location w/in Viewport is 0, 0 (view) - 2, 2 (screen)
  45. Assert.Equal (" ", Application.Driver?.Contents! [2, 2].Grapheme);
  46. // When we exit Draw, the view is excluded from the clip. So drawing at 0,0, is not valid and is clipped.
  47. view.AddRune (0, 0, Rune.ReplacementChar);
  48. Assert.Equal (" ", Application.Driver?.Contents! [2, 2].Grapheme);
  49. view.AddRune (-1, -1, Rune.ReplacementChar);
  50. Assert.Equal ("P", Application.Driver?.Contents! [1, 1].Grapheme);
  51. view.AddRune (1, 1, Rune.ReplacementChar);
  52. Assert.Equal ("P", Application.Driver?.Contents! [3, 3].Grapheme);
  53. }
  54. [Theory]
  55. [InlineData (0, 0, 1, 1)]
  56. [InlineData (0, 0, 2, 2)]
  57. [InlineData (-1, -1, 2, 2)]
  58. [SetupFakeApplication]
  59. public void FillRect_Fills_HonorsClip (int x, int y, int width, int height)
  60. {
  61. var superView = new View
  62. {
  63. App = ApplicationImpl.Instance,
  64. Width = Dim.Fill (), Height = Dim.Fill ()
  65. };
  66. var view = new View
  67. {
  68. Text = "X",
  69. X = 1, Y = 1,
  70. Width = 3, Height = 3,
  71. BorderStyle = LineStyle.Single
  72. };
  73. superView.Add (view);
  74. superView.BeginInit ();
  75. superView.EndInit ();
  76. superView.LayoutSubViews ();
  77. superView.Draw ();
  78. DriverAssert.AssertDriverContentsWithFrameAre (
  79. @"
  80. ┌─┐
  81. │X│
  82. └─┘",
  83. _output);
  84. Rectangle toFill = new (x, y, width, height);
  85. superView.SetClipToScreen ();
  86. view.FillRect (toFill);
  87. DriverAssert.AssertDriverContentsWithFrameAre (
  88. @"
  89. ┌─┐
  90. │ │
  91. └─┘",
  92. _output);
  93. // Now try to clear beyond Viewport (invalid; clipping should prevent)
  94. superView.SetNeedsDraw ();
  95. superView.Draw ();
  96. DriverAssert.AssertDriverContentsWithFrameAre (
  97. @"
  98. ┌─┐
  99. │X│
  100. └─┘",
  101. _output);
  102. toFill = new (-width, -height, width, height);
  103. view.FillRect (toFill);
  104. DriverAssert.AssertDriverContentsWithFrameAre (
  105. @"
  106. ┌─┐
  107. │X│
  108. └─┘",
  109. _output);
  110. // Now try to clear beyond Viewport (valid)
  111. superView.SetNeedsDraw ();
  112. superView.Draw ();
  113. DriverAssert.AssertDriverContentsWithFrameAre (
  114. @"
  115. ┌─┐
  116. │X│
  117. └─┘",
  118. _output);
  119. toFill = new (-1, -1, width + 1, height + 1);
  120. superView.SetClipToScreen ();
  121. view.FillRect (toFill);
  122. DriverAssert.AssertDriverContentsWithFrameAre (
  123. @"
  124. ┌─┐
  125. │ │
  126. └─┘",
  127. _output);
  128. // Now clear too much size
  129. superView.SetNeedsDraw ();
  130. superView.Draw ();
  131. DriverAssert.AssertDriverContentsWithFrameAre (
  132. @"
  133. ┌─┐
  134. │X│
  135. └─┘",
  136. _output);
  137. toFill = new (0, 0, width * 2, height * 2);
  138. superView.SetClipToScreen ();
  139. view.FillRect (toFill);
  140. DriverAssert.AssertDriverContentsWithFrameAre (
  141. @"
  142. ┌─┐
  143. │ │
  144. └─┘",
  145. _output);
  146. }
  147. // TODO: Simplify this test to just use AddRune directly
  148. [Fact]
  149. [SetupFakeApplication]
  150. [Trait ("Category", "Unicode")]
  151. public void Clipping_Wide_Runes ()
  152. {
  153. Application.Driver!.SetScreenSize (30, 1);
  154. var top = new View
  155. {
  156. App = ApplicationImpl.Instance,
  157. Id = "top",
  158. Width = Dim.Fill (),
  159. Height = Dim.Fill ()
  160. };
  161. var frameView = new View
  162. {
  163. Id = "frameView",
  164. Width = Dim.Fill (),
  165. Height = Dim.Fill (),
  166. Text = """
  167. これは広いルーンラインです。
  168. """
  169. };
  170. frameView.Border!.LineStyle = LineStyle.Single;
  171. frameView.Border!.Thickness = new (1, 0, 0, 0);
  172. top.Add (frameView);
  173. top.SetClipToScreen ();
  174. top.Layout ();
  175. top.Draw ();
  176. var expectedOutput = """
  177. │これは広いルーンラインです。
  178. """;
  179. DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  180. var view = new View
  181. {
  182. Text = "0123456789",
  183. //Text = "ワイドルー。",
  184. X = 2,
  185. Height = Dim.Auto (),
  186. Width = Dim.Auto (),
  187. BorderStyle = LineStyle.Single
  188. };
  189. view.Border!.Thickness = new (1, 0, 1, 0);
  190. top.Add (view);
  191. top.Layout ();
  192. top.SetClipToScreen ();
  193. top.Draw ();
  194. // 012345678901234567890123456789012345678
  195. // 012 34 56 78 90 12 34 56 78 90 12 34 56 78
  196. // │こ れ は 広 い ル ー ン ラ イ ン で す 。
  197. // 01 2345678901234 56 78 90 12 34 56
  198. // │� |0123456989│� ン ラ イ ン で す 。
  199. expectedOutput = """
  200. │�│0123456789│ ンラインです。
  201. """;
  202. DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  203. }
  204. // TODO: Add more AddRune tests to cover all the cases where wide runes are clipped
  205. [Fact]
  206. [SetupFakeApplication]
  207. public void SetClip_ClipVisibleContentOnly_VisibleContentIsClipped ()
  208. {
  209. // Screen is 25x25
  210. // View is 25x25
  211. // Viewport is (0, 0, 23, 23)
  212. // ContentSize is (10, 10)
  213. // ViewportToScreen is (1, 1, 23, 23)
  214. // Visible content is (1, 1, 10, 10)
  215. // Expected clip is (1, 1, 10, 10) - same as visible content
  216. Rectangle expectedClip = new (1, 1, 10, 10);
  217. // Arrange
  218. var view = new View
  219. {
  220. Width = Dim.Fill (),
  221. Height = Dim.Fill (),
  222. ViewportSettings = ViewportSettingsFlags.ClipContentOnly,
  223. App = ApplicationImpl.Instance
  224. };
  225. view.SetContentSize (new Size (10, 10));
  226. view.Border!.Thickness = new (1);
  227. view.BeginInit ();
  228. view.EndInit ();
  229. Assert.Equal (view.Frame, view.GetClip ()!.GetBounds ());
  230. // Act
  231. view.AddViewportToClip ();
  232. // Assert
  233. Assert.Equal (expectedClip, view.GetClip ()!.GetBounds ());
  234. view.Dispose ();
  235. }
  236. [Fact]
  237. [SetupFakeApplication]
  238. public void SetClip_Default_ClipsToViewport ()
  239. {
  240. // Screen is 25x25
  241. Application.Driver!.SetScreenSize (25, 25);
  242. // View is 25x25
  243. // Viewport is (0, 0, 23, 23)
  244. // ContentSize is (10, 10)
  245. // ViewportToScreen is (1, 1, 23, 23)
  246. // Visible content is (1, 1, 10, 10)
  247. // Expected clip is (1, 1, 23, 23) - same as Viewport
  248. Rectangle expectedClip = new (1, 1, 23, 23);
  249. // Arrange
  250. var view = new View
  251. {
  252. Width = Dim.Fill (),
  253. Height = Dim.Fill (),
  254. App = ApplicationImpl.Instance
  255. };
  256. view.SetContentSize (new Size (10, 10));
  257. view.Border!.Thickness = new (1);
  258. view.BeginInit ();
  259. view.EndInit ();
  260. Assert.Equal (view.Frame, view.GetClip ()!.GetBounds ());
  261. view.Viewport = view.Viewport with { X = 1, Y = 1 };
  262. // Act
  263. view.AddViewportToClip ();
  264. // Assert
  265. Assert.Equal (expectedClip, view.GetClip ()!.GetBounds ());
  266. view.Dispose ();
  267. }
  268. }