SceneBuilderTests.cs 18 KB

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