AdornmentTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. using Xunit.Abstractions;
  2. namespace Terminal.Gui.ViewTests;
  3. public class AdornmentTests (ITestOutputHelper output)
  4. {
  5. private readonly ITestOutputHelper _output = output;
  6. [Fact]
  7. public void Viewport_Location_Always_Empty_Size_Correct ()
  8. {
  9. var view = new View
  10. {
  11. X = 1,
  12. Y = 2,
  13. Width = 20,
  14. Height = 20
  15. };
  16. view.BeginInit ();
  17. view.EndInit ();
  18. Assert.Equal (new (1, 2, 20, 20), view.Frame);
  19. Assert.Equal (new (0, 0, 20, 20), view.Viewport);
  20. var marginThickness = 1;
  21. view.Margin.Thickness = new (marginThickness);
  22. Assert.Equal (new (0, 0, 18, 18), view.Viewport);
  23. var borderThickness = 2;
  24. view.Border.Thickness = new (borderThickness);
  25. Assert.Equal (new (0, 0, 14, 14), view.Viewport);
  26. var paddingThickness = 3;
  27. view.Padding.Thickness = new Thickness (paddingThickness);
  28. Assert.Equal (new (0, 0, 8, 8), view.Viewport);
  29. Assert.Equal (new (0, 0, view.Margin.Frame.Width, view.Margin.Frame.Height), view.Margin.Viewport);
  30. Assert.Equal (new (0, 0, view.Border.Frame.Width, view.Border.Frame.Height), view.Border.Viewport);
  31. Assert.Equal (new (0, 0, view.Padding.Frame.Width, view.Padding.Frame.Height), view.Padding.Viewport);
  32. }
  33. // Test that Adornment.Viewport_get override returns Frame.Size minus Thickness
  34. [Theory]
  35. [InlineData (0, 0, 0, 0, 0)]
  36. [InlineData (0, 0, 0, 1, 1)]
  37. [InlineData (0, 0, 0, 1, 0)]
  38. [InlineData (0, 0, 0, 0, 1)]
  39. [InlineData (1, 0, 0, 0, 0)]
  40. [InlineData (1, 0, 0, 1, 1)]
  41. [InlineData (1, 0, 0, 1, 0)]
  42. [InlineData (1, 0, 0, 0, 1)]
  43. [InlineData (1, 0, 0, 4, 4)]
  44. [InlineData (1, 0, 0, 4, 0)]
  45. [InlineData (1, 0, 0, 0, 4)]
  46. [InlineData (0, 1, 0, 0, 0)]
  47. [InlineData (0, 1, 0, 1, 1)]
  48. [InlineData (0, 1, 0, 1, 0)]
  49. [InlineData (0, 1, 0, 0, 1)]
  50. [InlineData (1, 1, 0, 0, 0)]
  51. [InlineData (1, 1, 0, 1, 1)]
  52. [InlineData (1, 1, 0, 1, 0)]
  53. [InlineData (1, 1, 0, 0, 1)]
  54. [InlineData (1, 1, 0, 4, 4)]
  55. [InlineData (1, 1, 0, 4, 0)]
  56. [InlineData (1, 1, 0, 0, 4)]
  57. [InlineData (0, 1, 1, 0, 0)]
  58. [InlineData (0, 1, 1, 1, 1)]
  59. [InlineData (0, 1, 1, 1, 0)]
  60. [InlineData (0, 1, 1, 0, 1)]
  61. [InlineData (1, 1, 1, 0, 0)]
  62. [InlineData (1, 1, 1, 1, 1)]
  63. [InlineData (1, 1, 1, 1, 0)]
  64. [InlineData (1, 1, 1, 0, 1)]
  65. [InlineData (1, 1, 1, 4, 4)]
  66. [InlineData (1, 1, 1, 4, 0)]
  67. [InlineData (1, 1, 1, 0, 4)]
  68. public void Viewport_Width_Is_Frame_Width (int thickness, int x, int y, int w, int h)
  69. {
  70. var adornment = new Adornment (null);
  71. adornment.Thickness = new Thickness (thickness);
  72. adornment.Frame = new Rectangle (x, y, w, h);
  73. Assert.Equal (new Rectangle (x, y, w, h), adornment.Frame);
  74. var expectedBounds = new Rectangle (0, 0, w, h);
  75. Assert.Equal (expectedBounds, adornment.Viewport);
  76. }
  77. // Test that Adornment.Viewport_get override uses Parent not SuperView
  78. [Fact]
  79. public void BoundsToScreen_Uses_Parent_Not_SuperView ()
  80. {
  81. var parent = new View { X = 1, Y = 2, Width = 10, Height = 10 };
  82. parent.BeginInit ();
  83. parent.EndInit ();
  84. Assert.Equal (new Rectangle (1, 2, 10, 10), parent.Frame);
  85. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Viewport);
  86. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Margin.Frame);
  87. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Margin.Viewport);
  88. Assert.Null (parent.Margin.SuperView);
  89. Rectangle boundsAsScreen = parent.Margin.ViewportToScreen (new Rectangle (1, 2, 5, 5));
  90. Assert.Equal (new Rectangle (2, 4, 5, 5), boundsAsScreen);
  91. }
  92. [Fact]
  93. public void SetAdornmentFrames_Sets_Frames_Correctly ()
  94. {
  95. var parent = new View { X = 1, Y = 2, Width = 10, Height = 20 };
  96. parent.SetAdornmentFrames();
  97. Assert.Equal (new Rectangle (1, 2, 10, 20), parent.Frame);
  98. Assert.Equal (new Rectangle (0, 0, 10, 20), parent.Viewport);
  99. Assert.Equal (new Rectangle (0, 0, 10, 20), parent.Margin.Frame);
  100. Assert.Equal (new Rectangle (0, 0, 10, 20), parent.Margin.Viewport);
  101. }
  102. [Fact]
  103. public void Frames_are_Parent_SuperView_Relative ()
  104. {
  105. var view = new View
  106. {
  107. X = 1,
  108. Y = 2,
  109. Width = 20,
  110. Height = 31
  111. };
  112. var marginThickness = 1;
  113. view.Margin.Thickness = new Thickness (marginThickness);
  114. var borderThickness = 2;
  115. view.Border.Thickness = new Thickness (borderThickness);
  116. var paddingThickness = 3;
  117. view.Padding.Thickness = new Thickness (paddingThickness);
  118. view.BeginInit ();
  119. view.EndInit ();
  120. Assert.Equal (new Rectangle (1, 2, 20, 31), view.Frame);
  121. Assert.Equal (new Rectangle (0, 0, 8, 19), view.Viewport);
  122. // Margin.Frame is always the same as the view frame
  123. Assert.Equal (new Rectangle (0, 0, 20, 31), view.Margin.Frame);
  124. // Border.Frame is View.Frame minus the Margin thickness
  125. Assert.Equal (
  126. new Rectangle (marginThickness, marginThickness, view.Frame.Width - marginThickness * 2, view.Frame.Height - marginThickness * 2),
  127. view.Border.Frame);
  128. // Padding.Frame is View.Frame minus the Border thickness plus Margin thickness
  129. Assert.Equal (
  130. new Rectangle (
  131. marginThickness + borderThickness,
  132. marginThickness + borderThickness,
  133. view.Frame.Width - (marginThickness + borderThickness) * 2,
  134. view.Frame.Height - (marginThickness + borderThickness) * 2),
  135. view.Padding.Frame);
  136. }
  137. // Test that Adornment.FrameToScreen override retains Frame.Size
  138. [Theory]
  139. [InlineData (0, 0, 0)]
  140. [InlineData (0, 1, 1)]
  141. [InlineData (0, 10, 10)]
  142. [InlineData (1, 0, 0)]
  143. [InlineData (1, 1, 1)]
  144. [InlineData (1, 10, 10)]
  145. public void FrameToScreen_Retains_Frame_Size (int marginThickness, int w, int h)
  146. {
  147. var parent = new View { X = 1, Y = 2, Width = w, Height = h };
  148. parent.Margin.Thickness = new Thickness (marginThickness);
  149. parent.BeginInit ();
  150. parent.EndInit ();
  151. Assert.Equal (new Rectangle (1, 2, w, h), parent.Frame);
  152. Assert.Equal (new Rectangle (0, 0, w, h), parent.Margin.Frame);
  153. Assert.Equal (parent.Frame, parent.Margin.FrameToScreen ());
  154. }
  155. // Test that Adornment.FrameToScreen override returns Frame if Parent is null
  156. [Fact]
  157. public void FrameToScreen_Returns_Frame_If_Parent_Is_Null ()
  158. {
  159. var a = new Adornment
  160. {
  161. X = 1,
  162. Y = 2,
  163. Width = 3,
  164. Height = 4
  165. };
  166. Assert.Null (a.Parent);
  167. Assert.Equal (a.Frame, a.FrameToScreen ());
  168. }
  169. // Test that Adornment.FrameToScreen override returns correct location
  170. [Theory]
  171. [InlineData (0, 0, 0, 0)]
  172. [InlineData (0, 0, 1, 1)]
  173. [InlineData (0, 0, 10, 10)]
  174. [InlineData (1, 0, 0, 0)]
  175. [InlineData (1, 0, 1, 1)]
  176. [InlineData (1, 0, 10, 10)]
  177. [InlineData (0, 1, 0, 0)]
  178. [InlineData (0, 1, 1, 1)]
  179. [InlineData (0, 1, 10, 10)]
  180. [InlineData (1, 1, 0, 0)]
  181. [InlineData (1, 1, 1, 1)]
  182. [InlineData (1, 1, 10, 10)]
  183. public void FrameToScreen_Returns_Screen_Location (int marginThickness, int borderThickness, int x, int y)
  184. {
  185. var superView = new View
  186. {
  187. X = 1,
  188. Y = 1,
  189. Width = 20,
  190. Height = 20
  191. };
  192. superView.Margin.Thickness = new Thickness (marginThickness);
  193. superView.Border.Thickness = new Thickness (borderThickness);
  194. var view = new View { X = x, Y = y, Width = 1, Height = 1 };
  195. superView.Add (view);
  196. superView.BeginInit ();
  197. superView.EndInit ();
  198. Assert.Equal (new Rectangle (x, y, 1, 1), view.Frame);
  199. Assert.Equal (new Rectangle (0, 0, 20, 20), superView.Margin.Frame);
  200. Assert.Equal (
  201. new Rectangle (marginThickness, marginThickness, 20 - marginThickness * 2, 20 - marginThickness * 2),
  202. superView.Border.Frame
  203. );
  204. Assert.Equal (
  205. new Rectangle (superView.Frame.X + marginThickness, superView.Frame.Y + marginThickness, 20 - marginThickness * 2, 20 - marginThickness * 2),
  206. superView.Border.FrameToScreen ()
  207. );
  208. }
  209. // Test that Adornment.FrameToScreen override uses Parent not SuperView
  210. [Fact]
  211. public void FrameToScreen_Uses_Parent_Not_SuperView ()
  212. {
  213. var parent = new View { X = 1, Y = 2, Width = 10, Height = 10 };
  214. parent.BeginInit ();
  215. parent.EndInit ();
  216. Assert.Equal (new Rectangle (1, 2, 10, 10), parent.Frame);
  217. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Viewport);
  218. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Margin.Frame);
  219. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Margin.Viewport);
  220. Assert.Null (parent.Margin.SuperView);
  221. Assert.Equal (new Rectangle (1, 2, 10, 10), parent.Margin.FrameToScreen ());
  222. }
  223. [Fact]
  224. public void GetAdornmentsThickness ()
  225. {
  226. var view = new View ();
  227. Assert.Equal (Thickness.Empty, view.GetAdornmentsThickness ());
  228. view.Margin.Thickness = new Thickness (1);
  229. Assert.Equal (new Thickness (1), view.GetAdornmentsThickness ());
  230. view.Border.Thickness = new Thickness (1);
  231. Assert.Equal (new Thickness (2), view.GetAdornmentsThickness ());
  232. view.Padding.Thickness = new Thickness (1);
  233. Assert.Equal (new Thickness (3), view.GetAdornmentsThickness ());
  234. view.Padding.Thickness = new Thickness (2);
  235. Assert.Equal (new Thickness (4), view.GetAdornmentsThickness ());
  236. view.Padding.Thickness = new Thickness (1, 2, 3, 4);
  237. Assert.Equal (new Thickness (3, 4, 5, 6), view.GetAdornmentsThickness ());
  238. view.Margin.Thickness = new Thickness (1, 2, 3, 4);
  239. Assert.Equal (new Thickness (3, 5, 7, 9), view.GetAdornmentsThickness ());
  240. view.Dispose ();
  241. }
  242. [Fact]
  243. public void Setting_Viewport_Throws ()
  244. {
  245. var adornment = new Adornment (null);
  246. Assert.Throws<InvalidOperationException> (() => adornment.Viewport = new Rectangle (1, 2, 3, 4));
  247. }
  248. [Fact]
  249. public void Setting_SuperView_Throws ()
  250. {
  251. var adornment = new Adornment (null);
  252. Assert.Throws<InvalidOperationException> (() => adornment.SuperView = new View ());
  253. }
  254. [Fact]
  255. public void Setting_SuperViewRendersLineCanvas_Throws ()
  256. {
  257. var adornment = new Adornment (null);
  258. Assert.Throws<InvalidOperationException> (() => adornment.SuperViewRendersLineCanvas = true);
  259. }
  260. [Fact]
  261. public void Setting_Thickness_Changes_Parent_Bounds ()
  262. {
  263. var parent = new View { Width = 10, Height = 10 };
  264. parent.BeginInit ();
  265. parent.EndInit ();
  266. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Frame);
  267. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Viewport);
  268. parent.Margin.Thickness = new Thickness (1);
  269. Assert.Equal (new Rectangle (0, 0, 10, 10), parent.Frame);
  270. Assert.Equal (new Rectangle (0, 0, 8, 8), parent.Viewport);
  271. }
  272. [Fact]
  273. public void Setting_Thickness_Raises_ThicknessChanged ()
  274. {
  275. var adornment = new Adornment (null);
  276. var super = new View ();
  277. var raised = false;
  278. adornment.ThicknessChanged += (s, e) =>
  279. {
  280. raised = true;
  281. Assert.Equal (new Thickness (1, 2, 3, 4), adornment.Thickness);
  282. };
  283. adornment.Thickness = new Thickness (1, 2, 3, 4);
  284. Assert.True (raised);
  285. }
  286. [Fact]
  287. public void Setting_Thickness_Causes_Parent_Layout ()
  288. {
  289. var parent = new View ();
  290. var raised = false;
  291. parent.BeginInit ();
  292. parent.EndInit ();
  293. parent.SubviewLayout += LayoutStarted;
  294. parent.Margin.Thickness = new Thickness (1, 2, 3, 4);
  295. Assert.True (parent.NeedsLayout);
  296. Assert.True (parent.Margin.NeedsLayout);
  297. parent.Layout ();
  298. Assert.True (raised);
  299. return;
  300. void LayoutStarted (object sender, LayoutEventArgs e)
  301. {
  302. raised = true;
  303. }
  304. }
  305. [Fact]
  306. public void Setting_Thickness_Causes_Adornment_Layout ()
  307. {
  308. var parent = new View ();
  309. var raised = false;
  310. parent.BeginInit ();
  311. parent.EndInit ();
  312. parent.Margin.SubviewLayout += LayoutStarted;
  313. parent.Margin.Thickness = new Thickness (1, 2, 3, 4);
  314. Assert.True (parent.NeedsLayout);
  315. Assert.True (parent.Margin.NeedsLayout);
  316. parent.Layout ();
  317. Assert.True (raised);
  318. return;
  319. void LayoutStarted (object sender, LayoutEventArgs e)
  320. {
  321. raised = true;
  322. }
  323. }
  324. [Fact]
  325. public void Set_Viewport_Throws ()
  326. {
  327. View view = new ();
  328. view.BeginInit ();
  329. view.EndInit ();
  330. view.Padding.Thickness = new (2, 2, 2, 2);
  331. Assert.Throws<InvalidOperationException> (() => view.Padding.Viewport = view.Padding.Viewport with { Location = new (1, 1) });
  332. }
  333. // Contains tests
  334. [Theory]
  335. [InlineData (0, 0, 0, 0, 0, 0, false)]
  336. [InlineData (0, 0, 0, 1, 0, 0, false)]
  337. [InlineData (0, 0, 1, 0, 0, 0, false)]
  338. [InlineData (0, 0, 1, 1, 0, 0, true)]
  339. [InlineData (0, 0, 1, 2, 0, 0, true)]
  340. [InlineData (1, 1, 0, 0, 0, 0, false)]
  341. [InlineData (1, 1, 0, 1, 0, 0, false)]
  342. [InlineData (1, 1, 1, 0, 0, 0, false)]
  343. [InlineData (1, 1, 1, 0, 0, 1, false)]
  344. [InlineData (1, 1, 1, 1, 1, 0, false)]
  345. [InlineData (1, 1, 1, 1, 0, 0, false)]
  346. [InlineData (1, 1, 1, 1, 1, 1, true)]
  347. [InlineData (1, 1, 1, 2, 1, 1, true)]
  348. public void Contains_Left_Only (int x, int y, int width, int height, int pointX, int pointY, bool expected)
  349. {
  350. Adornment adornment = new () { Id = "adornment" };
  351. adornment.Parent = new View () { Id = "parent" };
  352. adornment.Parent.Frame = new Rectangle (x, y, width, height);
  353. adornment.Thickness = new (1, 0, 0, 0);
  354. adornment.Frame = adornment.Parent.Frame with { Location = Point.Empty };
  355. bool result = adornment.Contains (new (pointX, pointY));
  356. Assert.Equal (expected, result);
  357. }
  358. [Theory]
  359. [InlineData (0, 0, 0, 0, 0, 0, false)]
  360. [InlineData (0, 0, 0, 1, 0, 0, false)]
  361. [InlineData (0, 0, 1, 0, 0, 0, false)]
  362. [InlineData (0, 0, 1, 1, 0, 0, true)]
  363. [InlineData (0, 0, 1, 2, 0, 0, true)]
  364. [InlineData (1, 1, 0, 0, 0, 0, false)]
  365. [InlineData (1, 1, 0, 1, 0, 0, false)]
  366. [InlineData (1, 1, 1, 0, 0, 0, false)]
  367. [InlineData (1, 1, 1, 0, 0, 1, false)]
  368. [InlineData (1, 1, 1, 1, 1, 0, false)]
  369. [InlineData (1, 1, 1, 1, 0, 0, false)]
  370. [InlineData (1, 1, 1, 1, 1, 1, true)]
  371. [InlineData (1, 1, 1, 2, 1, 1, true)]
  372. public void Contains_Right_Only (int x, int y, int width, int height, int pointX, int pointY, bool expected)
  373. {
  374. Adornment adornment = new () { Id = "adornment" };
  375. adornment.Parent = new View () { Id = "parent" };
  376. adornment.Parent.Frame = new Rectangle (x, y, width, height);
  377. adornment.Thickness = new (0, 0, 1, 0);
  378. adornment.Frame = adornment.Parent.Frame with { Location = Point.Empty };
  379. bool result = adornment.Contains (new (pointX, pointY));
  380. Assert.Equal (expected, result);
  381. }
  382. [Theory]
  383. [InlineData (0, 0, 0, 0, 0, 0, false)]
  384. [InlineData (0, 0, 0, 1, 0, 0, false)]
  385. [InlineData (0, 0, 1, 0, 0, 0, false)]
  386. [InlineData (0, 0, 1, 1, 0, 0, true)]
  387. [InlineData (0, 0, 1, 2, 0, 0, true)]
  388. [InlineData (1, 1, 0, 0, 0, 0, false)]
  389. [InlineData (1, 1, 0, 1, 0, 0, false)]
  390. [InlineData (1, 1, 1, 0, 0, 0, false)]
  391. [InlineData (1, 1, 1, 0, 0, 1, false)]
  392. [InlineData (1, 1, 1, 1, 1, 0, false)]
  393. [InlineData (1, 1, 1, 1, 0, 0, false)]
  394. [InlineData (1, 1, 1, 1, 1, 1, true)]
  395. [InlineData (1, 1, 1, 2, 1, 1, true)]
  396. public void Contains_Top_Only (int x, int y, int width, int height, int pointX, int pointY, bool expected)
  397. {
  398. Adornment adornment = new () { Id = "adornment" };
  399. adornment.Parent = new View () { Id = "parent" };
  400. adornment.Parent.Frame = new Rectangle (x, y, width, height);
  401. adornment.Thickness = new (0, 1, 0, 0);
  402. adornment.Frame = adornment.Parent.Frame with { Location = Point.Empty };
  403. bool result = adornment.Contains (new (pointX, pointY));
  404. Assert.Equal (expected, result);
  405. }
  406. [Theory]
  407. [InlineData (0, 0, 0, 0, 0, 0, false)]
  408. [InlineData (0, 0, 0, 1, 0, 0, false)]
  409. [InlineData (0, 0, 1, 0, 0, 0, false)]
  410. [InlineData (0, 0, 1, 1, 0, 0, true)]
  411. [InlineData (0, 0, 1, 2, 0, 0, true)]
  412. [InlineData (1, 1, 0, 0, 0, 0, false)]
  413. [InlineData (1, 1, 0, 1, 0, 0, false)]
  414. [InlineData (1, 1, 1, 0, 0, 0, false)]
  415. [InlineData (1, 1, 1, 0, 0, 1, false)]
  416. [InlineData (1, 1, 1, 1, 1, 0, false)]
  417. [InlineData (1, 1, 1, 1, 0, 0, false)]
  418. [InlineData (1, 1, 1, 1, 1, 1, true)]
  419. [InlineData (1, 1, 1, 2, 1, 1, true)]
  420. public void Contains_TopLeft_Only (int x, int y, int width, int height, int pointX, int pointY, bool expected)
  421. {
  422. Adornment adornment = new () { Id = "adornment" };
  423. adornment.Parent = new View () { Id = "parent" };
  424. adornment.Parent.Frame = new Rectangle (x, y, width, height);
  425. adornment.Thickness = new (1, 1, 0, 0);
  426. adornment.Frame = adornment.Parent.Frame with { Location = Point.Empty };
  427. bool result = adornment.Contains (new (pointX, pointY));
  428. Assert.Equal (expected, result);
  429. }
  430. }