A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Linq;
6using NUnit.Framework;
7using osu.Framework.Extensions;
8using osu.Framework.Extensions.IEnumerableExtensions;
9using osu.Framework.Graphics;
10using osu.Framework.Graphics.Containers;
11using osu.Framework.Graphics.Shapes;
12using osu.Framework.Graphics.Sprites;
13using osu.Framework.Utils;
14using osuTK;
15using osuTK.Graphics;
16
17namespace osu.Framework.Tests.Visual.Layout
18{
19 public class TestSceneGridContainer : FrameworkTestScene
20 {
21 private Container gridParent;
22 private GridContainer grid;
23
24 [SetUp]
25 public void Setup() => Schedule(() =>
26 {
27 Child = gridParent = new Container
28 {
29 Anchor = Anchor.Centre,
30 Origin = Anchor.Centre,
31 RelativeSizeAxes = Axes.Both,
32 Size = new Vector2(0.5f),
33 Children = new Drawable[]
34 {
35 grid = new GridContainer { RelativeSizeAxes = Axes.Both },
36 new Container
37 {
38 RelativeSizeAxes = Axes.Both,
39 Masking = true,
40 BorderColour = Color4.White,
41 BorderThickness = 2,
42 Child = new Box
43 {
44 RelativeSizeAxes = Axes.Both,
45 Alpha = 0,
46 AlwaysPresent = true
47 }
48 }
49 }
50 };
51 });
52
53 [TestCase(false)]
54 [TestCase(true)]
55 public void TestAutoSizeDoesNotConsiderRelativeSizeChildren(bool row)
56 {
57 Box relativeBox = null;
58 Box absoluteBox = null;
59
60 setSingleDimensionContent(() => new[]
61 {
62 new Drawable[]
63 {
64 relativeBox = new FillBox { RelativeSizeAxes = Axes.Both },
65 absoluteBox = new FillBox
66 {
67 RelativeSizeAxes = Axes.None,
68 Size = new Vector2(100)
69 }
70 }
71 }, new[] { new Dimension(GridSizeMode.AutoSize) }, row);
72
73 AddStep("resize absolute box", () => absoluteBox.Size = new Vector2(50));
74 AddAssert("relative box has length 50", () => Precision.AlmostEquals(row ? relativeBox.DrawHeight : relativeBox.DrawWidth, 50, 1));
75 }
76
77 [Test]
78 public void TestBlankGrid()
79 {
80 }
81
82 [Test]
83 public void TestSingleCellDistributedXy()
84 {
85 FillBox box = null;
86 AddStep("set content", () => grid.Content = new[] { new Drawable[] { box = new FillBox() }, });
87 AddAssert("box is same size as grid", () => Precision.AlmostEquals(box.DrawSize, grid.DrawSize));
88 }
89
90 [Test]
91 public void TestSingleCellAbsoluteXy()
92 {
93 const float size = 100;
94
95 FillBox box = null;
96 AddStep("set content", () =>
97 {
98 grid.Content = new[] { new Drawable[] { box = new FillBox() }, };
99 grid.RowDimensions = grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, size) };
100 });
101
102 AddAssert("box has expected size", () => Precision.AlmostEquals(box.DrawSize, new Vector2(size)));
103 }
104
105 [Test]
106 public void TestSingleCellRelativeXy()
107 {
108 const float size = 0.5f;
109
110 FillBox box = null;
111 AddStep("set content", () =>
112 {
113 grid.Content = new[] { new Drawable[] { box = new FillBox() }, };
114 grid.RowDimensions = grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.Relative, size) };
115 });
116
117 AddAssert("box has expected size", () => Precision.AlmostEquals(box.DrawSize, grid.DrawSize * new Vector2(size)));
118 }
119
120 [Test]
121 public void TestSingleCellRelativeXAbsoluteY()
122 {
123 const float absolute_height = 100;
124 const float relative_width = 0.5f;
125
126 FillBox box = null;
127 AddStep("set content", () =>
128 {
129 grid.Content = new[] { new Drawable[] { box = new FillBox() }, };
130 grid.RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, absolute_height) };
131 grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.Relative, relative_width) };
132 });
133
134 AddAssert("box has expected width", () => Precision.AlmostEquals(box.DrawWidth, grid.DrawWidth * relative_width));
135 AddAssert("box has expected height", () => Precision.AlmostEquals(box.DrawHeight, absolute_height));
136 }
137
138 [Test]
139 public void TestSingleCellDistributedXRelativeY()
140 {
141 const float height = 0.5f;
142
143 FillBox box = null;
144 AddStep("set content", () =>
145 {
146 grid.Content = new[] { new Drawable[] { box = new FillBox() }, };
147 grid.RowDimensions = new[] { new Dimension(GridSizeMode.Relative, height) };
148 });
149
150 AddAssert("box has expected width", () => Precision.AlmostEquals(box.DrawWidth, grid.DrawWidth));
151 AddAssert("box has expected height", () => Precision.AlmostEquals(box.DrawHeight, grid.DrawHeight * height));
152 }
153
154 [TestCase(false)]
155 [TestCase(true)]
156 public void Test3CellRowOrColumnDistributedXy(bool row)
157 {
158 FillBox[] boxes = new FillBox[3];
159
160 setSingleDimensionContent(() => new[]
161 {
162 new Drawable[] { boxes[0] = new FillBox(), boxes[1] = new FillBox(), boxes[2] = new FillBox() }
163 }, row: row);
164
165 for (int i = 0; i < 3; i++)
166 {
167 int local = i;
168
169 if (row)
170 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(grid.DrawWidth / 3f, grid.DrawHeight)));
171 else
172 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(grid.DrawWidth, grid.DrawHeight / 3f)));
173 }
174 }
175
176 [TestCase(false)]
177 [TestCase(true)]
178 public void Test3CellRowOrColumnDistributedXyAbsoluteYx(bool row)
179 {
180 var sizes = new[] { 50f, 100f, 75f };
181 var boxes = new FillBox[3];
182
183 setSingleDimensionContent(() => new[]
184 {
185 new Drawable[] { boxes[0] = new FillBox() },
186 new Drawable[] { boxes[1] = new FillBox() },
187 new Drawable[] { boxes[2] = new FillBox() },
188 }, new[]
189 {
190 new Dimension(GridSizeMode.Absolute, sizes[0]),
191 new Dimension(GridSizeMode.Absolute, sizes[1]),
192 new Dimension(GridSizeMode.Absolute, sizes[2])
193 }, row);
194
195 for (int i = 0; i < 3; i++)
196 {
197 int local = i;
198
199 if (row)
200 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(grid.DrawWidth, sizes[local])));
201 else
202 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(sizes[local], grid.DrawHeight)));
203 }
204 }
205
206 [TestCase(false)]
207 [TestCase(true)]
208 public void Test3CellRowOrColumnDistributedXyRelativeYx(bool row)
209 {
210 var sizes = new[] { 0.2f, 0.4f, 0.2f };
211 var boxes = new FillBox[3];
212
213 setSingleDimensionContent(() => new[]
214 {
215 new Drawable[] { boxes[0] = new FillBox() },
216 new Drawable[] { boxes[1] = new FillBox() },
217 new Drawable[] { boxes[2] = new FillBox() },
218 }, new[]
219 {
220 new Dimension(GridSizeMode.Relative, sizes[0]),
221 new Dimension(GridSizeMode.Relative, sizes[1]),
222 new Dimension(GridSizeMode.Relative, sizes[2])
223 }, row);
224
225 for (int i = 0; i < 3; i++)
226 {
227 int local = i;
228
229 if (row)
230 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(grid.DrawWidth, sizes[local] * grid.DrawHeight)));
231 else
232 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(sizes[local] * grid.DrawWidth, grid.DrawHeight)));
233 }
234 }
235
236 [TestCase(false)]
237 [TestCase(true)]
238 public void Test3CellRowOrColumnDistributedXyMixedYx(bool row)
239 {
240 var sizes = new[] { 0.2f, 75f };
241 var boxes = new FillBox[3];
242
243 setSingleDimensionContent(() => new[]
244 {
245 new Drawable[] { boxes[0] = new FillBox() },
246 new Drawable[] { boxes[1] = new FillBox() },
247 new Drawable[] { boxes[2] = new FillBox() },
248 }, new[]
249 {
250 new Dimension(GridSizeMode.Relative, sizes[0]),
251 new Dimension(GridSizeMode.Absolute, sizes[1]),
252 new Dimension(),
253 }, row);
254
255 if (row)
256 {
257 AddAssert("box 0 has correct size", () => Precision.AlmostEquals(boxes[0].DrawSize, new Vector2(grid.DrawWidth, sizes[0] * grid.DrawHeight)));
258 AddAssert("box 1 has correct size", () => Precision.AlmostEquals(boxes[1].DrawSize, new Vector2(grid.DrawWidth, sizes[1])));
259 AddAssert("box 2 has correct size", () => Precision.AlmostEquals(boxes[2].DrawSize, new Vector2(grid.DrawWidth, grid.DrawHeight - boxes[0].DrawHeight - boxes[1].DrawHeight)));
260 }
261 else
262 {
263 AddAssert("box 0 has correct size", () => Precision.AlmostEquals(boxes[0].DrawSize, new Vector2(sizes[0] * grid.DrawWidth, grid.DrawHeight)));
264 AddAssert("box 1 has correct size", () => Precision.AlmostEquals(boxes[1].DrawSize, new Vector2(sizes[1], grid.DrawHeight)));
265 AddAssert("box 2 has correct size", () => Precision.AlmostEquals(boxes[2].DrawSize, new Vector2(grid.DrawWidth - boxes[0].DrawWidth - boxes[1].DrawWidth, grid.DrawHeight)));
266 }
267 }
268
269 [Test]
270 public void Test3X3GridDistributedXy()
271 {
272 var boxes = new FillBox[9];
273
274 AddStep("set content", () =>
275 {
276 grid.Content = new[]
277 {
278 new Drawable[] { boxes[0] = new FillBox(), boxes[1] = new FillBox(), boxes[2] = new FillBox() },
279 new Drawable[] { boxes[3] = new FillBox(), boxes[4] = new FillBox(), boxes[5] = new FillBox() },
280 new Drawable[] { boxes[6] = new FillBox(), boxes[7] = new FillBox(), boxes[8] = new FillBox() }
281 };
282 });
283
284 for (int i = 0; i < 9; i++)
285 {
286 int local = i;
287 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(grid.DrawSize / 3f, boxes[local].DrawSize));
288 }
289 }
290
291 [Test]
292 public void Test3X3GridAbsoluteXy()
293 {
294 var boxes = new FillBox[9];
295
296 var dimensions = new[]
297 {
298 new Dimension(GridSizeMode.Absolute, 50),
299 new Dimension(GridSizeMode.Absolute, 100),
300 new Dimension(GridSizeMode.Absolute, 75)
301 };
302
303 AddStep("set content", () =>
304 {
305 grid.Content = new[]
306 {
307 new Drawable[] { boxes[0] = new FillBox(), boxes[1] = new FillBox(), boxes[2] = new FillBox() },
308 new Drawable[] { boxes[3] = new FillBox(), boxes[4] = new FillBox(), boxes[5] = new FillBox() },
309 new Drawable[] { boxes[6] = new FillBox(), boxes[7] = new FillBox(), boxes[8] = new FillBox() }
310 };
311
312 grid.RowDimensions = grid.ColumnDimensions = dimensions;
313 });
314
315 for (int i = 0; i < 9; i++)
316 {
317 int local = i;
318 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(new Vector2(dimensions[local % 3].Size, dimensions[local / 3].Size), boxes[local].DrawSize));
319 }
320 }
321
322 [Test]
323 public void Test3X3GridRelativeXy()
324 {
325 var boxes = new FillBox[9];
326
327 var dimensions = new[]
328 {
329 new Dimension(GridSizeMode.Relative, 0.2f),
330 new Dimension(GridSizeMode.Relative, 0.4f),
331 new Dimension(GridSizeMode.Relative, 0.2f)
332 };
333
334 AddStep("set content", () =>
335 {
336 grid.Content = new[]
337 {
338 new Drawable[] { boxes[0] = new FillBox(), boxes[1] = new FillBox(), boxes[2] = new FillBox() },
339 new Drawable[] { boxes[3] = new FillBox(), boxes[4] = new FillBox(), boxes[5] = new FillBox() },
340 new Drawable[] { boxes[6] = new FillBox(), boxes[7] = new FillBox(), boxes[8] = new FillBox() }
341 };
342
343 grid.RowDimensions = grid.ColumnDimensions = dimensions;
344 });
345
346 for (int i = 0; i < 9; i++)
347 {
348 int local = i;
349 AddAssert($"box {local} has correct size",
350 () => Precision.AlmostEquals(new Vector2(dimensions[local % 3].Size * grid.DrawWidth, dimensions[local / 3].Size * grid.DrawHeight), boxes[local].DrawSize));
351 }
352 }
353
354 [Test]
355 public void Test3X3GridMixedXy()
356 {
357 var boxes = new FillBox[9];
358
359 var dimensions = new[]
360 {
361 new Dimension(GridSizeMode.Absolute, 50),
362 new Dimension(GridSizeMode.Relative, 0.2f)
363 };
364
365 AddStep("set content", () =>
366 {
367 grid.Content = new[]
368 {
369 new Drawable[] { boxes[0] = new FillBox(), boxes[1] = new FillBox(), boxes[2] = new FillBox() },
370 new Drawable[] { boxes[3] = new FillBox(), boxes[4] = new FillBox(), boxes[5] = new FillBox() },
371 new Drawable[] { boxes[6] = new FillBox(), boxes[7] = new FillBox(), boxes[8] = new FillBox() }
372 };
373
374 grid.RowDimensions = grid.ColumnDimensions = dimensions;
375 });
376
377 // Row 1
378 AddAssert("box 0 has correct size", () => Precision.AlmostEquals(boxes[0].DrawSize, new Vector2(dimensions[0].Size, dimensions[0].Size)));
379 AddAssert("box 1 has correct size", () => Precision.AlmostEquals(boxes[1].DrawSize, new Vector2(grid.DrawWidth * dimensions[1].Size, dimensions[0].Size)));
380 AddAssert("box 2 has correct size", () => Precision.AlmostEquals(boxes[2].DrawSize, new Vector2(grid.DrawWidth - boxes[0].DrawWidth - boxes[1].DrawWidth, dimensions[0].Size)));
381
382 // Row 2
383 AddAssert("box 3 has correct size", () => Precision.AlmostEquals(boxes[3].DrawSize, new Vector2(dimensions[0].Size, grid.DrawHeight * dimensions[1].Size)));
384 AddAssert("box 4 has correct size", () => Precision.AlmostEquals(boxes[4].DrawSize, new Vector2(grid.DrawWidth * dimensions[1].Size, grid.DrawHeight * dimensions[1].Size)));
385 AddAssert("box 5 has correct size",
386 () => Precision.AlmostEquals(boxes[5].DrawSize, new Vector2(grid.DrawWidth - boxes[0].DrawWidth - boxes[1].DrawWidth, grid.DrawHeight * dimensions[1].Size)));
387
388 // Row 3
389 AddAssert("box 6 has correct size", () => Precision.AlmostEquals(boxes[6].DrawSize, new Vector2(dimensions[0].Size, grid.DrawHeight - boxes[3].DrawHeight - boxes[0].DrawHeight)));
390 AddAssert("box 7 has correct size",
391 () => Precision.AlmostEquals(boxes[7].DrawSize, new Vector2(grid.DrawWidth * dimensions[1].Size, grid.DrawHeight - boxes[4].DrawHeight - boxes[1].DrawHeight)));
392 AddAssert("box 8 has correct size",
393 () => Precision.AlmostEquals(boxes[8].DrawSize, new Vector2(grid.DrawWidth - boxes[0].DrawWidth - boxes[1].DrawWidth, grid.DrawHeight - boxes[5].DrawHeight - boxes[2].DrawHeight)));
394 }
395
396 [Test]
397 public void TestGridWithNullRowsAndColumns()
398 {
399 var boxes = new FillBox[4];
400
401 AddStep("set content", () =>
402 {
403 grid.Content = new[]
404 {
405 new Drawable[] { boxes[0] = new FillBox(), null, boxes[1] = new FillBox(), null },
406 null,
407 new Drawable[] { boxes[2] = new FillBox(), null, boxes[3] = new FillBox(), null },
408 null
409 };
410 });
411
412 AddAssert("two extra rows and columns", () =>
413 {
414 for (int i = 0; i < 4; i++)
415 {
416 if (!Precision.AlmostEquals(boxes[i].DrawSize, grid.DrawSize / 4))
417 return false;
418 }
419
420 return true;
421 });
422 }
423
424 [Test]
425 public void TestNestedGrids()
426 {
427 var boxes = new FillBox[4];
428
429 AddStep("set content", () =>
430 {
431 grid.Content = new[]
432 {
433 new Drawable[]
434 {
435 new GridContainer
436 {
437 RelativeSizeAxes = Axes.Both,
438 Content = new[]
439 {
440 new Drawable[] { boxes[0] = new FillBox(), new FillBox(), },
441 new Drawable[] { new FillBox(), boxes[1] = new FillBox(), },
442 }
443 },
444 new FillBox(),
445 },
446 new Drawable[]
447 {
448 new FillBox(),
449 new GridContainer
450 {
451 RelativeSizeAxes = Axes.Both,
452 Content = new[]
453 {
454 new Drawable[] { boxes[2] = new FillBox(), new FillBox(), },
455 new Drawable[] { new FillBox(), boxes[3] = new FillBox(), },
456 }
457 }
458 }
459 };
460 });
461
462 for (int i = 0; i < 4; i++)
463 {
464 int local = i;
465 AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, grid.DrawSize / 4));
466 }
467 }
468
469 [Test]
470 public void TestGridWithAutoSizingCells()
471 {
472 FillBox fillBox = null;
473 var autoSizingChildren = new Drawable[2];
474
475 AddStep("set content", () =>
476 {
477 grid.Content = new[]
478 {
479 new[]
480 {
481 autoSizingChildren[0] = new Box
482 {
483 Anchor = Anchor.Centre,
484 Origin = Anchor.Centre,
485 Size = new Vector2(50, 10)
486 },
487 fillBox = new FillBox(),
488 },
489 new[]
490 {
491 null,
492 autoSizingChildren[1] = new Box
493 {
494 Anchor = Anchor.Centre,
495 Origin = Anchor.Centre,
496 Size = new Vector2(50, 10)
497 },
498 },
499 };
500
501 grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) };
502 grid.RowDimensions = new[]
503 {
504 new Dimension(),
505 new Dimension(GridSizeMode.AutoSize),
506 };
507 });
508
509 AddAssert("fill box has correct size", () => Precision.AlmostEquals(fillBox.DrawSize, new Vector2(grid.DrawWidth - 50, grid.DrawHeight - 10)));
510 AddStep("rotate boxes", () => autoSizingChildren.ForEach(c => c.RotateTo(90)));
511 AddAssert("fill box has resized correctly", () => Precision.AlmostEquals(fillBox.DrawSize, new Vector2(grid.DrawWidth - 10, grid.DrawHeight - 50)));
512 }
513
514 [TestCase(false)]
515 [TestCase(true)]
516 public void TestDimensionsWithMaximumSize(bool row)
517 {
518 var boxes = new FillBox[8];
519
520 var dimensions = new[]
521 {
522 new Dimension(),
523 new Dimension(GridSizeMode.Absolute, 100),
524 new Dimension(GridSizeMode.Distributed, maxSize: 100),
525 new Dimension(),
526 new Dimension(GridSizeMode.Distributed, maxSize: 50),
527 new Dimension(GridSizeMode.Absolute, 100),
528 new Dimension(GridSizeMode.Distributed, maxSize: 80),
529 new Dimension(GridSizeMode.Distributed, maxSize: 150)
530 };
531
532 setSingleDimensionContent(() => new[]
533 {
534 new Drawable[]
535 {
536 boxes[0] = new FillBox(),
537 boxes[1] = new FillBox(),
538 boxes[2] = new FillBox(),
539 boxes[3] = new FillBox(),
540 boxes[4] = new FillBox(),
541 boxes[5] = new FillBox(),
542 boxes[6] = new FillBox(),
543 boxes[7] = new FillBox()
544 },
545 }.Invert(), dimensions, row);
546
547 checkClampedSizes(row, boxes, dimensions);
548 }
549
550 [TestCase(false)]
551 [TestCase(true)]
552 public void TestDimensionsWithMinimumSize(bool row)
553 {
554 var boxes = new FillBox[8];
555
556 var dimensions = new[]
557 {
558 new Dimension(),
559 new Dimension(GridSizeMode.Absolute, 100),
560 new Dimension(GridSizeMode.Distributed, minSize: 100),
561 new Dimension(),
562 new Dimension(GridSizeMode.Distributed, minSize: 50),
563 new Dimension(GridSizeMode.Absolute, 100),
564 new Dimension(GridSizeMode.Distributed, minSize: 80),
565 new Dimension(GridSizeMode.Distributed, minSize: 150)
566 };
567
568 setSingleDimensionContent(() => new[]
569 {
570 new Drawable[]
571 {
572 boxes[0] = new FillBox(),
573 boxes[1] = new FillBox(),
574 boxes[2] = new FillBox(),
575 boxes[3] = new FillBox(),
576 boxes[4] = new FillBox(),
577 boxes[5] = new FillBox(),
578 boxes[6] = new FillBox(),
579 boxes[7] = new FillBox()
580 },
581 }.Invert(), dimensions, row);
582
583 checkClampedSizes(row, boxes, dimensions);
584 }
585
586 [TestCase(false)]
587 [TestCase(true)]
588 public void TestDimensionsWithMinimumAndMaximumSize(bool row)
589 {
590 var boxes = new FillBox[8];
591
592 var dimensions = new[]
593 {
594 new Dimension(),
595 new Dimension(GridSizeMode.Absolute, 100),
596 new Dimension(GridSizeMode.Distributed, minSize: 100),
597 new Dimension(),
598 new Dimension(GridSizeMode.Distributed, maxSize: 50),
599 new Dimension(GridSizeMode.Absolute, 100),
600 new Dimension(GridSizeMode.Distributed, minSize: 80),
601 new Dimension(GridSizeMode.Distributed, maxSize: 150)
602 };
603
604 setSingleDimensionContent(() => new[]
605 {
606 new Drawable[]
607 {
608 boxes[0] = new FillBox(),
609 boxes[1] = new FillBox(),
610 boxes[2] = new FillBox(),
611 boxes[3] = new FillBox(),
612 boxes[4] = new FillBox(),
613 boxes[5] = new FillBox(),
614 boxes[6] = new FillBox(),
615 boxes[7] = new FillBox()
616 },
617 }.Invert(), dimensions, row);
618
619 checkClampedSizes(row, boxes, dimensions);
620 }
621
622 [Test]
623 public void TestCombinedMinimumAndMaximumSize()
624 {
625 AddStep("set content", () =>
626 {
627 gridParent.Masking = false;
628 gridParent.RelativeSizeAxes = Axes.Y;
629 gridParent.Width = 420;
630
631 grid.Content = new[]
632 {
633 new Drawable[]
634 {
635 new FillBox(),
636 new FillBox(),
637 new FillBox(),
638 },
639 };
640
641 grid.ColumnDimensions = new[]
642 {
643 new Dimension(GridSizeMode.Distributed, minSize: 180),
644 new Dimension(GridSizeMode.Distributed, minSize: 50, maxSize: 70),
645 new Dimension(GridSizeMode.Distributed, minSize: 40, maxSize: 70),
646 };
647 });
648
649 AddAssert("content spans grid size", () => Precision.AlmostEquals(grid.DrawWidth, grid.Content[0].Sum(d => d.DrawWidth)));
650 }
651
652 [Test]
653 public void TestCombinedMinimumAndMaximumSize2()
654 {
655 AddStep("set content", () =>
656 {
657 gridParent.Masking = false;
658 gridParent.RelativeSizeAxes = Axes.Y;
659 gridParent.Width = 230;
660
661 grid.Content = new[]
662 {
663 new Drawable[]
664 {
665 new FillBox(),
666 new FillBox(),
667 },
668 };
669
670 grid.ColumnDimensions = new[]
671 {
672 new Dimension(GridSizeMode.Distributed, minSize: 180),
673 new Dimension(GridSizeMode.Distributed, minSize: 40, maxSize: 70),
674 };
675 });
676
677 AddAssert("content spans grid size", () => Precision.AlmostEquals(grid.DrawWidth, grid.Content[0].Sum(d => d.DrawWidth)));
678 }
679
680 [TestCase(true)]
681 [TestCase(false)]
682 public void TestAutoSizedCellsWithTransparentContent(bool alwaysPresent)
683 {
684 AddStep("set content", () =>
685 {
686 grid.RowDimensions = new[]
687 {
688 new Dimension(),
689 new Dimension(),
690 new Dimension(GridSizeMode.AutoSize)
691 };
692 grid.ColumnDimensions = new[]
693 {
694 new Dimension(),
695 new Dimension(GridSizeMode.AutoSize),
696 new Dimension()
697 };
698 grid.Content = new[]
699 {
700 new Drawable[] { new FillBox(), transparentBox(alwaysPresent), new FillBox() },
701 new Drawable[] { new FillBox(), transparentBox(alwaysPresent), new FillBox() },
702 new Drawable[] { transparentBox(alwaysPresent), transparentBox(alwaysPresent), transparentBox(alwaysPresent) }
703 };
704 });
705
706 float desiredTransparentBoxSize = alwaysPresent ? 50 : 0;
707 AddAssert("non-transparent fill boxes have correct size", () =>
708 grid.Content
709 .SelectMany(row => row)
710 .Where(box => box.Alpha > 0)
711 .All(box => Precision.AlmostEquals(box.DrawWidth, (grid.DrawWidth - desiredTransparentBoxSize) / 2)
712 && Precision.AlmostEquals(box.DrawHeight, (grid.DrawHeight - desiredTransparentBoxSize) / 2)));
713 }
714
715 [TestCase(true)]
716 [TestCase(false)]
717 public void TestAutoSizedRowOrColumnWithTransparentContent(bool row)
718 {
719 var boxes = new FillBox[5];
720
721 var dimensions = new[]
722 {
723 new Dimension(GridSizeMode.Absolute, 100f),
724 new Dimension(),
725 new Dimension(GridSizeMode.AutoSize),
726 new Dimension(GridSizeMode.Relative, 0.2f),
727 new Dimension()
728 };
729
730 setSingleDimensionContent(() => new[]
731 {
732 new Drawable[]
733 {
734 boxes[0] = new FillBox(),
735 boxes[1] = new FillBox(),
736 boxes[2] = transparentBox(false),
737 boxes[3] = new FillBox(),
738 boxes[4] = new FillBox()
739 }
740 }.Invert(), dimensions, row);
741
742 AddAssert("box 0 has correct size", () => Precision.AlmostEquals(getDimension(boxes[0], row), 100f));
743 AddAssert("box 1 has correct size", () =>
744 Precision.AlmostEquals(getDimension(boxes[1], row), (getDimension(grid, row) * 0.8f - 100f) / 2));
745 AddAssert("box 3 has correct size", () => Precision.AlmostEquals(getDimension(boxes[3], row), getDimension(grid, row) * 0.2f));
746 AddAssert("box 4 has correct size", () =>
747 Precision.AlmostEquals(getDimension(boxes[4], row), (getDimension(grid, row) * 0.8f - 100f) / 2));
748 }
749
750 private FillBox transparentBox(bool alwaysPresent) => new FillBox
751 {
752 Alpha = 0,
753 AlwaysPresent = alwaysPresent,
754 RelativeSizeAxes = Axes.None,
755 Size = new Vector2(50)
756 };
757
758 [TestCase(true)]
759 [TestCase(false)]
760 public void TestAutoSizedRowOrColumnWithDelayedLifetimeContent(bool row)
761 {
762 var boxes = new FillBox[3];
763
764 var dimensions = new[]
765 {
766 new Dimension(GridSizeMode.Absolute, 75f),
767 new Dimension(GridSizeMode.AutoSize),
768 new Dimension()
769 };
770
771 setSingleDimensionContent(() => new[]
772 {
773 new Drawable[]
774 {
775 boxes[0] = new FillBox(),
776 boxes[1] = new FillBox
777 {
778 RelativeSizeAxes = Axes.None,
779 LifetimeStart = double.MaxValue,
780 Size = new Vector2(50)
781 },
782 boxes[2] = new FillBox()
783 }
784 }.Invert(), dimensions, row);
785
786 AddAssert("box 0 has correct size", () => Precision.AlmostEquals(getDimension(boxes[0], row), 75f));
787 AddAssert("box 2 has correct size", () => Precision.AlmostEquals(getDimension(boxes[2], row), getDimension(grid, row) - 75f));
788
789 AddStep("make box 1 alive", () => boxes[1].LifetimeStart = Time.Current);
790 AddUntilStep("wait for alive", () => boxes[1].IsAlive);
791
792 AddAssert("box 0 has correct size", () => Precision.AlmostEquals(getDimension(boxes[0], row), 75f));
793 AddAssert("box 2 has correct size", () => Precision.AlmostEquals(getDimension(boxes[2], row), getDimension(grid, row) - 125f));
794 }
795
796 private bool gridContentChangeEventWasFired;
797
798 [Test]
799 public void TestSetContentByIndex()
800 {
801 setSingleDimensionContent(() => new[]
802 {
803 new Drawable[]
804 {
805 new FillBox(),
806 new FillBox()
807 },
808 new Drawable[]
809 {
810 new FillBox(),
811 new FillBox()
812 }
813 });
814
815 AddStep("Subscribe to event", () => grid.Content.ArrayElementChanged += () => gridContentChangeEventWasFired = true);
816
817 AddStep("Replace bottom right box with a SpriteText", () =>
818 {
819 gridContentChangeEventWasFired = false;
820 grid.Content[1][1] = new SpriteText { Text = "test" };
821 });
822 assertContentChangeEventWasFired();
823 AddAssert("[1][1] cell contains a SpriteText", () => grid.Content[1][1].GetType() == typeof(SpriteText));
824
825 AddStep("Replace top line with [SpriteText][null]", () =>
826 {
827 gridContentChangeEventWasFired = false;
828 grid.Content[0] = new Drawable[] { new SpriteText { Text = "test" }, null };
829 });
830 assertContentChangeEventWasFired();
831 AddAssert("[0][0] cell contains a SpriteText", () => grid.Content[0][0].GetType() == typeof(SpriteText));
832 AddAssert("[0][1] cell contains null", () => grid.Content[0][1] == null);
833
834 void assertContentChangeEventWasFired() => AddAssert("Content change event was fired", () => gridContentChangeEventWasFired);
835 }
836
837 /// <summary>
838 /// Returns drawable dimension along desired axis.
839 /// </summary>
840 private float getDimension(Drawable drawable, bool row) => row ? drawable.DrawHeight : drawable.DrawWidth;
841
842 private void checkClampedSizes(bool row, FillBox[] boxes, Dimension[] dimensions)
843 {
844 AddAssert("sizes not over/underflowed", () =>
845 {
846 for (int i = 0; i < 8; i++)
847 {
848 if (dimensions[i].Mode != GridSizeMode.Distributed)
849 continue;
850
851 if (row && (boxes[i].DrawHeight > dimensions[i].MaxSize || boxes[i].DrawHeight < dimensions[i].MinSize))
852 return false;
853
854 if (!row && (boxes[i].DrawWidth > dimensions[i].MaxSize || boxes[i].DrawWidth < dimensions[i].MinSize))
855 return false;
856 }
857
858 return true;
859 });
860
861 AddAssert("column span total length", () =>
862 {
863 float expectedSize = row ? grid.DrawHeight : grid.DrawWidth;
864 float totalSize = row ? boxes.Sum(b => b.DrawHeight) : boxes.Sum(b => b.DrawWidth);
865
866 // Allowed to exceed the length of the columns due to absolute sizing
867 return totalSize >= expectedSize;
868 });
869 }
870
871 private void setSingleDimensionContent(Func<Drawable[][]> contentFunc, Dimension[] dimensions = null, bool row = false) => AddStep("set content", () =>
872 {
873 var content = contentFunc();
874
875 if (!row)
876 content = content.Invert();
877
878 grid.Content = content;
879
880 if (dimensions == null)
881 return;
882
883 if (row)
884 grid.RowDimensions = dimensions;
885 else
886 grid.ColumnDimensions = dimensions;
887 });
888
889 private class FillBox : Box
890 {
891 public FillBox()
892 {
893 RelativeSizeAxes = Axes.Both;
894 Colour = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1);
895 }
896 }
897 }
898}