SceneBuilderTests.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. using System;
  2. using System.Linq;
  3. using Avalonia.Controls;
  4. using Avalonia.Media;
  5. using Avalonia.Rendering.SceneGraph;
  6. using Avalonia.UnitTests;
  7. using Avalonia.VisualTree;
  8. using Xunit;
  9. using Avalonia.Layout;
  10. using Avalonia.Rendering;
  11. namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
  12. {
  13. public class SceneBuilderTests
  14. {
  15. [Fact]
  16. public void Should_Build_Initial_Scene()
  17. {
  18. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  19. {
  20. Border border;
  21. TextBlock textBlock;
  22. var tree = new TestRoot
  23. {
  24. Child = border = new Border
  25. {
  26. Width = 100,
  27. Height = 100,
  28. Background = Brushes.Red,
  29. Child = textBlock = new TextBlock
  30. {
  31. Text = "Hello World",
  32. }
  33. }
  34. };
  35. tree.Measure(Size.Infinity);
  36. tree.Arrange(new Rect(tree.DesiredSize));
  37. var result = new Scene(tree);
  38. var sceneBuilder = new SceneBuilder();
  39. sceneBuilder.UpdateAll(result, new LayerDirtyRects());
  40. Assert.Same(tree, ((VisualNode)result.Root).LayerRoot);
  41. Assert.Equal(1, result.Root.Children.Count);
  42. var borderNode = (VisualNode)result.Root.Children[0];
  43. Assert.Same(borderNode, result.FindNode(border));
  44. Assert.Same(border, borderNode.Visual);
  45. Assert.Equal(1, borderNode.Children.Count);
  46. Assert.Equal(1, borderNode.DrawOperations.Count);
  47. var backgroundNode = (RectangleNode)borderNode.DrawOperations[0];
  48. Assert.Equal(Brushes.Red, backgroundNode.Brush);
  49. var textBlockNode = (VisualNode)borderNode.Children[0];
  50. Assert.Same(textBlockNode, result.FindNode(textBlock));
  51. Assert.Same(textBlock, textBlockNode.Visual);
  52. Assert.Equal(1, textBlockNode.DrawOperations.Count);
  53. var textNode = (TextNode)textBlockNode.DrawOperations[0];
  54. Assert.NotNull(textNode.Text);
  55. }
  56. }
  57. [Fact]
  58. public void Should_Respect_Margin_For_ClipBounds()
  59. {
  60. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  61. {
  62. Canvas canvas;
  63. var tree = new TestRoot
  64. {
  65. Width = 200,
  66. Height = 300,
  67. Child = new Border
  68. {
  69. Margin = new Thickness(10, 20, 30, 40),
  70. Child = canvas = new Canvas
  71. {
  72. Background = Brushes.AliceBlue,
  73. }
  74. }
  75. };
  76. tree.Measure(Size.Infinity);
  77. tree.Arrange(new Rect(tree.DesiredSize));
  78. var result = new Scene(tree);
  79. var sceneBuilder = new SceneBuilder();
  80. sceneBuilder.UpdateAll(result, new LayerDirtyRects());
  81. var canvasNode = result.FindNode(canvas);
  82. Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds);
  83. // Initial ClipBounds are correct, make sure they're still correct after updating canvas.
  84. result = result.Clone();
  85. Assert.True(sceneBuilder.Update(result, canvas, new LayerDirtyRects()));
  86. canvasNode = result.FindNode(canvas);
  87. Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds);
  88. }
  89. }
  90. [Fact]
  91. public void ClipBounds_Should_Be_Intersection_With_Parent_ClipBounds()
  92. {
  93. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  94. {
  95. Border border;
  96. var tree = new TestRoot
  97. {
  98. Width = 200,
  99. Height = 300,
  100. Child = new Canvas
  101. {
  102. ClipToBounds = true,
  103. Width = 100,
  104. Height = 100,
  105. HorizontalAlignment = HorizontalAlignment.Left,
  106. VerticalAlignment = VerticalAlignment.Top,
  107. Children =
  108. {
  109. (border = new Border
  110. {
  111. Background = Brushes.AliceBlue,
  112. Width = 100,
  113. Height = 100,
  114. [Canvas.LeftProperty] = 50,
  115. [Canvas.TopProperty] = 50,
  116. })
  117. }
  118. }
  119. };
  120. tree.Measure(Size.Infinity);
  121. tree.Arrange(new Rect(tree.DesiredSize));
  122. var scene = new Scene(tree);
  123. var sceneBuilder = new SceneBuilder();
  124. sceneBuilder.UpdateAll(scene, new LayerDirtyRects());
  125. var borderNode = scene.FindNode(border);
  126. Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
  127. // Initial ClipBounds are correct, make sure they're still correct after updating border.
  128. scene = scene.Clone();
  129. Assert.True(sceneBuilder.Update(scene, border, new LayerDirtyRects()));
  130. borderNode = scene.FindNode(border);
  131. Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
  132. }
  133. }
  134. [Fact]
  135. public void Should_Respect_ZIndex()
  136. {
  137. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  138. {
  139. Border front;
  140. Border back;
  141. var tree = new TestRoot
  142. {
  143. Child = new Panel
  144. {
  145. Children =
  146. {
  147. (front = new Border
  148. {
  149. ZIndex = 1,
  150. }),
  151. (back = new Border
  152. {
  153. ZIndex = 0,
  154. }),
  155. }
  156. }
  157. };
  158. var result = new Scene(tree);
  159. var sceneBuilder = new SceneBuilder();
  160. sceneBuilder.UpdateAll(result, new LayerDirtyRects());
  161. var panelNode = result.FindNode(tree.Child);
  162. var expected = new IVisual[] { back, front };
  163. var actual = panelNode.Children.OfType<IVisualNode>().Select(x => x.Visual).ToArray();
  164. Assert.Equal(expected, actual);
  165. }
  166. }
  167. [Fact]
  168. public void ClipBounds_Should_Be_In_Global_Coordinates()
  169. {
  170. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  171. {
  172. Border target;
  173. var tree = new TestRoot
  174. {
  175. Child = new Decorator
  176. {
  177. Margin = new Thickness(24, 26),
  178. Child = target = new Border
  179. {
  180. Margin = new Thickness(26, 24),
  181. Width = 100,
  182. Height = 100,
  183. }
  184. }
  185. };
  186. tree.Measure(Size.Infinity);
  187. tree.Arrange(new Rect(tree.DesiredSize));
  188. var result = new Scene(tree);
  189. var sceneBuilder = new SceneBuilder();
  190. sceneBuilder.UpdateAll(result, new LayerDirtyRects());
  191. var targetNode = result.FindNode(target);
  192. Assert.Equal(new Rect(50, 50, 100, 100), targetNode.ClipBounds);
  193. }
  194. }
  195. [Fact]
  196. public void Should_Update_Border_Background_Node()
  197. {
  198. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  199. {
  200. Border border;
  201. TextBlock textBlock;
  202. var tree = new TestRoot
  203. {
  204. Child = border = new Border
  205. {
  206. Width = 100,
  207. Height = 100,
  208. Background = Brushes.Red,
  209. Child = textBlock = new TextBlock
  210. {
  211. Foreground = Brushes.Green,
  212. Text = "Hello World",
  213. }
  214. }
  215. };
  216. tree.Measure(Size.Infinity);
  217. tree.Arrange(new Rect(tree.DesiredSize));
  218. var initial = new Scene(tree);
  219. var sceneBuilder = new SceneBuilder();
  220. sceneBuilder.UpdateAll(initial, new LayerDirtyRects());
  221. var initialBackgroundNode = initial.FindNode(border).Children[0];
  222. var initialTextNode = initial.FindNode(textBlock).DrawOperations[0];
  223. Assert.NotNull(initialBackgroundNode);
  224. Assert.NotNull(initialTextNode);
  225. border.Background = Brushes.Green;
  226. var result = initial.Clone();
  227. sceneBuilder.Update(result, border, new LayerDirtyRects());
  228. var borderNode = (VisualNode)result.Root.Children[0];
  229. Assert.Same(border, borderNode.Visual);
  230. var backgroundNode = (RectangleNode)borderNode.DrawOperations[0];
  231. Assert.NotSame(initialBackgroundNode, backgroundNode);
  232. Assert.Equal(Brushes.Green, backgroundNode.Brush);
  233. var textBlockNode = (VisualNode)borderNode.Children[0];
  234. Assert.Same(textBlock, textBlockNode.Visual);
  235. var textNode = (TextNode)textBlockNode.DrawOperations[0];
  236. Assert.Same(initialTextNode, textNode);
  237. }
  238. }
  239. [Fact]
  240. public void Should_Update_When_Control_Added()
  241. {
  242. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  243. {
  244. Border border;
  245. var tree = new TestRoot
  246. {
  247. Width = 100,
  248. Height = 100,
  249. Child = border = new Border
  250. {
  251. Background = Brushes.Red,
  252. }
  253. };
  254. Canvas canvas;
  255. var decorator = new Decorator
  256. {
  257. Child = canvas = new Canvas(),
  258. };
  259. tree.Measure(Size.Infinity);
  260. tree.Arrange(new Rect(tree.DesiredSize));
  261. var initial = new Scene(tree);
  262. var sceneBuilder = new SceneBuilder();
  263. sceneBuilder.UpdateAll(initial, new LayerDirtyRects());
  264. border.Child = decorator;
  265. var result = initial.Clone();
  266. Assert.True(sceneBuilder.Update(result, decorator, new LayerDirtyRects()));
  267. // Updating canvas should result in no-op as it should have been updated along
  268. // with decorator as part of the add opeation.
  269. Assert.False(sceneBuilder.Update(result, canvas, new LayerDirtyRects()));
  270. var borderNode = (VisualNode)result.Root.Children[0];
  271. Assert.Equal(1, borderNode.Children.Count);
  272. Assert.Equal(1, borderNode.DrawOperations.Count);
  273. var decoratorNode = (VisualNode)borderNode.Children[0];
  274. Assert.Same(decorator, decoratorNode.Visual);
  275. Assert.Same(decoratorNode, result.FindNode(decorator));
  276. var canvasNode = (VisualNode)decoratorNode.Children[0];
  277. Assert.Same(canvas, canvasNode.Visual);
  278. Assert.Same(canvasNode, result.FindNode(canvas));
  279. }
  280. }
  281. [Fact]
  282. public void Should_Update_When_Control_Removed()
  283. {
  284. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  285. {
  286. Border border;
  287. Decorator decorator;
  288. Canvas canvas;
  289. var tree = new TestRoot
  290. {
  291. Width = 100,
  292. Height = 100,
  293. Child = border = new Border
  294. {
  295. Background = Brushes.Red,
  296. Child = decorator = new Decorator
  297. {
  298. Child = canvas = new Canvas
  299. {
  300. Background = Brushes.AliceBlue,
  301. }
  302. }
  303. }
  304. };
  305. tree.Measure(Size.Infinity);
  306. tree.Arrange(new Rect(tree.DesiredSize));
  307. var initial = new Scene(tree);
  308. var sceneBuilder = new SceneBuilder();
  309. sceneBuilder.UpdateAll(initial, new LayerDirtyRects());
  310. border.Child = null;
  311. var result = initial.Clone();
  312. Assert.True(sceneBuilder.Update(result, decorator, new LayerDirtyRects()));
  313. Assert.False(sceneBuilder.Update(result, canvas, new LayerDirtyRects()));
  314. var borderNode = (VisualNode)result.Root.Children[0];
  315. Assert.Equal(0, borderNode.Children.Count);
  316. Assert.Equal(1, borderNode.DrawOperations.Count);
  317. Assert.Null(result.FindNode(decorator));
  318. }
  319. }
  320. [Fact]
  321. public void Should_Update_When_Control_Made_Invisible()
  322. {
  323. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  324. {
  325. Decorator decorator;
  326. Border border;
  327. Canvas canvas;
  328. var tree = new TestRoot
  329. {
  330. Width = 100,
  331. Height = 100,
  332. Child = decorator = new Decorator
  333. {
  334. Child = border = new Border
  335. {
  336. Background = Brushes.Red,
  337. Child = canvas = new Canvas(),
  338. }
  339. }
  340. };
  341. tree.Measure(Size.Infinity);
  342. tree.Arrange(new Rect(tree.DesiredSize));
  343. var initial = new Scene(tree);
  344. var sceneBuilder = new SceneBuilder();
  345. sceneBuilder.UpdateAll(initial, new LayerDirtyRects());
  346. border.IsVisible = false;
  347. var result = initial.Clone();
  348. Assert.True(sceneBuilder.Update(result, border, new LayerDirtyRects()));
  349. Assert.False(sceneBuilder.Update(result, canvas, new LayerDirtyRects()));
  350. var decoratorNode = (VisualNode)result.Root.Children[0];
  351. Assert.Equal(0, decoratorNode.Children.Count);
  352. Assert.Null(result.FindNode(border));
  353. Assert.Null(result.FindNode(canvas));
  354. }
  355. }
  356. [Fact]
  357. public void Should_Update_Descendent_Tranform_When_Margin_Changed()
  358. {
  359. using (TestApplication())
  360. {
  361. Decorator decorator;
  362. Border border;
  363. Canvas canvas;
  364. var tree = new TestRoot
  365. {
  366. Width = 100,
  367. Height = 100,
  368. Child = decorator = new Decorator
  369. {
  370. Margin = new Thickness(0, 10, 0, 0),
  371. Child = border = new Border
  372. {
  373. Child = canvas = new Canvas(),
  374. }
  375. }
  376. };
  377. var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
  378. layout.ExecuteInitialLayoutPass(tree);
  379. var scene = new Scene(tree);
  380. var sceneBuilder = new SceneBuilder();
  381. sceneBuilder.UpdateAll(scene, new LayerDirtyRects());
  382. var borderNode = scene.FindNode(border);
  383. var canvasNode = scene.FindNode(canvas);
  384. Assert.Equal(Matrix.CreateTranslation(0, 10), borderNode.Transform);
  385. Assert.Equal(Matrix.CreateTranslation(0, 10), canvasNode.Transform);
  386. decorator.Margin = new Thickness(0, 20, 0, 0);
  387. layout.ExecuteLayoutPass();
  388. scene = scene.Clone();
  389. sceneBuilder.Update(scene, decorator, new LayerDirtyRects());
  390. borderNode = scene.FindNode(border);
  391. canvasNode = scene.FindNode(canvas);
  392. Assert.Equal(Matrix.CreateTranslation(0, 20), borderNode.Transform);
  393. Assert.Equal(Matrix.CreateTranslation(0, 20), canvasNode.Transform);
  394. }
  395. }
  396. [Fact]
  397. public void DirtyRects_Should_Contain_Old_And_New_Bounds_When_Margin_Changed()
  398. {
  399. using (TestApplication())
  400. {
  401. Decorator decorator;
  402. Border border;
  403. Canvas canvas;
  404. var tree = new TestRoot
  405. {
  406. Width = 100,
  407. Height = 100,
  408. Child = decorator = new Decorator
  409. {
  410. Margin = new Thickness(0, 10, 0, 0),
  411. Child = border = new Border
  412. {
  413. Background = Brushes.Red,
  414. Child = canvas = new Canvas(),
  415. }
  416. }
  417. };
  418. var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
  419. layout.ExecuteInitialLayoutPass(tree);
  420. var scene = new Scene(tree);
  421. var sceneBuilder = new SceneBuilder();
  422. sceneBuilder.UpdateAll(scene, new LayerDirtyRects());
  423. var borderNode = scene.FindNode(border);
  424. var canvasNode = scene.FindNode(canvas);
  425. Assert.Equal(Matrix.CreateTranslation(0, 10), borderNode.Transform);
  426. Assert.Equal(Matrix.CreateTranslation(0, 10), canvasNode.Transform);
  427. decorator.Margin = new Thickness(0, 20, 0, 0);
  428. layout.ExecuteLayoutPass();
  429. scene = scene.Clone();
  430. var dirty = new LayerDirtyRects();
  431. sceneBuilder.Update(scene, decorator, dirty);
  432. var rects = dirty.Single().Value.ToArray();
  433. Assert.Equal(new[] { new Rect(0, 10, 100, 90) }, rects);
  434. }
  435. }
  436. [Fact]
  437. public void Control_With_Transparency_Should_Start_New_Layer()
  438. {
  439. using (TestApplication())
  440. {
  441. Decorator decorator;
  442. Border border;
  443. Canvas canvas;
  444. var tree = new TestRoot
  445. {
  446. Padding = new Thickness(10),
  447. Width = 100,
  448. Height = 120,
  449. Child = decorator = new Decorator
  450. {
  451. Padding = new Thickness(11),
  452. Child = border = new Border
  453. {
  454. Opacity = 0.5,
  455. Background = Brushes.Red,
  456. Padding = new Thickness(12),
  457. Child = canvas = new Canvas(),
  458. }
  459. }
  460. };
  461. var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
  462. layout.ExecuteInitialLayoutPass(tree);
  463. var dirty = new LayerDirtyRects();
  464. var scene = new Scene(tree);
  465. var sceneBuilder = new SceneBuilder();
  466. sceneBuilder.UpdateAll(scene, dirty);
  467. var rootNode = (VisualNode)scene.Root;
  468. var borderNode = (VisualNode)scene.FindNode(border);
  469. var canvasNode = (VisualNode)scene.FindNode(canvas);
  470. Assert.Same(tree, rootNode.LayerRoot);
  471. Assert.Same(border, borderNode.LayerRoot);
  472. Assert.Same(border, canvasNode.LayerRoot);
  473. Assert.Equal(2, dirty.Count());
  474. Assert.Empty(dirty.Select(x => x.Key).Except(new IVisual[] { tree, border }));
  475. border.Opacity = 1;
  476. scene = scene.Clone();
  477. dirty = new LayerDirtyRects();
  478. sceneBuilder.Update(scene, border, dirty);
  479. rootNode = (VisualNode)scene.Root;
  480. borderNode = (VisualNode)scene.FindNode(border);
  481. canvasNode = (VisualNode)scene.FindNode(canvas);
  482. Assert.Same(tree, rootNode.LayerRoot);
  483. Assert.Same(tree, borderNode.LayerRoot);
  484. Assert.Same(tree, canvasNode.LayerRoot);
  485. var rootDirty = dirty[tree];
  486. var borderDirty = dirty[border];
  487. Assert.Equal(1, rootDirty.Count());
  488. Assert.Equal(1, borderDirty.Count());
  489. Assert.Equal(new Rect(21, 21, 58, 78), rootDirty.Single());
  490. Assert.Equal(new Rect(21, 21, 58, 78), borderDirty.Single());
  491. }
  492. }
  493. private IDisposable TestApplication()
  494. {
  495. return UnitTestApplication.Start(
  496. TestServices.MockPlatformRenderInterface.With(
  497. layoutManager: new LayoutManager()));
  498. }
  499. }
  500. }