SceneBuilderTests.cs 24 KB

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