GetViewsUnderLocationTests.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. #nullable enable
  2. namespace UnitTests.ViewMouseTests;
  3. [Trait ("Category", "Input")]
  4. public class GetViewsUnderLocationTests
  5. {
  6. [Theory]
  7. [InlineData (0, 0, 0, 0, 0, -1, -1, new string [] { })]
  8. [InlineData (0, 0, 0, 0, 0, 0, 0, new [] { "Top" })]
  9. [InlineData (0, 0, 0, 0, 0, 1, 1, new [] { "Top" })]
  10. [InlineData (0, 0, 0, 0, 0, 4, 4, new [] { "Top" })]
  11. [InlineData (0, 0, 0, 0, 0, 9, 9, new [] { "Top" })]
  12. [InlineData (0, 0, 0, 0, 0, 10, 10, new string [] { })]
  13. [InlineData (1, 1, 0, 0, 0, -1, -1, new string [] { })]
  14. [InlineData (1, 1, 0, 0, 0, 0, 0, new string [] { })]
  15. [InlineData (1, 1, 0, 0, 0, 1, 1, new [] { "Top" })]
  16. [InlineData (1, 1, 0, 0, 0, 4, 4, new [] { "Top" })]
  17. [InlineData (1, 1, 0, 0, 0, 9, 9, new [] { "Top" })]
  18. [InlineData (1, 1, 0, 0, 0, 10, 10, new [] { "Top" })]
  19. [InlineData (0, 0, 1, 0, 0, -1, -1, new string [] { })]
  20. [InlineData (0, 0, 1, 0, 0, 0, 0, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  21. [InlineData (0, 0, 1, 0, 0, 1, 1, new [] { "Top" })]
  22. [InlineData (0, 0, 1, 0, 0, 4, 4, new [] { "Top" })]
  23. [InlineData (0, 0, 1, 0, 0, 9, 9, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  24. [InlineData (0, 0, 1, 0, 0, 10, 10, new string [] { })]
  25. [InlineData (0, 0, 1, 1, 0, -1, -1, new string [] { })]
  26. [InlineData (0, 0, 1, 1, 0, 0, 0, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  27. [InlineData (0, 0, 1, 1, 0, 1, 1, new [] { "Top", "Border" })]
  28. [InlineData (0, 0, 1, 1, 0, 4, 4, new [] { "Top" })]
  29. [InlineData (0, 0, 1, 1, 0, 9, 9, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  30. [InlineData (0, 0, 1, 1, 0, 10, 10, new string [] { })]
  31. [InlineData (0, 0, 1, 1, 1, -1, -1, new string [] { })]
  32. [InlineData (0, 0, 1, 1, 1, 0, 0, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  33. [InlineData (0, 0, 1, 1, 1, 1, 1, new [] { "Top", "Border" })]
  34. [InlineData (0, 0, 1, 1, 1, 2, 2, new [] { "Top", "Padding" })]
  35. [InlineData (0, 0, 1, 1, 1, 4, 4, new [] { "Top" })]
  36. [InlineData (0, 0, 1, 1, 1, 9, 9, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  37. [InlineData (0, 0, 1, 1, 1, 10, 10, new string [] { })]
  38. [InlineData (1, 1, 1, 0, 0, -1, -1, new string [] { })]
  39. [InlineData (1, 1, 1, 0, 0, 0, 0, new string [] { })]
  40. [InlineData (1, 1, 1, 0, 0, 1, 1, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  41. [InlineData (1, 1, 1, 0, 0, 4, 4, new [] { "Top" })]
  42. [InlineData (1, 1, 1, 0, 0, 9, 9, new [] { "Top" })]
  43. [InlineData (1, 1, 1, 0, 0, 10, 10, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  44. [InlineData (1, 1, 1, 1, 0, -1, -1, new string [] { })]
  45. [InlineData (1, 1, 1, 1, 0, 0, 0, new string [] { })]
  46. [InlineData (1, 1, 1, 1, 0, 1, 1, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  47. [InlineData (1, 1, 1, 1, 0, 4, 4, new [] { "Top" })]
  48. [InlineData (1, 1, 1, 1, 0, 9, 9, new [] { "Top", "Border" })]
  49. [InlineData (1, 1, 1, 1, 0, 10, 10, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  50. [InlineData (1, 1, 1, 1, 1, -1, -1, new string [] { })]
  51. [InlineData (1, 1, 1, 1, 1, 0, 0, new string [] { })]
  52. [InlineData (1, 1, 1, 1, 1, 1, 1, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  53. [InlineData (1, 1, 1, 1, 1, 2, 2, new [] { "Top", "Border" })]
  54. [InlineData (1, 1, 1, 1, 1, 3, 3, new [] { "Top", "Padding" })]
  55. [InlineData (1, 1, 1, 1, 1, 4, 4, new [] { "Top" })]
  56. [InlineData (1, 1, 1, 1, 1, 8, 8, new [] { "Top", "Padding" })]
  57. [InlineData (1, 1, 1, 1, 1, 9, 9, new [] { "Top", "Border" })]
  58. [InlineData (1, 1, 1, 1, 1, 10, 10, new string [] { })] //margin is ViewportSettings.TransparentToMouse
  59. public void Top_Adornments_Returns_Correct_View (
  60. int frameX,
  61. int frameY,
  62. int marginThickness,
  63. int borderThickness,
  64. int paddingThickness,
  65. int testX,
  66. int testY,
  67. string [] expectedViewsFound
  68. )
  69. {
  70. // Arrange
  71. Application.TopRunnable = new ()
  72. {
  73. Id = "Top",
  74. Frame = new (frameX, frameY, 10, 10)
  75. };
  76. Application.TopRunnable.Margin!.Thickness = new (marginThickness);
  77. Application.TopRunnable.Margin!.Id = "Margin";
  78. Application.TopRunnable.Border!.Thickness = new (borderThickness);
  79. Application.TopRunnable.Border!.Id = "Border";
  80. Application.TopRunnable.Padding!.Thickness = new (paddingThickness);
  81. Application.TopRunnable.Padding.Id = "Padding";
  82. var location = new Point (testX, testY);
  83. // Act
  84. List<View?> viewsUnderMouse = Application.TopRunnable.GetViewsUnderLocation (location, ViewportSettingsFlags.TransparentMouse);
  85. // Assert
  86. if (expectedViewsFound.Length == 0)
  87. {
  88. Assert.Empty (viewsUnderMouse);
  89. }
  90. else
  91. {
  92. string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray ();
  93. Assert.Equal (expectedViewsFound, foundIds);
  94. }
  95. Application.TopRunnable.Dispose ();
  96. Application.ResetState (true);
  97. }
  98. [Theory]
  99. [InlineData (0, 0)]
  100. [InlineData (1, 1)]
  101. [InlineData (2, 2)]
  102. public void Returns_Top_If_No_SubViews (int testX, int testY)
  103. {
  104. // Arrange
  105. Application.TopRunnable = new ()
  106. {
  107. Frame = new (0, 0, 10, 10)
  108. };
  109. var location = new Point (testX, testY);
  110. // Act
  111. List<View?> viewsUnderMouse = Application.TopRunnable.GetViewsUnderLocation (location, ViewportSettingsFlags.TransparentMouse);
  112. // Assert
  113. Assert.Contains (viewsUnderMouse, v => v == Application.TopRunnable);
  114. Application.TopRunnable.Dispose ();
  115. Application.ResetState (true);
  116. }
  117. // Test that GetViewsUnderLocation returns the correct view if the start view has no subviews
  118. [Theory]
  119. [InlineData (0, 0)]
  120. [InlineData (1, 1)]
  121. [InlineData (2, 2)]
  122. public void Returns_Start_If_No_SubViews (int testX, int testY)
  123. {
  124. Application.ResetState (true);
  125. Application.TopRunnable = new ()
  126. {
  127. Width = 10, Height = 10
  128. };
  129. Assert.Same (Application.TopRunnable, Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ());
  130. Application.TopRunnable.Dispose ();
  131. Application.ResetState (true);
  132. }
  133. // Test that GetViewsUnderLocation returns the correct view if the start view has subviews
  134. [Theory]
  135. [InlineData (0, 0, false)]
  136. [InlineData (1, 1, false)]
  137. [InlineData (9, 9, false)]
  138. [InlineData (10, 10, false)]
  139. [InlineData (6, 7, false)]
  140. [InlineData (1, 2, true)]
  141. [InlineData (5, 6, true)]
  142. public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubViewFound)
  143. {
  144. Application.TopRunnable = new ()
  145. {
  146. Width = 10, Height = 10
  147. };
  148. var subview = new View
  149. {
  150. X = 1, Y = 2,
  151. Width = 5, Height = 5
  152. };
  153. Application.TopRunnable.Add (subview);
  154. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  155. Assert.Equal (expectedSubViewFound, found == subview);
  156. Application.TopRunnable.Dispose ();
  157. Application.ResetState (true);
  158. }
  159. [Theory]
  160. [InlineData (0, 0, false)]
  161. [InlineData (1, 1, false)]
  162. [InlineData (9, 9, false)]
  163. [InlineData (10, 10, false)]
  164. [InlineData (6, 7, false)]
  165. [InlineData (1, 2, false)]
  166. [InlineData (5, 6, false)]
  167. public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expectedSubViewFound)
  168. {
  169. Application.TopRunnable = new ()
  170. {
  171. Width = 10, Height = 10
  172. };
  173. var subview = new View
  174. {
  175. X = 1, Y = 2,
  176. Width = 5, Height = 5,
  177. Visible = false
  178. };
  179. Application.TopRunnable.Add (subview);
  180. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  181. Assert.Equal (expectedSubViewFound, found == subview);
  182. Application.TopRunnable.Dispose ();
  183. Application.ResetState (true);
  184. }
  185. [Theory]
  186. [InlineData (0, 0, false)]
  187. [InlineData (1, 1, false)]
  188. [InlineData (9, 9, false)]
  189. [InlineData (10, 10, false)]
  190. [InlineData (6, 7, false)]
  191. [InlineData (1, 2, false)]
  192. [InlineData (5, 6, false)]
  193. public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int testY, bool expectedSubViewFound)
  194. {
  195. Application.TopRunnable = new ()
  196. {
  197. Width = 10, Height = 10,
  198. Visible = false
  199. };
  200. var subview = new View
  201. {
  202. X = 1, Y = 2,
  203. Width = 5, Height = 5
  204. };
  205. Application.TopRunnable.Add (subview);
  206. subview.Visible = true;
  207. Assert.True (subview.Visible);
  208. Assert.False (Application.TopRunnable.Visible);
  209. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  210. Assert.Equal (expectedSubViewFound, found == subview);
  211. Application.TopRunnable.Dispose ();
  212. Application.ResetState (true);
  213. }
  214. // Test that GetViewsUnderLocation works if the start view has positive Adornments
  215. [Theory]
  216. [InlineData (0, 0, false)]
  217. [InlineData (1, 1, false)]
  218. [InlineData (9, 9, false)]
  219. [InlineData (10, 10, false)]
  220. [InlineData (7, 8, false)]
  221. [InlineData (1, 2, false)]
  222. [InlineData (2, 3, true)]
  223. [InlineData (5, 6, true)]
  224. [InlineData (6, 7, true)]
  225. public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
  226. {
  227. Application.TopRunnable = new ()
  228. {
  229. Width = 10, Height = 10
  230. };
  231. Application.TopRunnable.Margin!.Thickness = new (1);
  232. var subview = new View
  233. {
  234. X = 1, Y = 2,
  235. Width = 5, Height = 5
  236. };
  237. Application.TopRunnable.Add (subview);
  238. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  239. Assert.Equal (expectedSubViewFound, found == subview);
  240. Application.TopRunnable.Dispose ();
  241. Application.ResetState (true);
  242. }
  243. // Test that GetViewsUnderLocation works if the start view has offset Viewport location
  244. [Theory]
  245. [InlineData (1, 0, 0, true)]
  246. [InlineData (1, 1, 1, true)]
  247. [InlineData (1, 2, 2, false)]
  248. [InlineData (-1, 3, 3, true)]
  249. [InlineData (-1, 2, 2, true)]
  250. [InlineData (-1, 1, 1, false)]
  251. [InlineData (-1, 0, 0, false)]
  252. public void Returns_Correct_If_Start_Has_Offset_Viewport (int offset, int testX, int testY, bool expectedSubViewFound)
  253. {
  254. Application.TopRunnable = new ()
  255. {
  256. Width = 10, Height = 10,
  257. ViewportSettings = ViewportSettingsFlags.AllowNegativeLocation
  258. };
  259. Application.TopRunnable.Viewport = new (offset, offset, 10, 10);
  260. var subview = new View
  261. {
  262. X = 1, Y = 1,
  263. Width = 2, Height = 2
  264. };
  265. Application.TopRunnable.Add (subview);
  266. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  267. Assert.Equal (expectedSubViewFound, found == subview);
  268. Application.TopRunnable.Dispose ();
  269. Application.ResetState (true);
  270. }
  271. [Theory]
  272. [InlineData (9, 9, true)]
  273. [InlineData (0, 0, false)]
  274. [InlineData (1, 1, false)]
  275. [InlineData (10, 10, false)]
  276. [InlineData (7, 8, false)]
  277. [InlineData (1, 2, false)]
  278. [InlineData (2, 3, false)]
  279. [InlineData (5, 6, false)]
  280. [InlineData (6, 7, false)]
  281. public void Returns_Correct_If_Start_Has_Adornment_WithSubView (int testX, int testY, bool expectedSubViewFound)
  282. {
  283. Application.TopRunnable = new ()
  284. {
  285. Width = 10, Height = 10
  286. };
  287. Application.TopRunnable.Padding!.Thickness = new (1);
  288. var subview = new View
  289. {
  290. X = Pos.AnchorEnd (1), Y = Pos.AnchorEnd (1),
  291. Width = 1, Height = 1
  292. };
  293. Application.TopRunnable.Padding.Add (subview);
  294. Application.TopRunnable.BeginInit ();
  295. Application.TopRunnable.EndInit ();
  296. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  297. Assert.Equal (expectedSubViewFound, found == subview);
  298. Application.TopRunnable.Dispose ();
  299. Application.ResetState (true);
  300. }
  301. [Theory]
  302. [InlineData (0, 0, new string [] { })]
  303. [InlineData (9, 9, new string [] { })]
  304. [InlineData (1, 1, new [] { "Top", "Border" })]
  305. [InlineData (8, 8, new [] { "Top", "Border" })]
  306. [InlineData (2, 2, new [] { "Top", "Padding" })]
  307. [InlineData (7, 7, new [] { "Top", "Padding" })]
  308. [InlineData (5, 5, new [] { "Top" })]
  309. public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, string [] expectedViewsFound)
  310. {
  311. Application.ResetState (true);
  312. Application.TopRunnable = new ()
  313. {
  314. Id = "Top",
  315. Width = 10, Height = 10
  316. };
  317. Application.TopRunnable.Margin!.Thickness = new (1);
  318. Application.TopRunnable.Margin!.Id = "Margin";
  319. Application.TopRunnable.Border!.Thickness = new (1);
  320. Application.TopRunnable.Border!.Id = "Border";
  321. Application.TopRunnable.Padding!.Thickness = new (1);
  322. Application.TopRunnable.Padding.Id = "Padding";
  323. var subview = new View
  324. {
  325. Id = "SubView",
  326. X = 1, Y = 1,
  327. Width = 1, Height = 1
  328. };
  329. Application.TopRunnable.Add (subview);
  330. List<View?> viewsUnderMouse = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse);
  331. string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray ();
  332. Assert.Equal (expectedViewsFound, foundIds);
  333. Application.TopRunnable.Dispose ();
  334. Application.ResetState (true);
  335. }
  336. // Test that GetViewsUnderLocation works if the subview has positive Adornments
  337. [Theory]
  338. [InlineData (0, 0, new [] { "Top" })]
  339. [InlineData (1, 1, new [] { "Top" })]
  340. [InlineData (9, 9, new [] { "Top" })]
  341. [InlineData (10, 10, new string [] { })]
  342. [InlineData (7, 8, new [] { "Top" })]
  343. [InlineData (6, 7, new [] { "Top" })]
  344. [InlineData (1, 2, new [] { "Top", "subview", "border" })]
  345. [InlineData (5, 6, new [] { "Top", "subview", "border" })]
  346. [InlineData (2, 3, new [] { "Top", "subview" })]
  347. public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, string [] expectedViewsFound)
  348. {
  349. Application.TopRunnable = new ()
  350. {
  351. Id = "Top",
  352. Width = 10, Height = 10
  353. };
  354. var subview = new View
  355. {
  356. Id = "subview",
  357. X = 1, Y = 2,
  358. Width = 5, Height = 5
  359. };
  360. subview.Border!.Thickness = new (1);
  361. subview.Border!.Id = "border";
  362. Application.TopRunnable.Add (subview);
  363. List<View?> viewsUnderMouse = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse);
  364. string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray ();
  365. Assert.Equal (expectedViewsFound, foundIds);
  366. Application.TopRunnable.Dispose ();
  367. Application.ResetState (true);
  368. }
  369. // Test that GetViewsUnderLocation works if the subview has positive Adornments
  370. [Theory]
  371. [InlineData (0, 0, new [] { "Top" })]
  372. [InlineData (1, 1, new [] { "Top" })]
  373. [InlineData (9, 9, new [] { "Top" })]
  374. [InlineData (10, 10, new string [] { })]
  375. [InlineData (7, 8, new [] { "Top" })]
  376. [InlineData (6, 7, new [] { "Top" })]
  377. [InlineData (1, 2, new [] { "Top" })]
  378. [InlineData (5, 6, new [] { "Top" })]
  379. [InlineData (2, 3, new [] { "Top", "subview" })]
  380. public void Returns_Correct_If_SubView_Has_Adornments_With_TransparentMouse (int testX, int testY, string [] expectedViewsFound)
  381. {
  382. Application.TopRunnable = new ()
  383. {
  384. Id = "Top",
  385. Width = 10, Height = 10
  386. };
  387. var subview = new View
  388. {
  389. Id = "subview",
  390. X = 1, Y = 2,
  391. Width = 5, Height = 5
  392. };
  393. subview.Border!.Thickness = new (1);
  394. subview.Border!.ViewportSettings = ViewportSettingsFlags.TransparentMouse;
  395. subview.Border!.Id = "border";
  396. Application.TopRunnable.Add (subview);
  397. List<View?> viewsUnderMouse = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse);
  398. string [] foundIds = viewsUnderMouse.Select (v => v!.Id).ToArray ();
  399. Assert.Equal (expectedViewsFound, foundIds);
  400. Application.TopRunnable.Dispose ();
  401. Application.ResetState (true);
  402. }
  403. [Theory]
  404. [InlineData (0, 0, false)]
  405. [InlineData (1, 1, false)]
  406. [InlineData (9, 9, false)]
  407. [InlineData (10, 10, false)]
  408. [InlineData (7, 8, false)]
  409. [InlineData (6, 7, false)]
  410. [InlineData (1, 2, false)]
  411. [InlineData (5, 6, false)]
  412. [InlineData (6, 5, false)]
  413. [InlineData (5, 5, true)]
  414. public void Returns_Correct_If_SubView_Has_Adornment_WithSubView (int testX, int testY, bool expectedSubViewFound)
  415. {
  416. Application.TopRunnable = new ()
  417. {
  418. Width = 10, Height = 10
  419. };
  420. // A subview with + Padding
  421. var subview = new View
  422. {
  423. X = 1, Y = 1,
  424. Width = 5, Height = 5
  425. };
  426. subview.Padding!.Thickness = new (1);
  427. // This subview will be at the bottom-right-corner of subview
  428. // So screen-relative location will be X + Width - 1 = 5
  429. var paddingSubView = new View
  430. {
  431. X = Pos.AnchorEnd (1),
  432. Y = Pos.AnchorEnd (1),
  433. Width = 1,
  434. Height = 1
  435. };
  436. subview.Padding.Add (paddingSubView);
  437. Application.TopRunnable.Add (subview);
  438. Application.TopRunnable.BeginInit ();
  439. Application.TopRunnable.EndInit ();
  440. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  441. Assert.Equal (expectedSubViewFound, found == paddingSubView);
  442. Application.TopRunnable.Dispose ();
  443. Application.ResetState (true);
  444. }
  445. [Theory]
  446. [InlineData (0, 0, false)]
  447. [InlineData (1, 1, false)]
  448. [InlineData (9, 9, false)]
  449. [InlineData (10, 10, false)]
  450. [InlineData (7, 8, false)]
  451. [InlineData (6, 7, false)]
  452. [InlineData (1, 2, false)]
  453. [InlineData (5, 6, false)]
  454. [InlineData (6, 5, false)]
  455. [InlineData (5, 5, true)]
  456. public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubView (int testX, int testY, bool expectedSubViewFound)
  457. {
  458. Application.TopRunnable = new ()
  459. {
  460. Width = 10, Height = 10
  461. };
  462. // A subview with + Padding
  463. var subview = new View
  464. {
  465. X = 1, Y = 1,
  466. Width = 5, Height = 5
  467. };
  468. subview.Padding!.Thickness = new (1);
  469. // Scroll the subview
  470. subview.SetContentSize (new (10, 10));
  471. subview.Viewport = subview.Viewport with { Location = new (1, 1) };
  472. // This subview will be at the bottom-right-corner of subview
  473. // So screen-relative location will be X + Width - 1 = 5
  474. var paddingSubView = new View
  475. {
  476. X = Pos.AnchorEnd (1),
  477. Y = Pos.AnchorEnd (1),
  478. Width = 1,
  479. Height = 1
  480. };
  481. subview.Padding.Add (paddingSubView);
  482. Application.TopRunnable.Add (subview);
  483. Application.TopRunnable.BeginInit ();
  484. Application.TopRunnable.EndInit ();
  485. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  486. Assert.Equal (expectedSubViewFound, found == paddingSubView);
  487. Application.TopRunnable.Dispose ();
  488. Application.ResetState (true);
  489. }
  490. // Test that GetViewsUnderLocation works with nested subviews
  491. [Theory]
  492. [InlineData (0, 0, -1)]
  493. [InlineData (9, 9, -1)]
  494. [InlineData (10, 10, -1)]
  495. [InlineData (1, 1, 0)]
  496. [InlineData (1, 2, 0)]
  497. [InlineData (2, 2, 1)]
  498. [InlineData (3, 3, 2)]
  499. [InlineData (5, 5, 2)]
  500. public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expectedSubViewFound)
  501. {
  502. Application.TopRunnable = new ()
  503. {
  504. Width = 10, Height = 10
  505. };
  506. var numSubViews = 3;
  507. List<View> subviews = new ();
  508. for (var i = 0; i < numSubViews; i++)
  509. {
  510. var subview = new View
  511. {
  512. X = 1, Y = 1,
  513. Width = 5, Height = 5
  514. };
  515. subviews.Add (subview);
  516. if (i > 0)
  517. {
  518. subviews [i - 1].Add (subview);
  519. }
  520. }
  521. Application.TopRunnable.Add (subviews [0]);
  522. View? found = Application.TopRunnable.GetViewsUnderLocation (new (testX, testY), ViewportSettingsFlags.TransparentMouse).LastOrDefault ();
  523. Assert.Equal (expectedSubViewFound, subviews.IndexOf (found!));
  524. Application.TopRunnable.Dispose ();
  525. Application.ResetState (true);
  526. }
  527. [Theory]
  528. [InlineData (0, 0, new [] { "top" })]
  529. [InlineData (9, 9, new [] { "top" })]
  530. [InlineData (10, 10, new string [] { })]
  531. [InlineData (1, 1, new [] { "top", "view" })]
  532. [InlineData (1, 2, new [] { "top", "view" })]
  533. [InlineData (2, 1, new [] { "top", "view" })]
  534. [InlineData (2, 2, new [] { "top", "view", "subView" })]
  535. [InlineData (3, 3, new [] { "top" })] // clipped
  536. [InlineData (2, 3, new [] { "top" })] // clipped
  537. public void Tiled_SubViews (int mouseX, int mouseY, string [] viewIdStrings)
  538. {
  539. // Arrange
  540. Application.TopRunnable = new ()
  541. {
  542. Frame = new (0, 0, 10, 10),
  543. Id = "top"
  544. };
  545. var view = new View
  546. {
  547. Id = "view",
  548. X = 1,
  549. Y = 1,
  550. Width = 2,
  551. Height = 2,
  552. Arrangement = ViewArrangement.Overlapped
  553. }; // at 1,1 to 3,2 (screen)
  554. var subView = new View
  555. {
  556. Id = "subView",
  557. X = 1,
  558. Y = 1,
  559. Width = 2,
  560. Height = 2,
  561. Arrangement = ViewArrangement.Overlapped
  562. }; // at 2,2 to 4,3 (screen)
  563. view.Add (subView);
  564. Application.TopRunnable.Add (view);
  565. List<View?> found = Application.TopRunnable.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse);
  566. string [] foundIds = found.Select (v => v!.Id).ToArray ();
  567. Assert.Equal (viewIdStrings, foundIds);
  568. Application.TopRunnable.Dispose ();
  569. Application.ResetState (true);
  570. }
  571. [Theory]
  572. [InlineData (0, 0, new [] { "top" })]
  573. [InlineData (9, 9, new [] { "top" })]
  574. [InlineData (10, 10, new string [] { })]
  575. [InlineData (-1, -1, new string [] { })]
  576. [InlineData (1, 1, new [] { "top", "view" })]
  577. [InlineData (1, 2, new [] { "top", "view" })]
  578. [InlineData (2, 1, new [] { "top", "view" })]
  579. [InlineData (2, 2, new [] { "top", "view", "popover" })]
  580. [InlineData (3, 3, new [] { "top" })] // clipped
  581. [InlineData (2, 3, new [] { "top" })] // clipped
  582. public void Popover (int mouseX, int mouseY, string [] viewIdStrings)
  583. {
  584. // Arrange
  585. Application.TopRunnable = new ()
  586. {
  587. Frame = new (0, 0, 10, 10),
  588. Id = "top"
  589. };
  590. var view = new View
  591. {
  592. Id = "view",
  593. X = 1,
  594. Y = 1,
  595. Width = 2,
  596. Height = 2,
  597. Arrangement = ViewArrangement.Overlapped
  598. }; // at 1,1 to 3,2 (screen)
  599. var popOver = new View
  600. {
  601. Id = "popover",
  602. X = 1,
  603. Y = 1,
  604. Width = 2,
  605. Height = 2,
  606. Arrangement = ViewArrangement.Overlapped
  607. }; // at 2,2 to 4,3 (screen)
  608. view.Add (popOver);
  609. Application.TopRunnable.Add (view);
  610. List<View?> found = Application.TopRunnable.GetViewsUnderLocation (new (mouseX, mouseY), ViewportSettingsFlags.TransparentMouse);
  611. string [] foundIds = found.Select (v => v!.Id).ToArray ();
  612. Assert.Equal (viewIdStrings, foundIds);
  613. Application.TopRunnable.Dispose ();
  614. Application.ResetState (true);
  615. }
  616. [Fact]
  617. public void Returns_TopToplevel_When_Point_Inside_Only_TopToplevel ()
  618. {
  619. Application.ResetState (true);
  620. Toplevel topToplevel = new ()
  621. {
  622. Id = "topToplevel",
  623. Frame = new (0, 0, 20, 20)
  624. };
  625. Toplevel secondaryToplevel = new ()
  626. {
  627. Id = "secondaryToplevel",
  628. Frame = new (5, 5, 10, 10)
  629. };
  630. secondaryToplevel.Margin!.Thickness = new (1);
  631. secondaryToplevel.Layout ();
  632. Application.SessionStack.Clear ();
  633. Application.SessionStack.Push (topToplevel);
  634. Application.SessionStack.Push (secondaryToplevel);
  635. Application.TopRunnable = secondaryToplevel;
  636. List<View?> found = Application.TopRunnable.GetViewsUnderLocation (new (2, 2), ViewportSettingsFlags.TransparentMouse);
  637. Assert.Contains (found, v => v?.Id == topToplevel.Id);
  638. Assert.Contains (found, v => v == topToplevel);
  639. topToplevel.Dispose ();
  640. secondaryToplevel.Dispose ();
  641. Application.SessionStack.Clear ();
  642. Application.ResetState (true);
  643. }
  644. [Fact]
  645. public void Returns_SecondaryToplevel_When_Point_Inside_Only_SecondaryToplevel ()
  646. {
  647. Application.ResetState (true);
  648. Toplevel topToplevel = new ()
  649. {
  650. Id = "topToplevel",
  651. Frame = new (0, 0, 20, 20)
  652. };
  653. Toplevel secondaryToplevel = new ()
  654. {
  655. Id = "secondaryToplevel",
  656. Frame = new (5, 5, 10, 10)
  657. };
  658. secondaryToplevel.Margin!.Thickness = new (1);
  659. secondaryToplevel.Layout ();
  660. Application.SessionStack.Clear ();
  661. Application.SessionStack.Push (topToplevel);
  662. Application.SessionStack.Push (secondaryToplevel);
  663. Application.TopRunnable = secondaryToplevel;
  664. List<View?> found = Application.TopRunnable.GetViewsUnderLocation (new (7, 7), ViewportSettingsFlags.TransparentMouse);
  665. Assert.Contains (found, v => v?.Id == secondaryToplevel.Id);
  666. Assert.DoesNotContain (found, v => v?.Id == topToplevel.Id);
  667. topToplevel.Dispose ();
  668. secondaryToplevel.Dispose ();
  669. Application.SessionStack.Clear ();
  670. Application.ResetState (true);
  671. }
  672. [Fact]
  673. public void Returns_Depends_On_Margin_ViewportSettings_When_Point_In_Margin_Of_SecondaryToplevel ()
  674. {
  675. Application.ResetState (true);
  676. Toplevel topToplevel = new ()
  677. {
  678. Id = "topToplevel",
  679. Frame = new (0, 0, 20, 20)
  680. };
  681. Toplevel secondaryToplevel = new ()
  682. {
  683. Id = "secondaryToplevel",
  684. Frame = new (5, 5, 10, 10)
  685. };
  686. secondaryToplevel.Margin!.Thickness = new (1);
  687. Application.SessionStack.Clear ();
  688. Application.SessionStack.Push (topToplevel);
  689. Application.SessionStack.Push (secondaryToplevel);
  690. Application.TopRunnable = secondaryToplevel;
  691. secondaryToplevel.Margin!.ViewportSettings = ViewportSettingsFlags.None;
  692. List<View?> found = Application.TopRunnable.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse);
  693. Assert.Contains (found, v => v == secondaryToplevel);
  694. Assert.Contains (found, v => v == secondaryToplevel.Margin);
  695. Assert.DoesNotContain (found, v => v?.Id == topToplevel.Id);
  696. secondaryToplevel.Margin!.ViewportSettings = ViewportSettingsFlags.TransparentMouse;
  697. found = Application.TopRunnable.GetViewsUnderLocation (new (5, 5), ViewportSettingsFlags.TransparentMouse);
  698. Assert.DoesNotContain (found, v => v == secondaryToplevel);
  699. Assert.DoesNotContain (found, v => v == secondaryToplevel.Margin);
  700. Assert.Contains (found, v => v?.Id == topToplevel.Id);
  701. topToplevel.Dispose ();
  702. secondaryToplevel.Dispose ();
  703. Application.SessionStack.Clear ();
  704. Application.ResetState (true);
  705. }
  706. [Fact]
  707. public void Returns_Empty_When_Point_Outside_All_Toplevels ()
  708. {
  709. Application.ResetState (true);
  710. Toplevel topToplevel = new ()
  711. {
  712. Id = "topToplevel",
  713. Frame = new (0, 0, 20, 20)
  714. };
  715. Toplevel secondaryToplevel = new ()
  716. {
  717. Id = "secondaryToplevel",
  718. Frame = new (5, 5, 10, 10)
  719. };
  720. secondaryToplevel.Margin!.Thickness = new (1);
  721. secondaryToplevel.Layout ();
  722. Application.SessionStack.Clear ();
  723. Application.SessionStack.Push (topToplevel);
  724. Application.SessionStack.Push (secondaryToplevel);
  725. Application.TopRunnable = secondaryToplevel;
  726. List<View?> found = Application.TopRunnable.GetViewsUnderLocation (new (20, 20), ViewportSettingsFlags.TransparentMouse);
  727. Assert.Empty (found);
  728. topToplevel.Dispose ();
  729. secondaryToplevel.Dispose ();
  730. Application.SessionStack.Clear ();
  731. Application.ResetState (true);
  732. }
  733. }