UniformGridTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. using System;
  2. using Avalonia.Controls.Primitives;
  3. using Avalonia.UnitTests;
  4. using Xunit;
  5. namespace Avalonia.Controls.UnitTests.Primitives
  6. {
  7. public class UniformGridTests : ScopedTestBase
  8. {
  9. [Fact]
  10. public void Grid_Columns_Equals_Rows_For_Auto_Columns_And_Rows()
  11. {
  12. var target = new UniformGrid
  13. {
  14. Children =
  15. {
  16. new Border { Width = 50, Height = 70 },
  17. new Border { Width = 30, Height = 50 },
  18. new Border { Width = 80, Height = 90 }
  19. }
  20. };
  21. target.Measure(Size.Infinity);
  22. target.Arrange(new Rect(target.DesiredSize));
  23. // 2 * 2 grid => each cell: 80 x 90
  24. // Final size => (2 * 80) x (2 * 90) = 160 x 180
  25. Assert.Equal(new Size(160, 180), target.Bounds.Size);
  26. }
  27. [Fact]
  28. public void Grid_Expands_Vertically_For_Columns_With_Auto_Rows()
  29. {
  30. var target = new UniformGrid
  31. {
  32. Columns = 2,
  33. Children =
  34. {
  35. new Border { Width = 50, Height = 70 },
  36. new Border { Width = 30, Height = 50 },
  37. new Border { Width = 80, Height = 90 },
  38. new Border { Width = 20, Height = 30 },
  39. new Border { Width = 40, Height = 60 }
  40. }
  41. };
  42. target.Measure(Size.Infinity);
  43. target.Arrange(new Rect(target.DesiredSize));
  44. // 2 * 3 grid => each cell: 80 x 90
  45. // Final size => (2 * 80) x (3 * 90) = 160 x 270
  46. Assert.Equal(new Size(160, 270), target.Bounds.Size);
  47. }
  48. [Fact]
  49. public void Grid_Extends_For_Columns_And_First_Column_With_Auto_Rows()
  50. {
  51. var target = new UniformGrid
  52. {
  53. Columns = 3,
  54. FirstColumn = 2,
  55. Children =
  56. {
  57. new Border { Width = 50, Height = 70 },
  58. new Border { Width = 30, Height = 50 },
  59. new Border { Width = 80, Height = 90 },
  60. new Border { Width = 20, Height = 30 },
  61. new Border { Width = 40, Height = 60 }
  62. }
  63. };
  64. target.Measure(Size.Infinity);
  65. target.Arrange(new Rect(target.DesiredSize));
  66. // 3 * 3 grid => each cell: 80 x 90
  67. // Final size => (3 * 80) x (3 * 90) = 240 x 270
  68. Assert.Equal(new Size(240, 270), target.Bounds.Size);
  69. }
  70. [Fact]
  71. public void Grid_Expands_Horizontally_For_Rows_With_Auto_Columns()
  72. {
  73. var target = new UniformGrid
  74. {
  75. Rows = 2,
  76. Children =
  77. {
  78. new Border { Width = 50, Height = 70 },
  79. new Border { Width = 30, Height = 50 },
  80. new Border { Width = 80, Height = 90 },
  81. new Border { Width = 20, Height = 30 },
  82. new Border { Width = 40, Height = 60 }
  83. }
  84. };
  85. target.Measure(Size.Infinity);
  86. target.Arrange(new Rect(target.DesiredSize));
  87. // 3 * 2 grid => each cell: 80 x 90
  88. // Final size => (3 * 80) x (2 * 90) = 240 x 180
  89. Assert.Equal(new Size(240, 180), target.Bounds.Size);
  90. }
  91. [Fact]
  92. public void Grid_Size_Is_Limited_By_Rows_And_Columns()
  93. {
  94. var target = new UniformGrid
  95. {
  96. Columns = 2,
  97. Rows = 2,
  98. Children =
  99. {
  100. new Border { Width = 50, Height = 70 },
  101. new Border { Width = 30, Height = 50 },
  102. new Border { Width = 80, Height = 90 },
  103. new Border { Width = 20, Height = 30 },
  104. new Border { Width = 40, Height = 60 }
  105. }
  106. };
  107. target.Measure(Size.Infinity);
  108. target.Arrange(new Rect(target.DesiredSize));
  109. // 2 * 2 grid => each cell: 80 x 90
  110. // Final size => (2 * 80) x (2 * 90) = 160 x 180
  111. Assert.Equal(new Size(160, 180), target.Bounds.Size);
  112. }
  113. [Fact]
  114. public void Not_Visible_Children_Are_Ignored()
  115. {
  116. var target = new UniformGrid
  117. {
  118. Children =
  119. {
  120. new Border { Width = 50, Height = 70 },
  121. new Border { Width = 30, Height = 50 },
  122. new Border { Width = 80, Height = 90, IsVisible = false },
  123. new Border { Width = 20, Height = 30 },
  124. new Border { Width = 40, Height = 60 }
  125. }
  126. };
  127. target.Measure(Size.Infinity);
  128. target.Arrange(new Rect(target.DesiredSize));
  129. // Visible children: 4
  130. // Auto => 2 x 2 grid => each cell: 50 x 70
  131. // Final size => (2 * 50) x (2 * 70) = 100 x 140
  132. Assert.Equal(new Size(100, 140), target.Bounds.Size);
  133. }
  134. //
  135. // New tests to cover RowSpacing and ColumnSpacing
  136. //
  137. [Fact]
  138. public void Grid_Respects_ColumnSpacing_For_Auto_Columns_And_Rows()
  139. {
  140. // We have 3 visible children and no fixed Rows/Columns => 2x2 grid
  141. // Largest child is 80 x 90. ColumnSpacing = 10, RowSpacing = 0
  142. var target = new UniformGrid
  143. {
  144. ColumnSpacing = 10,
  145. Children =
  146. {
  147. new Border { Width = 50, Height = 70 },
  148. new Border { Width = 30, Height = 50 },
  149. new Border { Width = 80, Height = 90 }
  150. }
  151. };
  152. target.Measure(Size.Infinity);
  153. target.Arrange(new Rect(target.DesiredSize));
  154. // Without spacing => width = 2*80 = 160, height = 2*90 = 180
  155. // With columnSpacing=10 => total width = 2*80 + 1*10 = 170
  156. // RowSpacing=0 => total height = 180
  157. Assert.Equal(new Size(170, 180), target.Bounds.Size);
  158. }
  159. [Fact]
  160. public void Grid_Respects_RowSpacing_For_Auto_Columns_And_Rows()
  161. {
  162. // 3 visible children => 2x2 grid again
  163. // Largest child is 80 x 90. RowSpacing = 15, ColumnSpacing = 0
  164. var target = new UniformGrid
  165. {
  166. RowSpacing = 15,
  167. Children =
  168. {
  169. new Border { Width = 50, Height = 70 },
  170. new Border { Width = 30, Height = 50 },
  171. new Border { Width = 80, Height = 90 }
  172. }
  173. };
  174. target.Measure(Size.Infinity);
  175. target.Arrange(new Rect(target.DesiredSize));
  176. // Without spacing => width = 160, height = 180
  177. // With rowSpacing=15 => total height = 2*90 + 1*15 = 195
  178. // ColumnSpacing=0 => total width = 160
  179. Assert.Equal(new Size(160, 195), target.Bounds.Size);
  180. }
  181. [Fact]
  182. public void Grid_Respects_Both_Row_And_Column_Spacing_For_Fixed_Grid()
  183. {
  184. // 4 visible children => 2 rows x 2 columns, each child is 50x70 or 80x90
  185. // We'll fix the Grid to 2x2 so the largest child dictates the cell size: 80x90
  186. // RowSpacing=10, ColumnSpacing=5
  187. var target = new UniformGrid
  188. {
  189. Rows = 2,
  190. Columns = 2,
  191. RowSpacing = 10,
  192. ColumnSpacing = 5,
  193. Children =
  194. {
  195. new Border { Width = 50, Height = 70 },
  196. new Border { Width = 30, Height = 50 },
  197. new Border { Width = 80, Height = 90 },
  198. new Border { Width = 20, Height = 30 },
  199. }
  200. };
  201. target.Measure(Size.Infinity);
  202. target.Arrange(new Rect(target.DesiredSize));
  203. // Each cell = 80 x 90
  204. // Final width = (2 * 80) + (1 * 5) = 160 + 5 = 165
  205. // Final height = (2 * 90) + (1 * 10) = 180 + 10 = 190
  206. Assert.Equal(new Size(165, 190), target.Bounds.Size);
  207. }
  208. [Fact]
  209. public void Grid_Respects_Spacing_When_Invisible_Child_Exists()
  210. {
  211. // 3 *visible* children => auto => 2x2 grid
  212. // Largest child is 80 x 90.
  213. // Add spacing so we can confirm it doesn't add extra columns/rows for invisible child.
  214. var target = new UniformGrid
  215. {
  216. RowSpacing = 5,
  217. ColumnSpacing = 5,
  218. Children =
  219. {
  220. new Border { Width = 50, Height = 70 },
  221. new Border { Width = 80, Height = 90, IsVisible = false },
  222. new Border { Width = 30, Height = 50 },
  223. new Border { Width = 40, Height = 60 }
  224. }
  225. };
  226. // Visible children: 3 => auto => sqrt(3) => 2x2
  227. // Largest visible child is 50x70 or 30x50 or 40x60 => the biggest is 50x70
  228. // Actually, let's ensure we have a child bigger than that:
  229. // (So let's modify the 40x60 to something bigger than 50x70, e.g. 80x90 for clarity)
  230. // We'll do that in the collection above if needed, but let's keep as is for example.
  231. target.Measure(Size.Infinity);
  232. target.Arrange(new Rect(target.DesiredSize));
  233. // The largest visible child is 50x70. So each cell is 50x70.
  234. // For a 2x2 grid with 3 visible children:
  235. // - total width = (2 * 50) + (1 * 5) = 100 + 5 = 105
  236. // - total height = (2 * 70) + (1 * 5) = 140 + 5 = 145
  237. Assert.Equal(new Size(105, 145), target.Bounds.Size);
  238. }
  239. /// <summary>
  240. /// Exposes MeasureOverride for testing inherited classes
  241. /// </summary>
  242. public class UniformGridExposeMeasureOverride : UniformGrid
  243. {
  244. public new Size MeasureOverride(Size availableSize)
  245. {
  246. return base.MeasureOverride(availableSize);
  247. }
  248. }
  249. [Fact]
  250. public void Measure_WithRowsAndColumnsZeroAndNonZeroSpacing_ProducesZeroDesiredSize()
  251. {
  252. // MeasureOverride() is called by Layoutable.MeasureCore() and it ensures that
  253. // the desired size is never negative. but in case of inherited classes MeasureOverride() may return negative values.
  254. var target = new UniformGridExposeMeasureOverride
  255. {
  256. Rows = 0,
  257. Columns = 0,
  258. RowSpacing = 10,
  259. ColumnSpacing = 20
  260. };
  261. var availableSize = new Size(100, 100);
  262. var desiredSize = target.MeasureOverride(availableSize);
  263. // Fail case:
  264. // Because _rows and _columns are 0, the calculation becomes:
  265. // totalWidth = maxWidth * 0 + ColumnSpacing * (0 - 1) = -ColumnSpacing
  266. // totalHeight = maxHeight * 0 + RowSpacing * (0 - 1) = -RowSpacing
  267. // Expected: (0, 0)
  268. Assert.Equal(0, desiredSize.Width);
  269. Assert.Equal(0, desiredSize.Height);
  270. }
  271. [Fact]
  272. public void Arrange_Does_Not_Throw_InvalidOperationException_When_Row_Spacing_Takes_All_Available_Height()
  273. {
  274. // Minimum required height = 20 (2 row gaps size 10)
  275. // Provide height of 19 so that row gaps take all available space
  276. // thus, available height for children may be negative.
  277. // In that case, UniformGrid should arrange its children with rects of height 0.
  278. var target = new UniformGrid
  279. {
  280. Columns = 1,
  281. RowSpacing = 10,
  282. Children =
  283. {
  284. new Border(),
  285. new Border(),
  286. new Border()
  287. }
  288. };
  289. var availableSize = new Size(100, 19);
  290. target.Measure(Size.Infinity);
  291. // Fail case:
  292. // Invalid operation will be thrown if any child rect contains a negative dimension
  293. try
  294. {
  295. target.Arrange(new Rect(availableSize));
  296. }
  297. catch (InvalidOperationException exception)
  298. {
  299. Assert.Fail("Arrange threw InvalidOperationException: " + exception.Message);
  300. }
  301. }
  302. [Fact]
  303. public void Arrange_Does_Not_Throw_InvalidOperationException_When_Column_Spacing_Takes_All_Available_Width()
  304. {
  305. // Minimum required width = 20 (2 row gaps size 10)
  306. // Provide width of 19 so that column gaps take all available space
  307. // thus, available width for children may be negative.
  308. // In that case, UniformGrid should arrange its children with rects of width 0.
  309. var target = new UniformGrid
  310. {
  311. Rows = 1,
  312. ColumnSpacing = 10,
  313. Children =
  314. {
  315. new Border(),
  316. new Border(),
  317. new Border()
  318. }
  319. };
  320. var availableSize = new Size(19, 100);
  321. target.Measure(Size.Infinity);
  322. // Fail case:
  323. // Invalid operation will be thrown if any child rect contains a negative dimension
  324. try
  325. {
  326. target.Arrange(new Rect(availableSize));
  327. }
  328. catch (InvalidOperationException exception)
  329. {
  330. Assert.Fail("Arrange threw InvalidOperationException: " + exception.Message);
  331. }
  332. }
  333. }
  334. }