CompositorHitTestingTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. using System;
  2. using System.Linq;
  3. using Avalonia.Base.UnitTests.VisualTree;
  4. using Avalonia.Controls;
  5. using Avalonia.Controls.Presenters;
  6. using Avalonia.Controls.Shapes;
  7. using Avalonia.Layout;
  8. using Avalonia.Media;
  9. using Avalonia.Platform;
  10. using Avalonia.Rendering;
  11. using Avalonia.UnitTests;
  12. using Avalonia.VisualTree;
  13. using Moq;
  14. using Xunit;
  15. namespace Avalonia.Base.UnitTests.Rendering;
  16. public class CompositorHitTestingTests : CompositorTestsBase
  17. {
  18. [Fact]
  19. public void HitTest_Should_Find_Controls_At_Point()
  20. {
  21. using (var s = new CompositorTestServices(new Size(200, 200)))
  22. {
  23. var border = new Border
  24. {
  25. Width = 100,
  26. Height = 100,
  27. Background = Brushes.Red,
  28. HorizontalAlignment = HorizontalAlignment.Center,
  29. VerticalAlignment = VerticalAlignment.Center
  30. };
  31. s.TopLevel.Content = border;
  32. s.AssertHitTest(new Point(100, 100), null, border);
  33. }
  34. }
  35. [Fact]
  36. public void HitTest_Should_Not_Find_Empty_Controls_At_Point()
  37. {
  38. using (var s = new CompositorTestServices(new Size(200, 200)))
  39. {
  40. var border = new Border
  41. {
  42. Width = 100,
  43. Height = 100,
  44. HorizontalAlignment = HorizontalAlignment.Center,
  45. VerticalAlignment = VerticalAlignment.Center
  46. };
  47. s.TopLevel.Content = border;
  48. s.AssertHitTest(new Point(100, 100), null);
  49. }
  50. }
  51. [Fact]
  52. public void HitTest_Should_Not_Find_Invisible_Controls_At_Point()
  53. {
  54. using (var s = new CompositorTestServices(new Size(200, 200)))
  55. {
  56. Border visible, border;
  57. s.TopLevel.Content = border = new Border
  58. {
  59. Width = 100,
  60. Height = 100,
  61. Background = Brushes.Red,
  62. HorizontalAlignment = HorizontalAlignment.Center,
  63. VerticalAlignment = VerticalAlignment.Center,
  64. IsVisible = false,
  65. Child = visible = new Border
  66. {
  67. Background = Brushes.Red,
  68. HorizontalAlignment = HorizontalAlignment.Stretch,
  69. VerticalAlignment = VerticalAlignment.Stretch,
  70. }
  71. };
  72. s.AssertHitTest(new Point(100, 100), null);
  73. }
  74. }
  75. [Theory,
  76. InlineData(false, false),
  77. InlineData(true, false),
  78. InlineData(false, true),
  79. InlineData(true, true),
  80. ]
  81. public void HitTest_Should_Find_Zero_Opacity_Controls_At_Point(bool parent, bool child)
  82. {
  83. using (var s = new CompositorTestServices(new Size(200, 200)))
  84. {
  85. Border visible, border;
  86. s.TopLevel.Content = border = new Border
  87. {
  88. Width = 100,
  89. Height = 100,
  90. Background = Brushes.Red,
  91. HorizontalAlignment = HorizontalAlignment.Center,
  92. VerticalAlignment = VerticalAlignment.Center,
  93. Opacity = parent ? 0 : 1,
  94. Child = visible = new Border
  95. {
  96. Opacity = child ? 0 : 1,
  97. Background = Brushes.Red,
  98. HorizontalAlignment = HorizontalAlignment.Stretch,
  99. VerticalAlignment = VerticalAlignment.Stretch,
  100. }
  101. };
  102. s.AssertHitTest(new Point(100, 100), null, visible, border);
  103. }
  104. }
  105. [Fact]
  106. public void HitTest_Should_Not_Find_Control_Outside_Point()
  107. {
  108. using (var s = new CompositorTestServices(new Size(200, 200)))
  109. {
  110. var border = new Border
  111. {
  112. Width = 100,
  113. Height = 100,
  114. Background = Brushes.Red,
  115. HorizontalAlignment = HorizontalAlignment.Center,
  116. VerticalAlignment = VerticalAlignment.Center
  117. };
  118. s.TopLevel.Content = border;
  119. s.AssertHitTest(new Point(10, 10), null);
  120. }
  121. }
  122. [Fact]
  123. public void HitTest_Should_Return_Top_Controls_First()
  124. {
  125. using (var s = new CompositorTestServices(new Size(200, 200)))
  126. {
  127. Panel container = new Panel
  128. {
  129. Width = 200,
  130. Height = 200,
  131. Children =
  132. {
  133. new Border
  134. {
  135. Width = 100,
  136. Height = 100,
  137. Background = Brushes.Red,
  138. HorizontalAlignment = HorizontalAlignment.Center,
  139. VerticalAlignment = VerticalAlignment.Center
  140. },
  141. new Border
  142. {
  143. Width = 50,
  144. Height = 50,
  145. Background = Brushes.Blue,
  146. HorizontalAlignment = HorizontalAlignment.Center,
  147. VerticalAlignment = VerticalAlignment.Center
  148. }
  149. }
  150. };
  151. s.TopLevel.Content = container;
  152. s.AssertHitTest(new Point(100, 100), null, container.Children[1], container.Children[0]);
  153. }
  154. }
  155. [Fact]
  156. public void HitTest_Should_Return_Top_Controls_First_With_ZIndex()
  157. {
  158. using (var s = new CompositorTestServices(new Size(200, 200)))
  159. {
  160. Panel container = new Panel
  161. {
  162. Width = 200,
  163. Height = 200,
  164. Children =
  165. {
  166. new Border
  167. {
  168. Width = 100,
  169. Height = 100,
  170. ZIndex = 1,
  171. Background = Brushes.Red,
  172. HorizontalAlignment = HorizontalAlignment.Center,
  173. VerticalAlignment = VerticalAlignment.Center
  174. },
  175. new Border
  176. {
  177. Width = 50,
  178. Height = 50,
  179. Background = Brushes.Red,
  180. HorizontalAlignment = HorizontalAlignment.Center,
  181. VerticalAlignment = VerticalAlignment.Center
  182. },
  183. new Border
  184. {
  185. Width = 75,
  186. Height = 75,
  187. ZIndex = 2,
  188. Background = Brushes.Red,
  189. HorizontalAlignment = HorizontalAlignment.Center,
  190. VerticalAlignment = VerticalAlignment.Center
  191. }
  192. }
  193. };
  194. s.TopLevel.Content = container;
  195. s.AssertHitTest(new Point(100, 100), null, new[] { container.Children[2], container.Children[0], container.Children[1] });
  196. }
  197. }
  198. [Fact]
  199. public void HitTest_Should_Find_Control_Translated_Outside_Parent_Bounds()
  200. {
  201. using (var s = new CompositorTestServices(new Size(200, 200)))
  202. {
  203. Border target;
  204. Panel container = new Panel
  205. {
  206. Width = 200,
  207. Height = 200,
  208. Background = Brushes.Red,
  209. ClipToBounds = false,
  210. Children =
  211. {
  212. new Border
  213. {
  214. Width = 100,
  215. Height = 100,
  216. ZIndex = 1,
  217. Background = Brushes.Red,
  218. HorizontalAlignment = HorizontalAlignment.Left,
  219. VerticalAlignment = VerticalAlignment.Top,
  220. Child = target = new Border
  221. {
  222. Width = 50,
  223. Height = 50,
  224. Background = Brushes.Red,
  225. HorizontalAlignment = HorizontalAlignment.Left,
  226. VerticalAlignment = VerticalAlignment.Top,
  227. RenderTransform = new TranslateTransform(110, 110),
  228. }
  229. },
  230. }
  231. };
  232. s.TopLevel.Content = container;
  233. s.AssertHitTest(new Point(120, 120), null, target, container);
  234. }
  235. }
  236. [Fact]
  237. public void HitTest_Should_Not_Find_Control_Outside_Parent_Bounds_When_Clipped()
  238. {
  239. using (var s = new CompositorTestServices(new Size(200, 200)))
  240. {
  241. Border target;
  242. Panel container = new Panel
  243. {
  244. Width = 100,
  245. Height = 200,
  246. Background = Brushes.Red,
  247. Children =
  248. {
  249. new Panel()
  250. {
  251. Width = 100,
  252. Height = 100,
  253. Background = Brushes.Red,
  254. Margin = new Thickness(0, 100, 0, 0),
  255. ClipToBounds = true,
  256. Children =
  257. {
  258. (target = new Border()
  259. {
  260. Width = 100,
  261. Height = 100,
  262. Background = Brushes.Red,
  263. Margin = new Thickness(0, -100, 0, 0)
  264. })
  265. }
  266. }
  267. }
  268. };
  269. s.TopLevel.Content = container;
  270. s.AssertHitTest(new Point(50, 50), null, container);
  271. }
  272. }
  273. [Fact]
  274. public void HitTest_Should_Not_Find_Control_Outside_Scroll_Viewport()
  275. {
  276. using (var s = new CompositorTestServices(new Size(100, 200)))
  277. {
  278. Border target;
  279. Border item1;
  280. Border item2;
  281. ScrollContentPresenter scroll;
  282. Panel container = new Panel
  283. {
  284. Width = 100,
  285. Height = 200,
  286. Background = Brushes.Red,
  287. Children =
  288. {
  289. (target = new Border()
  290. {
  291. Name = "b1",
  292. Width = 100,
  293. Height = 100,
  294. Background = Brushes.Red,
  295. }),
  296. new Border()
  297. {
  298. Name = "b2",
  299. Width = 100,
  300. Height = 100,
  301. Background = Brushes.Red,
  302. Margin = new Thickness(0, 100, 0, 0),
  303. Child = scroll = new ScrollContentPresenter()
  304. {
  305. CanHorizontallyScroll = true,
  306. CanVerticallyScroll = true,
  307. Content = new StackPanel()
  308. {
  309. Children =
  310. {
  311. (item1 = new Border()
  312. {
  313. Name = "b3",
  314. Width = 100,
  315. Height = 100,
  316. Background = Brushes.Red,
  317. }),
  318. (item2 = new Border()
  319. {
  320. Name = "b4",
  321. Width = 100,
  322. Height = 100,
  323. Background = Brushes.Red,
  324. }),
  325. }
  326. }
  327. }
  328. }
  329. }
  330. };
  331. s.TopLevel.Content = container;
  332. scroll.UpdateChild();
  333. s.AssertHitTestFirst(new Point(50, 150), null, item1);
  334. s.AssertHitTestFirst(new Point(50,50), null, target);
  335. scroll.Offset = new Vector(0, 100);
  336. s.AssertHitTestFirst(new Point(50, 150), null, item2);
  337. s.AssertHitTestFirst(new Point(50,50), null, target);
  338. }
  339. }
  340. [Fact]
  341. public void HitTest_Should_Not_Find_Path_When_Outside_Fill()
  342. {
  343. using (var s = new CompositorTestServices(new Size(200, 200)))
  344. {
  345. Path path = new Path
  346. {
  347. Width = 200,
  348. Height = 200,
  349. Fill = Brushes.Red,
  350. Data = StreamGeometry.Parse("M100,0 L0,100 100,100")
  351. };
  352. s.TopLevel.Content = path;
  353. s.AssertHitTest(new Point(100, 100), null, path);
  354. s.AssertHitTest(new Point(10, 10), null);
  355. }
  356. }
  357. [Fact]
  358. public void HitTest_Should_Respect_Geometry_Clip()
  359. {
  360. using (var s = new CompositorTestServices(new Size(400, 400)))
  361. {
  362. Canvas canvas;
  363. Border border = new Border
  364. {
  365. Background = Brushes.Red,
  366. Clip = StreamGeometry.Parse("M100,0 L0,100 100,100"),
  367. Width = 200,
  368. Height = 200,
  369. Child = canvas = new Canvas
  370. {
  371. Background = Brushes.Yellow,
  372. Margin = new Thickness(10),
  373. }
  374. };
  375. s.TopLevel.Content = border;
  376. s.RunJobs();
  377. Assert.Equal(new Rect(100, 100, 200, 200), border.Bounds);
  378. s.AssertHitTest(new Point(200,200), null, canvas, border);
  379. s.AssertHitTest(new Point(110, 110), null);
  380. }
  381. }
  382. [Fact]
  383. public void HitTest_Should_Accommodate_ICustomHitTest()
  384. {
  385. using (var s = new CompositorTestServices(new Size(300, 200)))
  386. {
  387. Border border = new CustomHitTestBorder
  388. {
  389. ClipToBounds = false,
  390. Width = 100,
  391. Height = 100,
  392. Background = Brushes.Red,
  393. HorizontalAlignment = HorizontalAlignment.Center,
  394. VerticalAlignment = VerticalAlignment.Center
  395. };
  396. s.TopLevel.Content = border;
  397. s.AssertHitTest(75, 100, null, border);
  398. s.AssertHitTest(125, 100, null, border);
  399. s.AssertHitTest(175, 100, null);
  400. }
  401. }
  402. [Fact]
  403. public void HitTest_Should_Not_Hit_Controls_Next_Pixel()
  404. {
  405. using (var s = new CompositorTestServices(new Size(200, 200)))
  406. {
  407. Border targetRectangle;
  408. var stackPanel = new StackPanel
  409. {
  410. Orientation = Orientation.Vertical,
  411. HorizontalAlignment = HorizontalAlignment.Left,
  412. Children =
  413. {
  414. new Border { Width = 10, Height = 10, Background= Brushes.Red},
  415. { targetRectangle = new Border { Width = 10, Height = 10, Background = Brushes.Green} }
  416. }
  417. };
  418. s.TopLevel.Content = stackPanel;
  419. s.AssertHitTest(new Point(5, 10), null, targetRectangle);
  420. }
  421. }
  422. [Fact]
  423. public void HitTest_Filter_Should_Filter_Out_Children()
  424. {
  425. using (var s = new CompositorTestServices(new Size(200, 200)))
  426. {
  427. Border child, parent;
  428. s.TopLevel.Content = parent = new Border
  429. {
  430. Width = 100,
  431. Height = 100,
  432. Background = Brushes.Red,
  433. HorizontalAlignment = HorizontalAlignment.Center,
  434. VerticalAlignment = VerticalAlignment.Center,
  435. Child = child = new Border
  436. {
  437. Background = Brushes.Red,
  438. HorizontalAlignment = HorizontalAlignment.Stretch,
  439. VerticalAlignment = VerticalAlignment.Stretch,
  440. }
  441. };
  442. s.AssertHitTest(new Point(100, 100), null, child, parent);
  443. s.AssertHitTest(new Point(100, 100), v => v != parent);
  444. }
  445. }
  446. private static IDisposable TestApplication()
  447. {
  448. return UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
  449. }
  450. }