SceneBuilderTests.cs 16 KB

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