LayoutableTests.cs 11 KB

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