ViewClearViewportTests.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #nullable disable
  2. using System.Text;
  3. using UnitTests;
  4. namespace ViewBaseTests.Viewport;
  5. public class ViewClearViewportTests () : FakeDriverBase
  6. {
  7. [Fact]
  8. public void ClearViewport_FillsViewportArea ()
  9. {
  10. IDriver driver = CreateFakeDriver (80, 25);
  11. driver.Clip = new Region (driver.Screen);
  12. var view = new View
  13. {
  14. X = 1,
  15. Y = 1,
  16. Width = 20,
  17. Height = 20,
  18. Driver = driver
  19. };
  20. view.BeginInit ();
  21. view.EndInit ();
  22. view.LayoutSubViews ();
  23. // Clear the driver contents first
  24. driver.FillRect (driver.Screen, new Rune ('X'));
  25. view.ClearViewport ();
  26. // The viewport area should be filled with spaces
  27. Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) });
  28. for (int y = viewportScreen.Y; y < viewportScreen.Y + viewportScreen.Height; y++)
  29. {
  30. for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++)
  31. {
  32. Assert.Equal (" ", driver.Contents [y, x].Grapheme);
  33. }
  34. }
  35. }
  36. [Fact]
  37. public void ClearViewport_WithClearContentOnly_LimitsToVisibleContent ()
  38. {
  39. IDriver driver = CreateFakeDriver (80, 25);
  40. driver.Clip = new Region (driver.Screen);
  41. var view = new View
  42. {
  43. X = 1,
  44. Y = 1,
  45. Width = 20,
  46. Height = 20,
  47. Driver = driver
  48. };
  49. view.SetContentSize (new Size (100, 100)); // Content larger than viewport
  50. view.ViewportSettings = ViewportSettingsFlags.ClearContentOnly;
  51. view.BeginInit ();
  52. view.EndInit ();
  53. view.LayoutSubViews ();
  54. // Clear the driver contents first
  55. driver.FillRect (driver.Screen, new Rune ('X'));
  56. view.ClearViewport ();
  57. // The visible content area should be cleared
  58. Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ()));
  59. Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) });
  60. Rectangle toClear = Rectangle.Intersect (viewportScreen, visibleContent);
  61. for (int y = toClear.Y; y < toClear.Y + toClear.Height; y++)
  62. {
  63. for (int x = toClear.X; x < toClear.X + toClear.Width; x++)
  64. {
  65. Assert.Equal (" ", driver.Contents [y, x].Grapheme);
  66. }
  67. }
  68. }
  69. [Fact]
  70. public void ClearViewport_NullDriver_DoesNotThrow ()
  71. {
  72. var view = new View
  73. {
  74. X = 1,
  75. Y = 1,
  76. Width = 20,
  77. Height = 20
  78. };
  79. view.BeginInit ();
  80. view.EndInit ();
  81. var exception = Record.Exception (() => view.ClearViewport ());
  82. Assert.Null (exception);
  83. }
  84. [Fact]
  85. public void ClearViewport_SetsNeedsDraw ()
  86. {
  87. IDriver driver = CreateFakeDriver (80, 25);
  88. driver.Clip = new Region (driver.Screen);
  89. var view = new View
  90. {
  91. X = 10,
  92. Y = 10,
  93. Width = 20,
  94. Height = 20,
  95. Driver = driver
  96. };
  97. view.BeginInit ();
  98. view.EndInit ();
  99. view.LayoutSubViews ();
  100. // Clear NeedsDraw first
  101. view.Draw ();
  102. Assert.False (view.NeedsDraw);
  103. view.ClearViewport ();
  104. Assert.True (view.NeedsDraw);
  105. }
  106. [Fact]
  107. public void ClearViewport_WithTransparentFlag_DoesNotClear ()
  108. {
  109. IDriver driver = CreateFakeDriver (80, 25);
  110. driver.Clip = new Region (driver.Screen);
  111. var view = new View
  112. {
  113. X = 1,
  114. Y = 1,
  115. Width = 20,
  116. Height = 20,
  117. Driver = driver,
  118. ViewportSettings = ViewportSettingsFlags.Transparent
  119. };
  120. view.BeginInit ();
  121. view.EndInit ();
  122. view.LayoutSubViews ();
  123. // Fill driver with a character
  124. driver.FillRect (driver.Screen, new Rune ('X'));
  125. view.Draw ();
  126. // The viewport area should still have 'X' (not cleared)
  127. Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) });
  128. for (int y = viewportScreen.Y; y < viewportScreen.Y + viewportScreen.Height; y++)
  129. {
  130. for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++)
  131. {
  132. Assert.Equal ("X", driver.Contents [y, x].Grapheme);
  133. }
  134. }
  135. }
  136. [Fact]
  137. public void ClearingViewport_Event_Raised ()
  138. {
  139. IDriver driver = CreateFakeDriver (80, 25);
  140. driver.Clip = new Region (driver.Screen);
  141. bool eventRaised = false;
  142. Rectangle? receivedRect = null;
  143. var view = new View
  144. {
  145. X = 10,
  146. Y = 10,
  147. Width = 20,
  148. Height = 20,
  149. Driver = driver
  150. };
  151. view.BeginInit ();
  152. view.EndInit ();
  153. view.LayoutSubViews ();
  154. view.ClearingViewport += (s, e) =>
  155. {
  156. eventRaised = true;
  157. receivedRect = e.NewViewport;
  158. };
  159. view.Draw ();
  160. Assert.True (eventRaised);
  161. Assert.NotNull (receivedRect);
  162. Assert.Equal (view.Viewport, receivedRect);
  163. }
  164. [Fact]
  165. public void ClearedViewport_Event_Raised ()
  166. {
  167. IDriver driver = CreateFakeDriver (80, 25);
  168. driver.Clip = new Region (driver.Screen);
  169. bool eventRaised = false;
  170. var view = new View
  171. {
  172. X = 10,
  173. Y = 10,
  174. Width = 20,
  175. Height = 20,
  176. Driver = driver
  177. };
  178. view.BeginInit ();
  179. view.EndInit ();
  180. view.LayoutSubViews ();
  181. view.ClearedViewport += (s, e) => eventRaised = true;
  182. view.Draw ();
  183. Assert.True (eventRaised);
  184. }
  185. [Fact]
  186. public void OnClearingViewport_CanPreventClear ()
  187. {
  188. IDriver driver = CreateFakeDriver (80, 25);
  189. driver.Clip = new Region (driver.Screen);
  190. bool clearedCalled = false;
  191. var view = new TestView
  192. {
  193. X = 10,
  194. Y = 10,
  195. Width = 20,
  196. Height = 20,
  197. Driver = driver,
  198. PreventClear = true
  199. };
  200. view.BeginInit ();
  201. view.EndInit ();
  202. view.LayoutSubViews ();
  203. view.ClearedViewport += (s, e) => clearedCalled = true;
  204. view.Draw ();
  205. Assert.False (clearedCalled);
  206. }
  207. [Fact]
  208. public void ClearViewport_EmptyViewport_DoesNotThrow ()
  209. {
  210. IDriver driver = CreateFakeDriver (80, 25);
  211. driver.Clip = new Region (driver.Screen);
  212. var view = new View
  213. {
  214. X = 10,
  215. Y = 10,
  216. Width = 1,
  217. Height = 1,
  218. Driver = driver
  219. };
  220. view.Border!.Thickness = new Thickness (1);
  221. view.BeginInit ();
  222. view.EndInit ();
  223. view.LayoutSubViews ();
  224. // With border of 1, viewport should be empty
  225. Assert.True (view.Viewport.Width == 0 || view.Viewport.Height == 0);
  226. var exception = Record.Exception (() => view.ClearViewport ());
  227. Assert.Null (exception);
  228. }
  229. [Fact]
  230. public void ClearViewport_WithScrolledViewport_ClearsCorrectArea ()
  231. {
  232. IDriver driver = CreateFakeDriver (80, 25);
  233. driver.Clip = new Region (driver.Screen);
  234. var view = new View
  235. {
  236. X = 1,
  237. Y = 1,
  238. Width = 20,
  239. Height = 20,
  240. Driver = driver
  241. };
  242. view.SetContentSize (new Size (100, 100));
  243. view.BeginInit ();
  244. view.EndInit ();
  245. view.LayoutSubViews ();
  246. // Scroll the viewport
  247. view.Viewport = view.Viewport with { X = 10, Y = 10 };
  248. // Fill driver with a character
  249. driver.FillRect (driver.Screen, new Rune ('X'));
  250. view.ClearViewport ();
  251. // The viewport area should be cleared (not the scrolled content area)
  252. Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) });
  253. for (int y = viewportScreen.Y; y < viewportScreen.Y + viewportScreen.Height; y++)
  254. {
  255. for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++)
  256. {
  257. Assert.Equal (" ", driver.Contents [y, x].Grapheme);
  258. }
  259. }
  260. }
  261. [Fact]
  262. public void ClearViewport_WithClearContentOnly_AndScrolledViewport_ClearsOnlyVisibleContent ()
  263. {
  264. IDriver driver = CreateFakeDriver (80, 25);
  265. driver.Clip = new Region (driver.Screen);
  266. var view = new View
  267. {
  268. X = 10,
  269. Y = 10,
  270. Width = 20,
  271. Height = 20,
  272. Driver = driver
  273. };
  274. view.SetContentSize (new Size (15, 15)); // Content smaller than viewport
  275. view.ViewportSettings = ViewportSettingsFlags.ClearContentOnly;
  276. view.BeginInit ();
  277. view.EndInit ();
  278. view.LayoutSubViews ();
  279. // Scroll past the content
  280. view.Viewport = view.Viewport with { X = 5, Y = 5 };
  281. // Fill driver with a character
  282. driver.FillRect (driver.Screen, new Rune ('X'));
  283. view.ClearViewport ();
  284. // Only the visible part of the content should be cleared
  285. Rectangle visibleContent = view.ViewportToScreen (new Rectangle (new (-view.Viewport.X, -view.Viewport.Y), view.GetContentSize ()));
  286. Rectangle viewportScreen = view.ViewportToScreen (view.Viewport with { Location = new (0, 0) });
  287. Rectangle toClear = Rectangle.Intersect (viewportScreen, visibleContent);
  288. if (toClear != Rectangle.Empty)
  289. {
  290. for (int y = toClear.Y; y < toClear.Y + toClear.Height; y++)
  291. {
  292. for (int x = toClear.X; x < toClear.X + toClear.Width; x++)
  293. {
  294. Assert.Equal (" ", driver.Contents[y, x].Grapheme);
  295. }
  296. }
  297. }
  298. }
  299. private class TestView : View
  300. {
  301. public bool PreventClear { get; set; }
  302. protected override bool OnClearingViewport ()
  303. {
  304. return PreventClear || base.OnClearingViewport ();
  305. }
  306. }
  307. }