ClipTests.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #nullable enable
  2. using System.Text;
  3. using UnitTests;
  4. using Xunit.Abstractions;
  5. namespace Terminal.Gui.ViewTests;
  6. [Trait ("Category", "Output")]
  7. public class ClipTests (ITestOutputHelper _output)
  8. {
  9. [Fact]
  10. [SetupFakeDriver]
  11. public void Move_Is_Not_Constrained_To_Viewport ()
  12. {
  13. var view = new View
  14. {
  15. X = 1,
  16. Y = 1,
  17. Width = 3, Height = 3
  18. };
  19. view.Margin!.Thickness = new (1);
  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 (1, 1), new Point (Application.Driver!.Col, Application.Driver!.Row));
  24. view.Move (1, 1);
  25. Assert.Equal (new (3, 3), 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. DriverAssert.AssertDriverContentsWithFrameAre (
  73. @"
  74. ┌─┐
  75. │X│
  76. └─┘",
  77. _output);
  78. Rectangle toFill = new (x, y, width, height);
  79. View.SetClipToScreen ();
  80. view.FillRect (toFill);
  81. DriverAssert.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. DriverAssert.AssertDriverContentsWithFrameAre (
  91. @"
  92. ┌─┐
  93. │X│
  94. └─┘",
  95. _output);
  96. toFill = new (-width, -height, width, height);
  97. view.FillRect (toFill);
  98. DriverAssert.AssertDriverContentsWithFrameAre (
  99. @"
  100. ┌─┐
  101. │X│
  102. └─┘",
  103. _output);
  104. // Now try to clear beyond Viewport (valid)
  105. superView.SetNeedsDraw ();
  106. superView.Draw ();
  107. DriverAssert.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. DriverAssert.AssertDriverContentsWithFrameAre (
  117. @"
  118. ┌─┐
  119. │ │
  120. └─┘",
  121. _output);
  122. // Now clear too much size
  123. superView.SetNeedsDraw ();
  124. superView.Draw ();
  125. DriverAssert.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. DriverAssert.AssertDriverContentsWithFrameAre (
  135. @"
  136. ┌─┐
  137. │ │
  138. └─┘",
  139. _output);
  140. }
  141. // TODO: Simplify this test to just use AddRune directly
  142. [Fact]
  143. [SetupFakeDriver]
  144. [Trait ("Category", "Unicode")]
  145. public void Clipping_Wide_Runes ()
  146. {
  147. ((FakeDriver)Application.Driver!).SetBufferSize (30, 1);
  148. var top = new View
  149. {
  150. Id = "top",
  151. Width = Dim.Fill (),
  152. Height = Dim.Fill ()
  153. };
  154. var frameView = new View
  155. {
  156. Id = "frameView",
  157. Width = Dim.Fill (),
  158. Height = Dim.Fill (),
  159. Text = """
  160. これは広いルーンラインです。
  161. """
  162. };
  163. frameView.Border!.LineStyle = LineStyle.Single;
  164. frameView.Border.Thickness = new (1, 0, 0, 0);
  165. top.Add (frameView);
  166. View.SetClipToScreen ();
  167. top.Layout ();
  168. top.Draw ();
  169. var expectedOutput = """
  170. │これは広いルーンラインです。
  171. """;
  172. DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  173. var view = new View
  174. {
  175. Text = "0123456789",
  176. //Text = "ワイドルー。",
  177. X = 2,
  178. Height = Dim.Auto (),
  179. Width = Dim.Auto (),
  180. BorderStyle = LineStyle.Single
  181. };
  182. view.Border!.Thickness = new (1, 0, 1, 0);
  183. top.Add (view);
  184. top.Layout ();
  185. View.SetClipToScreen ();
  186. top.Draw ();
  187. // 012345678901234567890123456789012345678
  188. // 012 34 56 78 90 12 34 56 78 90 12 34 56 78
  189. // │こ れ は 広 い ル ー ン ラ イ ン で す 。
  190. // 01 2345678901234 56 78 90 12 34 56
  191. // │� |0123456989│� ン ラ イ ン で す 。
  192. expectedOutput = """
  193. │�│0123456789│�ンラインです。
  194. """;
  195. DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  196. }
  197. // TODO: Add more AddRune tests to cover all the cases where wide runes are clipped
  198. [Fact]
  199. [SetupFakeDriver]
  200. public void SetClip_ClipVisibleContentOnly_VisibleContentIsClipped ()
  201. {
  202. // Screen is 25x25
  203. // View is 25x25
  204. // Viewport is (0, 0, 23, 23)
  205. // ContentSize is (10, 10)
  206. // ViewportToScreen is (1, 1, 23, 23)
  207. // Visible content is (1, 1, 10, 10)
  208. // Expected clip is (1, 1, 10, 10) - same as visible content
  209. Rectangle expectedClip = new (1, 1, 10, 10);
  210. // Arrange
  211. var view = new View
  212. {
  213. Width = Dim.Fill (),
  214. Height = Dim.Fill (),
  215. ViewportSettings = ViewportSettings.ClipContentOnly
  216. };
  217. view.SetContentSize (new Size (10, 10));
  218. view.Border!.Thickness = new (1);
  219. view.BeginInit ();
  220. view.EndInit ();
  221. Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
  222. // Act
  223. view.AddViewportToClip ();
  224. // Assert
  225. Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
  226. view.Dispose ();
  227. }
  228. [Fact]
  229. [SetupFakeDriver]
  230. public void SetClip_Default_ClipsToViewport ()
  231. {
  232. // Screen is 25x25
  233. // View is 25x25
  234. // Viewport is (0, 0, 23, 23)
  235. // ContentSize is (10, 10)
  236. // ViewportToScreen is (1, 1, 23, 23)
  237. // Visible content is (1, 1, 10, 10)
  238. // Expected clip is (1, 1, 23, 23) - same as Viewport
  239. Rectangle expectedClip = new (1, 1, 23, 23);
  240. // Arrange
  241. var view = new View
  242. {
  243. Width = Dim.Fill (),
  244. Height = Dim.Fill ()
  245. };
  246. view.SetContentSize (new Size (10, 10));
  247. view.Border!.Thickness = new (1);
  248. view.BeginInit ();
  249. view.EndInit ();
  250. Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
  251. view.Viewport = view.Viewport with { X = 1, Y = 1 };
  252. // Act
  253. view.AddViewportToClip ();
  254. // Assert
  255. Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
  256. view.Dispose ();
  257. }
  258. }