SceneBuilderTests.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  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. // Initial ClipBounds are correct, make sure they're still correct after updating border.
  129. scene = scene.Clone();
  130. Assert.True(sceneBuilder.Update(scene, border));
  131. borderNode = scene.FindNode(border);
  132. Assert.Equal(new Rect(50, 50, 50, 50), borderNode.ClipBounds);
  133. }
  134. }
  135. [Fact]
  136. public void Should_Respect_ZIndex()
  137. {
  138. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  139. {
  140. Border front;
  141. Border back;
  142. var tree = new TestRoot
  143. {
  144. Child = new Panel
  145. {
  146. Children =
  147. {
  148. (front = new Border
  149. {
  150. ZIndex = 1,
  151. }),
  152. (back = new Border
  153. {
  154. ZIndex = 0,
  155. }),
  156. }
  157. }
  158. };
  159. var result = new Scene(tree);
  160. var sceneBuilder = new SceneBuilder();
  161. sceneBuilder.UpdateAll(result);
  162. var panelNode = result.FindNode(tree.Child);
  163. var expected = new IVisual[] { back, front };
  164. var actual = panelNode.Children.OfType<IVisualNode>().Select(x => x.Visual).ToArray();
  165. Assert.Equal(expected, actual);
  166. }
  167. }
  168. [Fact]
  169. public void ClipBounds_Should_Be_In_Global_Coordinates()
  170. {
  171. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  172. {
  173. Border target;
  174. var tree = new TestRoot
  175. {
  176. Child = new Decorator
  177. {
  178. Margin = new Thickness(24, 26),
  179. Child = target = new Border
  180. {
  181. Margin = new Thickness(26, 24),
  182. Width = 100,
  183. Height = 100,
  184. }
  185. }
  186. };
  187. tree.Measure(Size.Infinity);
  188. tree.Arrange(new Rect(tree.DesiredSize));
  189. var result = new Scene(tree);
  190. var sceneBuilder = new SceneBuilder();
  191. sceneBuilder.UpdateAll(result);
  192. var targetNode = result.FindNode(target);
  193. Assert.Equal(new Rect(50, 50, 100, 100), targetNode.ClipBounds);
  194. }
  195. }
  196. [Fact]
  197. public void Transform_For_Control_With_RenderTransform_Should_Be_Correct_After_Update()
  198. {
  199. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  200. {
  201. Border border;
  202. var tree = new TestRoot
  203. {
  204. Width = 400,
  205. Height = 200,
  206. Child = new Decorator
  207. {
  208. Width = 200,
  209. Height = 100,
  210. Child = border = new Border
  211. {
  212. Background = Brushes.Red,
  213. HorizontalAlignment = HorizontalAlignment.Right,
  214. Width = 100,
  215. RenderTransform = new ScaleTransform(0.5, 1),
  216. }
  217. }
  218. };
  219. tree.Measure(Size.Infinity);
  220. tree.Arrange(new Rect(tree.DesiredSize));
  221. var scene = new Scene(tree);
  222. var sceneBuilder = new SceneBuilder();
  223. sceneBuilder.UpdateAll(scene);
  224. var expectedTransform = Matrix.CreateScale(0.5, 1) * Matrix.CreateTranslation(225, 50);
  225. var borderNode = scene.FindNode(border);
  226. Assert.Equal(expectedTransform, borderNode.Transform);
  227. scene = scene.Clone();
  228. Assert.True(sceneBuilder.Update(scene, border));
  229. borderNode = scene.FindNode(border);
  230. Assert.Equal(expectedTransform, borderNode.Transform);
  231. }
  232. }
  233. [Fact]
  234. public void Should_Update_Border_Background_Node()
  235. {
  236. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  237. {
  238. Border border;
  239. TextBlock textBlock;
  240. var tree = new TestRoot
  241. {
  242. Child = border = new Border
  243. {
  244. Width = 100,
  245. Height = 100,
  246. Background = Brushes.Red,
  247. Child = textBlock = new TextBlock
  248. {
  249. Foreground = Brushes.Green,
  250. Text = "Hello World",
  251. }
  252. }
  253. };
  254. tree.Measure(Size.Infinity);
  255. tree.Arrange(new Rect(tree.DesiredSize));
  256. var initial = new Scene(tree);
  257. var sceneBuilder = new SceneBuilder();
  258. sceneBuilder.UpdateAll(initial);
  259. var initialBackgroundNode = initial.FindNode(border).Children[0];
  260. var initialTextNode = initial.FindNode(textBlock).DrawOperations[0];
  261. Assert.NotNull(initialBackgroundNode);
  262. Assert.NotNull(initialTextNode);
  263. border.Background = Brushes.Green;
  264. var result = initial.Clone();
  265. sceneBuilder.Update(result, border);
  266. var borderNode = (VisualNode)result.Root.Children[0];
  267. Assert.Same(border, borderNode.Visual);
  268. var backgroundNode = (RectangleNode)borderNode.DrawOperations[0];
  269. Assert.NotSame(initialBackgroundNode, backgroundNode);
  270. Assert.Equal(Brushes.Green, backgroundNode.Brush);
  271. var textBlockNode = (VisualNode)borderNode.Children[0];
  272. Assert.Same(textBlock, textBlockNode.Visual);
  273. var textNode = (TextNode)textBlockNode.DrawOperations[0];
  274. Assert.Same(initialTextNode, textNode);
  275. }
  276. }
  277. [Fact]
  278. public void Should_Update_When_Control_Added()
  279. {
  280. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  281. {
  282. Border border;
  283. var tree = new TestRoot
  284. {
  285. Width = 100,
  286. Height = 100,
  287. Child = border = new Border
  288. {
  289. Background = Brushes.Red,
  290. }
  291. };
  292. Canvas canvas;
  293. var decorator = new Decorator
  294. {
  295. Child = canvas = new Canvas(),
  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. border.Child = decorator;
  303. var result = initial.Clone();
  304. Assert.True(sceneBuilder.Update(result, decorator));
  305. // Updating canvas should result in no-op as it should have been updated along
  306. // with decorator as part of the add opeation.
  307. Assert.False(sceneBuilder.Update(result, canvas));
  308. var borderNode = (VisualNode)result.Root.Children[0];
  309. Assert.Equal(1, borderNode.Children.Count);
  310. Assert.Equal(1, borderNode.DrawOperations.Count);
  311. var decoratorNode = (VisualNode)borderNode.Children[0];
  312. Assert.Same(decorator, decoratorNode.Visual);
  313. Assert.Same(decoratorNode, result.FindNode(decorator));
  314. var canvasNode = (VisualNode)decoratorNode.Children[0];
  315. Assert.Same(canvas, canvasNode.Visual);
  316. Assert.Same(canvasNode, result.FindNode(canvas));
  317. }
  318. }
  319. [Fact]
  320. public void Should_Update_When_Control_Removed()
  321. {
  322. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  323. {
  324. Border border;
  325. Decorator decorator;
  326. Canvas canvas;
  327. var tree = new TestRoot
  328. {
  329. Width = 100,
  330. Height = 100,
  331. Child = border = new Border
  332. {
  333. Background = Brushes.Red,
  334. Child = decorator = new Decorator
  335. {
  336. Child = canvas = new Canvas
  337. {
  338. Background = Brushes.AliceBlue,
  339. }
  340. }
  341. }
  342. };
  343. tree.Measure(Size.Infinity);
  344. tree.Arrange(new Rect(tree.DesiredSize));
  345. var initial = new Scene(tree);
  346. var sceneBuilder = new SceneBuilder();
  347. sceneBuilder.UpdateAll(initial);
  348. border.Child = null;
  349. var result = initial.Clone();
  350. Assert.True(sceneBuilder.Update(result, decorator));
  351. Assert.False(sceneBuilder.Update(result, canvas));
  352. var borderNode = (VisualNode)result.Root.Children[0];
  353. Assert.Equal(0, borderNode.Children.Count);
  354. Assert.Equal(1, borderNode.DrawOperations.Count);
  355. Assert.Null(result.FindNode(decorator));
  356. }
  357. }
  358. [Fact]
  359. public void Should_Update_When_Control_Made_Invisible()
  360. {
  361. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  362. {
  363. Decorator decorator;
  364. Border border;
  365. Canvas canvas;
  366. var tree = new TestRoot
  367. {
  368. Width = 100,
  369. Height = 100,
  370. Child = decorator = new Decorator
  371. {
  372. Child = border = new Border
  373. {
  374. Background = Brushes.Red,
  375. Child = canvas = new Canvas(),
  376. }
  377. }
  378. };
  379. tree.Measure(Size.Infinity);
  380. tree.Arrange(new Rect(tree.DesiredSize));
  381. var initial = new Scene(tree);
  382. var sceneBuilder = new SceneBuilder();
  383. sceneBuilder.UpdateAll(initial);
  384. border.IsVisible = false;
  385. var result = initial.Clone();
  386. Assert.True(sceneBuilder.Update(result, border));
  387. Assert.False(sceneBuilder.Update(result, canvas));
  388. var decoratorNode = (VisualNode)result.Root.Children[0];
  389. Assert.Equal(0, decoratorNode.Children.Count);
  390. Assert.Null(result.FindNode(border));
  391. Assert.Null(result.FindNode(canvas));
  392. }
  393. }
  394. [Fact]
  395. public void Should_Update_Descendent_Tranform_When_Margin_Changed()
  396. {
  397. using (TestApplication())
  398. {
  399. Decorator decorator;
  400. Border border;
  401. Canvas canvas;
  402. var tree = new TestRoot
  403. {
  404. Width = 100,
  405. Height = 100,
  406. Child = decorator = new Decorator
  407. {
  408. Margin = new Thickness(0, 10, 0, 0),
  409. Child = border = new Border
  410. {
  411. Child = canvas = new Canvas(),
  412. }
  413. }
  414. };
  415. var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
  416. layout.ExecuteInitialLayoutPass(tree);
  417. var scene = new Scene(tree);
  418. var sceneBuilder = new SceneBuilder();
  419. sceneBuilder.UpdateAll(scene);
  420. var borderNode = scene.FindNode(border);
  421. var canvasNode = scene.FindNode(canvas);
  422. Assert.Equal(Matrix.CreateTranslation(0, 10), borderNode.Transform);
  423. Assert.Equal(Matrix.CreateTranslation(0, 10), canvasNode.Transform);
  424. decorator.Margin = new Thickness(0, 20, 0, 0);
  425. layout.ExecuteLayoutPass();
  426. scene = scene.Clone();
  427. sceneBuilder.Update(scene, decorator);
  428. borderNode = scene.FindNode(border);
  429. canvasNode = scene.FindNode(canvas);
  430. Assert.Equal(Matrix.CreateTranslation(0, 20), borderNode.Transform);
  431. Assert.Equal(Matrix.CreateTranslation(0, 20), canvasNode.Transform);
  432. }
  433. }
  434. [Fact]
  435. public void DirtyRects_Should_Contain_Old_And_New_Bounds_When_Margin_Changed()
  436. {
  437. using (TestApplication())
  438. {
  439. Decorator decorator;
  440. Border border;
  441. Canvas canvas;
  442. var tree = new TestRoot
  443. {
  444. Width = 100,
  445. Height = 100,
  446. Child = decorator = new Decorator
  447. {
  448. Margin = new Thickness(0, 10, 0, 0),
  449. Child = border = new Border
  450. {
  451. Background = Brushes.Red,
  452. Child = canvas = new Canvas(),
  453. }
  454. }
  455. };
  456. var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
  457. layout.ExecuteInitialLayoutPass(tree);
  458. var scene = new Scene(tree);
  459. var sceneBuilder = new SceneBuilder();
  460. sceneBuilder.UpdateAll(scene);
  461. var borderNode = scene.FindNode(border);
  462. var canvasNode = scene.FindNode(canvas);
  463. Assert.Equal(Matrix.CreateTranslation(0, 10), borderNode.Transform);
  464. Assert.Equal(Matrix.CreateTranslation(0, 10), canvasNode.Transform);
  465. decorator.Margin = new Thickness(0, 20, 0, 0);
  466. layout.ExecuteLayoutPass();
  467. scene = scene.Clone();
  468. sceneBuilder.Update(scene, decorator);
  469. var rects = scene.Layers.Single().Dirty.ToArray();
  470. Assert.Equal(new[] { new Rect(0, 10, 100, 90) }, rects);
  471. }
  472. }
  473. [Fact]
  474. public void Resizing_Scene_Should_Add_DirtyRects()
  475. {
  476. using (TestApplication())
  477. {
  478. Decorator decorator;
  479. Border border;
  480. Canvas canvas;
  481. var tree = new TestRoot
  482. {
  483. Child = decorator = new Decorator
  484. {
  485. Margin = new Thickness(0, 10, 0, 0),
  486. Child = border = new Border
  487. {
  488. Opacity = 0.5,
  489. Background = Brushes.Red,
  490. Child = canvas = new Canvas(),
  491. }
  492. }
  493. };
  494. var scene = new Scene(tree);
  495. var sceneBuilder = new SceneBuilder();
  496. sceneBuilder.UpdateAll(scene);
  497. Assert.Equal(new Size(100, 100), scene.Size);
  498. tree.ClientSize = new Size(110, 120);
  499. scene = scene.Clone();
  500. sceneBuilder.Update(scene, tree);
  501. Assert.Equal(new Size(110, 120), scene.Size);
  502. var expected = new[]
  503. {
  504. new Rect(100, 0, 10, 100),
  505. new Rect(0, 100, 110, 20),
  506. };
  507. Assert.Equal(expected, scene.Layers[tree].Dirty.ToArray());
  508. Assert.Equal(expected, scene.Layers[border].Dirty.ToArray());
  509. }
  510. }
  511. [Fact]
  512. public void Should_Set_GeometryClip()
  513. {
  514. using (TestApplication())
  515. {
  516. var clip = StreamGeometry.Parse("M100,0 L0,100 100,100");
  517. Decorator decorator;
  518. var tree = new TestRoot
  519. {
  520. Child = decorator = new Decorator
  521. {
  522. Clip = clip,
  523. }
  524. };
  525. var scene = new Scene(tree);
  526. var sceneBuilder = new SceneBuilder();
  527. sceneBuilder.UpdateAll(scene);
  528. var decoratorNode = scene.FindNode(decorator);
  529. Assert.Same(clip.PlatformImpl, decoratorNode.GeometryClip);
  530. }
  531. }
  532. private IDisposable TestApplication()
  533. {
  534. return UnitTestApplication.Start(
  535. TestServices.MockPlatformRenderInterface.With(
  536. layoutManager: new LayoutManager()));
  537. }
  538. }
  539. }