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 System.Reflection;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.Shapes;
10using osu.Framework.Graphics.Sprites;
11using osu.Framework.Graphics.UserInterface;
12using osu.Framework.Localisation;
13using osu.Framework.Utils;
14using osu.Framework.Threading;
15using osuTK;
16using osuTK.Graphics;
17
18namespace osu.Framework.Tests.Visual.Layout
19{
20 public class TestSceneFillFlowContainer : FrameworkTestScene
21 {
22 private FillDirectionDropdown selectionDropdown;
23
24 private Anchor childAnchor = Anchor.TopLeft;
25 private AnchorDropdown anchorDropdown;
26
27 private Anchor childOrigin = Anchor.TopLeft;
28 private AnchorDropdown originDropdown;
29
30 private FillFlowContainer fillContainer;
31 private ScheduledDelegate scheduledAdder;
32 private bool doNotAddChildren;
33
34 public TestSceneFillFlowContainer()
35 {
36 reset();
37 }
38
39 private void reset()
40 {
41 doNotAddChildren = false;
42 scheduledAdder?.Cancel();
43
44 Child = new Container
45 {
46 RelativeSizeAxes = Axes.Both,
47 Width = 0.2f,
48 Anchor = Anchor.TopRight,
49 Origin = Anchor.TopRight,
50 Depth = float.MinValue,
51 Children = new[]
52 {
53 new FillFlowContainer
54 {
55 Direction = FillDirection.Vertical,
56 RelativeSizeAxes = Axes.X,
57 AutoSizeAxes = Axes.Y,
58 Children = new Drawable[]
59 {
60 new SpriteText { Text = @"Fill mode" },
61 selectionDropdown = new FillDirectionDropdown
62 {
63 RelativeSizeAxes = Axes.X,
64 Items = (FlowTestType[])Enum.GetValues(typeof(FlowTestType)),
65 },
66 new SpriteText { Text = @"Child anchor" },
67 anchorDropdown = new AnchorDropdown
68 {
69 RelativeSizeAxes = Axes.X,
70 Items = new[]
71 {
72 Anchor.TopLeft,
73 Anchor.TopCentre,
74 Anchor.TopRight,
75 Anchor.CentreLeft,
76 Anchor.Centre,
77 Anchor.CentreRight,
78 Anchor.BottomLeft,
79 Anchor.BottomCentre,
80 Anchor.BottomRight,
81 },
82 },
83 new SpriteText { Text = @"Child origin" },
84 originDropdown = new AnchorDropdown
85 {
86 RelativeSizeAxes = Axes.X,
87 Items = new[]
88 {
89 Anchor.TopLeft,
90 Anchor.TopCentre,
91 Anchor.TopRight,
92 Anchor.CentreLeft,
93 Anchor.Centre,
94 Anchor.CentreRight,
95 Anchor.BottomLeft,
96 Anchor.BottomCentre,
97 Anchor.BottomRight,
98 },
99 },
100 }
101 }
102 }
103 };
104
105 selectionDropdown.Current.ValueChanged += e => changeTest(e.NewValue);
106 buildTest();
107 selectionDropdown.Current.Value = FlowTestType.Full;
108 changeTest(FlowTestType.Full);
109 }
110
111 protected override void Update()
112 {
113 base.Update();
114
115 if (childAnchor != anchorDropdown.Current.Value)
116 {
117 childAnchor = anchorDropdown.Current.Value;
118 foreach (var child in fillContainer.Children)
119 child.Anchor = childAnchor;
120 }
121
122 if (childOrigin != originDropdown.Current.Value)
123 {
124 childOrigin = originDropdown.Current.Value;
125 foreach (var child in fillContainer.Children)
126 child.Origin = childOrigin;
127 }
128 }
129
130 private void changeTest(FlowTestType testType)
131 {
132 var method =
133 GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(m => m.GetCustomAttribute<FlowTestCaseAttribute>()?.TestType == testType);
134 if (method != null)
135 method.Invoke(this, Array.Empty<object>());
136 }
137
138 private void buildTest()
139 {
140 Add(new Container
141 {
142 Padding = new MarginPadding(25f),
143 RelativeSizeAxes = Axes.Both,
144 Children = new Drawable[]
145 {
146 fillContainer = new FillFlowContainer
147 {
148 RelativeSizeAxes = Axes.Both,
149 AutoSizeAxes = Axes.None,
150 },
151 new Box
152 {
153 Anchor = Anchor.CentreLeft,
154 Origin = Anchor.Centre,
155 RelativeSizeAxes = Axes.Y,
156 Size = new Vector2(3, 1),
157 Colour = Color4.HotPink,
158 },
159 new Box
160 {
161 Anchor = Anchor.CentreRight,
162 Origin = Anchor.Centre,
163 RelativeSizeAxes = Axes.Y,
164 Size = new Vector2(3, 1),
165 Colour = Color4.HotPink,
166 },
167 new Box
168 {
169 Anchor = Anchor.TopCentre,
170 Origin = Anchor.Centre,
171 RelativeSizeAxes = Axes.X,
172 Size = new Vector2(1, 3),
173 Colour = Color4.HotPink,
174 },
175 new Box
176 {
177 Anchor = Anchor.BottomCentre,
178 Origin = Anchor.Centre,
179 RelativeSizeAxes = Axes.X,
180 Size = new Vector2(1, 3),
181 Colour = Color4.HotPink,
182 }
183 }
184 });
185
186 AddToggleStep("Rotate Container", state => { fillContainer.RotateTo(state ? 45f : 0, 1000); });
187 AddToggleStep("Scale Container", state => { fillContainer.ScaleTo(state ? 1.2f : 1f, 1000); });
188 AddToggleStep("Shear Container", state => { fillContainer.Shear = state ? new Vector2(0.5f, 0f) : new Vector2(0f, 0f); });
189 AddToggleStep("Center Container Anchor", state => { fillContainer.Anchor = state ? Anchor.Centre : Anchor.TopLeft; });
190 AddToggleStep("Center Container Origin", state => { fillContainer.Origin = state ? Anchor.Centre : Anchor.TopLeft; });
191 AddToggleStep("Autosize Container", state =>
192 {
193 if (state)
194 {
195 fillContainer.RelativeSizeAxes = Axes.None;
196 fillContainer.AutoSizeAxes = Axes.Both;
197 }
198 else
199 {
200 fillContainer.AutoSizeAxes = Axes.None;
201 fillContainer.RelativeSizeAxes = Axes.Both;
202 fillContainer.Width = 1;
203 fillContainer.Height = 1;
204 }
205 });
206 AddToggleStep("Rotate children", state =>
207 {
208 if (state)
209 {
210 foreach (var child in fillContainer.Children)
211 child.RotateTo(45f, 1000);
212 }
213 else
214 {
215 foreach (var child in fillContainer.Children)
216 child.RotateTo(0f, 1000);
217 }
218 });
219 AddToggleStep("Shear children", state =>
220 {
221 if (state)
222 {
223 foreach (var child in fillContainer.Children)
224 child.Shear = new Vector2(0.2f, 0.2f);
225 }
226 else
227 {
228 foreach (var child in fillContainer.Children)
229 child.Shear = Vector2.Zero;
230 }
231 });
232 AddToggleStep("Scale children", state =>
233 {
234 if (state)
235 {
236 foreach (var child in fillContainer.Children)
237 child.ScaleTo(1.25f, 1000);
238 }
239 else
240 {
241 foreach (var child in fillContainer.Children)
242 child.ScaleTo(1f, 1000);
243 }
244 });
245 AddToggleStep("Randomly scale children", state =>
246 {
247 if (state)
248 {
249 foreach (var child in fillContainer.Children)
250 child.ScaleTo(RNG.NextSingle(1, 2), 1000);
251 }
252 else
253 {
254 foreach (var child in fillContainer.Children)
255 child.ScaleTo(1f, 1000);
256 }
257 });
258 AddToggleStep("Randomly set child origins", state =>
259 {
260 if (state)
261 {
262 foreach (var child in fillContainer.Children)
263 {
264 switch (RNG.Next(9))
265 {
266 case 0:
267 child.Origin = Anchor.TopLeft;
268 break;
269
270 case 1:
271 child.Origin = Anchor.TopCentre;
272 break;
273
274 case 2:
275 child.Origin = Anchor.TopRight;
276 break;
277
278 case 3:
279 child.Origin = Anchor.CentreLeft;
280 break;
281
282 case 4:
283 child.Origin = Anchor.Centre;
284 break;
285
286 case 5:
287 child.Origin = Anchor.CentreRight;
288 break;
289
290 case 6:
291 child.Origin = Anchor.BottomLeft;
292 break;
293
294 case 7:
295 child.Origin = Anchor.BottomCentre;
296 break;
297
298 case 8:
299 child.Origin = Anchor.BottomRight;
300 break;
301 }
302 }
303 }
304 else
305 {
306 foreach (var child in fillContainer.Children)
307 child.Origin = originDropdown.Current.Value;
308 }
309 });
310
311 AddToggleStep("Stop adding children", state => { doNotAddChildren = state; });
312
313 scheduledAdder?.Cancel();
314 scheduledAdder = Scheduler.AddDelayed(
315 () =>
316 {
317 if (fillContainer.Parent == null)
318 scheduledAdder.Cancel();
319
320 if (doNotAddChildren)
321 {
322 fillContainer.Invalidate();
323 }
324
325 if (fillContainer.Children.Count < 1000 && !doNotAddChildren)
326 {
327 fillContainer.Add(new Container
328 {
329 Anchor = childAnchor,
330 Origin = childOrigin,
331 AutoSizeAxes = Axes.Both,
332 Children = new Drawable[]
333 {
334 new Box
335 {
336 Width = 50,
337 Height = 50,
338 Colour = Color4.White
339 },
340 new SpriteText
341 {
342 Colour = Color4.Black,
343 RelativePositionAxes = Axes.Both,
344 Position = new Vector2(0.5f, 0.5f),
345 Origin = Anchor.Centre,
346 Text = fillContainer.Children.Count.ToString()
347 }
348 }
349 });
350 }
351 },
352 100,
353 true
354 );
355 }
356
357 [FlowTestCase(FlowTestType.Full)]
358 private void test1()
359 {
360 fillContainer.Direction = FillDirection.Full;
361 fillContainer.Spacing = new Vector2(5, 5);
362 }
363
364 [FlowTestCase(FlowTestType.Horizontal)]
365 private void test2()
366 {
367 fillContainer.Direction = FillDirection.Horizontal;
368 fillContainer.Spacing = new Vector2(5, 5);
369 }
370
371 [FlowTestCase(FlowTestType.Vertical)]
372 private void test3()
373 {
374 fillContainer.Direction = FillDirection.Vertical;
375 fillContainer.Spacing = new Vector2(5, 5);
376 }
377
378 private class TestSceneDropdownHeader : DropdownHeader
379 {
380 private readonly SpriteText label;
381
382 protected internal override LocalisableString Label
383 {
384 get => label.Text;
385 set => label.Text = value;
386 }
387
388 public TestSceneDropdownHeader()
389 {
390 Foreground.Padding = new MarginPadding(4);
391 BackgroundColour = new Color4(100, 100, 100, 255);
392 BackgroundColourHover = Color4.HotPink;
393 Children = new[]
394 {
395 label = new SpriteText(),
396 };
397 }
398 }
399
400 private class AnchorDropdown : BasicDropdown<Anchor>
401 {
402 protected override DropdownHeader CreateHeader() => new TestSceneDropdownHeader();
403 }
404
405 private class FillDirectionDropdown : BasicDropdown<FlowTestType>
406 {
407 protected override DropdownHeader CreateHeader() => new TestSceneDropdownHeader();
408 }
409
410 [AttributeUsage(AttributeTargets.Method)]
411 private class FlowTestCaseAttribute : Attribute
412 {
413 public FlowTestType TestType { get; }
414
415 public FlowTestCaseAttribute(FlowTestType testType)
416 {
417 TestType = testType;
418 }
419 }
420
421 private enum FlowTestType
422 {
423 Full,
424 Horizontal,
425 Vertical,
426 }
427 }
428}