SceneBuilderTests.cs 24 KB

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