LayoutableTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. using System;
  2. using Avalonia.Controls;
  3. using Avalonia.Layout;
  4. using Avalonia.UnitTests;
  5. using Moq;
  6. using Xunit;
  7. namespace Avalonia.Base.UnitTests.Layout
  8. {
  9. public class LayoutableTests
  10. {
  11. [Theory]
  12. [InlineData(0, 0, 0, 0, 100, 100)]
  13. [InlineData(10, 0, 0, 0, 90, 100)]
  14. [InlineData(10, 0, 5, 0, 85, 100)]
  15. [InlineData(0, 10, 0, 0, 100, 90)]
  16. [InlineData(0, 10, 0, 5, 100, 85)]
  17. [InlineData(4, 4, 6, 7, 90, 89)]
  18. public void Margin_Is_Applied_To_MeasureOverride_Size(
  19. double l,
  20. double t,
  21. double r,
  22. double b,
  23. double expectedWidth,
  24. double expectedHeight)
  25. {
  26. var target = new TestLayoutable
  27. {
  28. Margin = new Thickness(l, t, r, b),
  29. };
  30. target.Measure(new Size(100, 100));
  31. Assert.Equal(new Size(expectedWidth, expectedHeight), target.MeasureSize);
  32. }
  33. [Theory]
  34. [InlineData(HorizontalAlignment.Stretch, 100)]
  35. [InlineData(HorizontalAlignment.Left, 10)]
  36. [InlineData(HorizontalAlignment.Center, 10)]
  37. [InlineData(HorizontalAlignment.Right, 10)]
  38. public void HorizontalAlignment_Is_Applied_To_ArrangeOverride_Size(
  39. HorizontalAlignment h,
  40. double expectedWidth)
  41. {
  42. var target = new TestLayoutable
  43. {
  44. HorizontalAlignment = h,
  45. };
  46. target.Measure(Size.Infinity);
  47. target.Arrange(new Rect(0, 0, 100, 100));
  48. Assert.Equal(new Size(expectedWidth, 100), target.ArrangeSize);
  49. }
  50. [Theory]
  51. [InlineData(VerticalAlignment.Stretch, 100)]
  52. [InlineData(VerticalAlignment.Top, 10)]
  53. [InlineData(VerticalAlignment.Center, 10)]
  54. [InlineData(VerticalAlignment.Bottom, 10)]
  55. public void VerticalAlignment_Is_Applied_To_ArrangeOverride_Size(
  56. VerticalAlignment v,
  57. double expectedHeight)
  58. {
  59. var target = new TestLayoutable
  60. {
  61. VerticalAlignment = v,
  62. };
  63. target.Measure(Size.Infinity);
  64. target.Arrange(new Rect(0, 0, 100, 100));
  65. Assert.Equal(new Size(100, expectedHeight), target.ArrangeSize);
  66. }
  67. [Theory]
  68. [InlineData(0, 0, 0, 0, 100, 100)]
  69. [InlineData(10, 0, 0, 0, 90, 100)]
  70. [InlineData(10, 0, 5, 0, 85, 100)]
  71. [InlineData(0, 10, 0, 0, 100, 90)]
  72. [InlineData(0, 10, 0, 5, 100, 85)]
  73. [InlineData(4, 4, 6, 7, 90, 89)]
  74. public void Margin_Is_Applied_To_ArrangeOverride_Size(
  75. double l,
  76. double t,
  77. double r,
  78. double b,
  79. double expectedWidth,
  80. double expectedHeight)
  81. {
  82. var target = new TestLayoutable
  83. {
  84. Margin = new Thickness(l, t, r, b),
  85. };
  86. target.Measure(Size.Infinity);
  87. target.Arrange(new Rect(0, 0, 100, 100));
  88. Assert.Equal(new Size(expectedWidth, expectedHeight), target.ArrangeSize);
  89. }
  90. [Fact]
  91. public void Only_Calls_LayoutManager_InvalidateMeasure_Once()
  92. {
  93. var target = new Mock<ILayoutManager>();
  94. var control = new Decorator();
  95. var root = new LayoutTestRoot
  96. {
  97. Child = control,
  98. LayoutManager = target.Object,
  99. };
  100. root.Measure(Size.Infinity);
  101. root.Arrange(new Rect(root.DesiredSize));
  102. target.Invocations.Clear();
  103. control.InvalidateMeasure();
  104. control.InvalidateMeasure();
  105. target.Verify(x => x.InvalidateMeasure(control), Times.Once());
  106. }
  107. [Fact]
  108. public void Only_Calls_LayoutManager_InvalidateArrange_Once()
  109. {
  110. var target = new Mock<ILayoutManager>();
  111. var control = new Decorator();
  112. var root = new LayoutTestRoot
  113. {
  114. Child = control,
  115. LayoutManager = target.Object,
  116. };
  117. root.Measure(Size.Infinity);
  118. root.Arrange(new Rect(root.DesiredSize));
  119. target.Invocations.Clear();
  120. control.InvalidateArrange();
  121. control.InvalidateArrange();
  122. target.Verify(x => x.InvalidateArrange(control), Times.Once());
  123. }
  124. [Fact]
  125. public void Attaching_Control_To_Tree_Invalidates_Parent_Measure()
  126. {
  127. var target = new Mock<ILayoutManager>();
  128. var control = new Decorator();
  129. var root = new LayoutTestRoot
  130. {
  131. Child = control,
  132. LayoutManager = target.Object,
  133. };
  134. root.Measure(Size.Infinity);
  135. root.Arrange(new Rect(root.DesiredSize));
  136. Assert.True(control.IsMeasureValid);
  137. root.Child = null;
  138. root.Measure(Size.Infinity);
  139. root.Arrange(new Rect(root.DesiredSize));
  140. Assert.False(control.IsMeasureValid);
  141. Assert.True(root.IsMeasureValid);
  142. target.Invocations.Clear();
  143. root.Child = control;
  144. Assert.False(root.IsMeasureValid);
  145. Assert.False(control.IsMeasureValid);
  146. target.Verify(x => x.InvalidateMeasure(root), Times.Once());
  147. }
  148. [Fact]
  149. public void LayoutUpdated_Is_Called_At_End_Of_Layout_Pass()
  150. {
  151. Border border1;
  152. Border border2;
  153. var root = new TestRoot
  154. {
  155. Child = border1 = new Border
  156. {
  157. Child = border2 = new Border(),
  158. },
  159. };
  160. var raised = 0;
  161. void ValidateBounds(object sender, EventArgs e)
  162. {
  163. Assert.Equal(new Rect(0, 0, 100, 100), border1.Bounds);
  164. Assert.Equal(new Rect(0, 0, 100, 100), border2.Bounds);
  165. ++raised;
  166. }
  167. root.LayoutUpdated += ValidateBounds;
  168. border1.LayoutUpdated += ValidateBounds;
  169. border2.LayoutUpdated += ValidateBounds;
  170. root.Measure(new Size(100, 100));
  171. root.Arrange(new Rect(0, 0, 100, 100));
  172. root.LayoutManager.ExecuteLayoutPass();
  173. Assert.Equal(3, raised);
  174. Assert.Equal(new Rect(0, 0, 100, 100), border1.Bounds);
  175. Assert.Equal(new Rect(0, 0, 100, 100), border2.Bounds);
  176. }
  177. [Fact]
  178. public void LayoutUpdated_Subscribes_To_LayoutManager()
  179. {
  180. Border target;
  181. var layoutManager = new Mock<ILayoutManager>();
  182. layoutManager.SetupAdd(m => m.LayoutUpdated += (sender, args) => { });
  183. var root = new TestRoot
  184. {
  185. Child = new Border
  186. {
  187. Child = target = new Border(),
  188. },
  189. LayoutManager = layoutManager.Object,
  190. };
  191. void Handler(object sender, EventArgs e) {}
  192. layoutManager.Invocations.Clear();
  193. target.LayoutUpdated += Handler;
  194. layoutManager.VerifyAdd(
  195. x => x.LayoutUpdated += It.IsAny<EventHandler>(),
  196. Times.Once);
  197. layoutManager.Invocations.Clear();
  198. target.LayoutUpdated -= Handler;
  199. layoutManager.VerifyRemove(
  200. x => x.LayoutUpdated -= It.IsAny<EventHandler>(),
  201. Times.Once);
  202. }
  203. [Fact]
  204. public void LayoutManager_LayoutUpdated_Is_Subscribed_When_Attached_To_Tree()
  205. {
  206. Border border1;
  207. var layoutManager = new Mock<ILayoutManager>();
  208. layoutManager.SetupAdd(m => m.LayoutUpdated += (sender, args) => { });
  209. var root = new TestRoot
  210. {
  211. Child = border1 = new Border(),
  212. LayoutManager = layoutManager.Object,
  213. };
  214. var border2 = new Border();
  215. border2.LayoutUpdated += (s, e) => { };
  216. layoutManager.Invocations.Clear();
  217. border1.Child = border2;
  218. layoutManager.VerifyAdd(
  219. x => x.LayoutUpdated += It.IsAny<EventHandler>(),
  220. Times.Once);
  221. }
  222. [Fact]
  223. public void LayoutManager_LayoutUpdated_Is_Unsubscribed_When_Detached_From_Tree()
  224. {
  225. Border border1;
  226. var layoutManager = new Mock<ILayoutManager>();
  227. layoutManager.SetupAdd(m => m.LayoutUpdated += (sender, args) => { });
  228. var root = new TestRoot
  229. {
  230. Child = border1 = new Border(),
  231. LayoutManager = layoutManager.Object,
  232. };
  233. var border2 = new Border();
  234. border2.LayoutUpdated += (s, e) => { };
  235. border1.Child = border2;
  236. layoutManager.Invocations.Clear();
  237. border1.Child = null;
  238. layoutManager.VerifyRemove(
  239. x => x.LayoutUpdated -= It.IsAny<EventHandler>(),
  240. Times.Once);
  241. }
  242. [Fact]
  243. public void Making_Control_Invisible_Should_Invalidate_Parent_Measure()
  244. {
  245. Border child;
  246. var target = new StackPanel
  247. {
  248. Children =
  249. {
  250. (child = new Border
  251. {
  252. Width = 100,
  253. }),
  254. }
  255. };
  256. target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  257. target.Arrange(new Rect(target.DesiredSize));
  258. Assert.True(target.IsMeasureValid);
  259. Assert.True(target.IsArrangeValid);
  260. Assert.True(child.IsMeasureValid);
  261. Assert.True(child.IsArrangeValid);
  262. child.IsVisible = false;
  263. Assert.False(target.IsMeasureValid);
  264. Assert.False(target.IsArrangeValid);
  265. Assert.True(child.IsMeasureValid);
  266. Assert.True(child.IsArrangeValid);
  267. }
  268. [Fact]
  269. public void Making_Control_Visible_Should_Invalidate_Own_And_Parent_Measure()
  270. {
  271. Border child;
  272. var target = new StackPanel
  273. {
  274. Children =
  275. {
  276. (child = new Border
  277. {
  278. Width = 100,
  279. IsVisible = false,
  280. }),
  281. }
  282. };
  283. target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  284. target.Arrange(new Rect(target.DesiredSize));
  285. Assert.True(target.IsMeasureValid);
  286. Assert.True(target.IsArrangeValid);
  287. Assert.True(child.IsMeasureValid);
  288. Assert.False(child.IsArrangeValid);
  289. child.IsVisible = true;
  290. Assert.False(target.IsMeasureValid);
  291. Assert.False(target.IsArrangeValid);
  292. Assert.False(child.IsMeasureValid);
  293. Assert.False(child.IsArrangeValid);
  294. }
  295. [Fact]
  296. public void Measuring_Invisible_Control_Should_Not_Invalidate_Parent_Measure()
  297. {
  298. Border child;
  299. var target = new StackPanel
  300. {
  301. Children =
  302. {
  303. (child = new Border
  304. {
  305. Width = 100,
  306. }),
  307. }
  308. };
  309. target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  310. target.Arrange(new Rect(target.DesiredSize));
  311. Assert.True(target.IsMeasureValid);
  312. Assert.True(target.IsArrangeValid);
  313. Assert.Equal(new Size(100, 0), child.DesiredSize);
  314. child.IsVisible = false;
  315. Assert.Equal(default, child.DesiredSize);
  316. target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  317. target.Arrange(new Rect(target.DesiredSize));
  318. child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  319. Assert.True(target.IsMeasureValid);
  320. Assert.True(target.IsArrangeValid);
  321. Assert.Equal(default, child.DesiredSize);
  322. }
  323. private class TestLayoutable : Layoutable
  324. {
  325. public Size ArrangeSize { get; private set; }
  326. public Size MeasureResult { get; set; } = new Size(10, 10);
  327. public Size MeasureSize { get; private set; }
  328. protected override Size MeasureOverride(Size availableSize)
  329. {
  330. MeasureSize = availableSize;
  331. return MeasureResult;
  332. }
  333. protected override Size ArrangeOverride(Size finalSize)
  334. {
  335. ArrangeSize = finalSize;
  336. return base.ArrangeOverride(finalSize);
  337. }
  338. }
  339. }
  340. }