SceneBuilderTests.cs 22 KB

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