DeferredRendererTests_HitTesting.cs 21 KB

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