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 osu.Framework.Extensions.Color4Extensions;
5using osu.Framework.Graphics;
6using osu.Framework.Graphics.Containers;
7using osu.Framework.Graphics.Shapes;
8using osu.Framework.Graphics.Sprites;
9using osuTK;
10using osuTK.Graphics;
11
12namespace osu.Framework.Tests.Visual.Drawables
13{
14 public class TestSceneProxyDrawables : FrameworkTestScene
15 {
16 public TestSceneProxyDrawables()
17 {
18 Child = new BasicScrollContainer
19 {
20 RelativeSizeAxes = Axes.Both,
21 Child = new FillFlowContainer
22 {
23 RelativeSizeAxes = Axes.X,
24 AutoSizeAxes = Axes.Y,
25 Children = new[]
26 {
27 generateProxyAboveAllPresentTest(1),
28 generateProxyBelowAllPresentTest(1),
29 generateProxyAboveAllPresentTest(6),
30 generateProxyBelowAllPresentTest(6),
31 generateProxyAboveOriginalMaskedAway(1),
32 generateProxyBelowOriginalMaskedAway(1),
33 generateProxyAboveOriginalMaskedAway(6),
34 generateProxyBelowOriginalMaskedAway(6),
35 generateProxyAboveBoxParentNotPresent(1),
36 generateProxyBelowBoxParentNotPresent(1),
37 generateProxyAboveBoxParentNotPresent(6),
38 generateProxyBelowBoxParentNotPresent(6),
39 generateProxyAboveBoxParentNotAlive(1),
40 generateProxyBelowBoxParentNotAlive(1),
41 generateProxyAboveBoxParentNotAlive(6),
42 generateProxyBelowBoxParentNotAlive(6),
43 generateProxyAboveParentOriginalIndirectlyMaskedAway(1),
44 generateProxyBelowParentOriginalIndirectlyMaskedAway(1),
45 generateProxyAboveParentOriginalIndirectlyMaskedAway(6),
46 generateProxyBelowParentOriginalIndirectlyMaskedAway(6),
47 generateOpaqueProxyAboveOpaqueBox(),
48 }
49 }
50 };
51 }
52
53 private Drawable generateProxyAboveAllPresentTest(int proxyCount)
54 {
55 var box = new Box
56 {
57 Anchor = Anchor.Centre,
58 Origin = Anchor.Centre,
59 Size = new Vector2(50),
60 };
61
62 var proxy = box.CreateProxy();
63 for (int i = 1; i < proxyCount; i++)
64 proxy = proxy.CreateProxy();
65
66 return new Visualiser($"{proxyCount} proxy(s) above, all present")
67 {
68 Children = new Drawable[] { box, new ProxyVisualiser(proxy, true) }
69 };
70 }
71
72 private Drawable generateProxyBelowAllPresentTest(int proxyCount)
73 {
74 var box = new Box
75 {
76 Anchor = Anchor.Centre,
77 Origin = Anchor.Centre,
78 Size = new Vector2(50),
79 };
80
81 var proxy = box.CreateProxy();
82 for (int i = 1; i < proxyCount; i++)
83 proxy = proxy.CreateProxy();
84
85 return new Visualiser($"{proxyCount} proxy(s) below, all present")
86 {
87 Children = new Drawable[] { new ProxyVisualiser(proxy, false), box }
88 };
89 }
90
91 private Drawable generateProxyAboveOriginalMaskedAway(int proxyCount)
92 {
93 var box = new Box
94 {
95 RelativeSizeAxes = Axes.Both,
96 Position = new Vector2(60, 0)
97 };
98
99 var boxMaskingContainer = new Container
100 {
101 Anchor = Anchor.Centre,
102 Origin = Anchor.Centre,
103 Size = new Vector2(50),
104 Masking = true,
105 BorderColour = Color4.Yellow,
106 BorderThickness = 2,
107 Children = new Drawable[]
108 {
109 new Box
110 {
111 RelativeSizeAxes = Axes.Both,
112 Alpha = 0,
113 AlwaysPresent = true
114 },
115 box
116 }
117 };
118
119 var proxy = box.CreateProxy();
120 for (int i = 1; i < proxyCount; i++)
121 proxy = proxy.CreateProxy();
122
123 return new Visualiser($"{proxyCount} proxy(s) above, original masked away")
124 {
125 Children = new Drawable[] { boxMaskingContainer, new ProxyVisualiser(proxy, true) }
126 };
127 }
128
129 private Drawable generateProxyBelowOriginalMaskedAway(int proxyCount)
130 {
131 var box = new Box
132 {
133 RelativeSizeAxes = Axes.Both,
134 Position = new Vector2(60, 0)
135 };
136
137 var boxMaskingContainer = new Container
138 {
139 Anchor = Anchor.Centre,
140 Origin = Anchor.Centre,
141 Size = new Vector2(50),
142 Masking = true,
143 BorderColour = Color4.Yellow,
144 BorderThickness = 2,
145 Children = new Drawable[]
146 {
147 new Box
148 {
149 RelativeSizeAxes = Axes.Both,
150 Alpha = 0,
151 AlwaysPresent = true
152 },
153 box
154 }
155 };
156
157 var proxy = box.CreateProxy();
158 for (int i = 1; i < proxyCount; i++)
159 proxy = proxy.CreateProxy();
160
161 return new Visualiser($"{proxyCount} proxy(s) below, original masked away")
162 {
163 Children = new Drawable[] { new ProxyVisualiser(proxy, false), boxMaskingContainer }
164 };
165 }
166
167 private Drawable generateProxyAboveBoxParentNotPresent(int proxyCount)
168 {
169 var box = new Box { RelativeSizeAxes = Axes.Both };
170
171 var invisibleContainer = new NonPresentContainer
172 {
173 Anchor = Anchor.Centre,
174 Origin = Anchor.Centre,
175 Size = new Vector2(50),
176 Child = new Container
177 {
178 RelativeSizeAxes = Axes.Both,
179 Child = box
180 }
181 };
182
183 var proxy = box.CreateProxy();
184 for (int i = 1; i < proxyCount; i++)
185 proxy = proxy.CreateProxy();
186
187 return new Visualiser($"{proxyCount} proxy(s) above, original parent invisible")
188 {
189 Children = new Drawable[] { invisibleContainer, new ProxyVisualiser(proxy, true) }
190 };
191 }
192
193 private Drawable generateProxyBelowBoxParentNotPresent(int proxyCount)
194 {
195 var box = new Box { RelativeSizeAxes = Axes.Both };
196
197 var invisibleContainer = new NonPresentContainer
198 {
199 Anchor = Anchor.Centre,
200 Origin = Anchor.Centre,
201 Size = new Vector2(50),
202 Child = new Container
203 {
204 RelativeSizeAxes = Axes.Both,
205 Child = box
206 }
207 };
208
209 var proxy = box.CreateProxy();
210 for (int i = 1; i < proxyCount; i++)
211 proxy = proxy.CreateProxy();
212
213 return new Visualiser($"{proxyCount} proxy(s) below, original parent invisible")
214 {
215 Children = new Drawable[] { new ProxyVisualiser(proxy, false), invisibleContainer }
216 };
217 }
218
219 private Drawable generateProxyAboveBoxParentNotAlive(int proxyCount)
220 {
221 var box = new Box { RelativeSizeAxes = Axes.Both };
222
223 var invisibleContainer = new NonAliveContainer
224 {
225 Anchor = Anchor.Centre,
226 Origin = Anchor.Centre,
227 Size = new Vector2(50),
228 Child = new Container
229 {
230 RelativeSizeAxes = Axes.Both,
231 Child = box
232 }
233 };
234
235 var proxy = box.CreateProxy();
236 for (int i = 1; i < proxyCount; i++)
237 proxy = proxy.CreateProxy();
238
239 return new Visualiser($"{proxyCount} proxy(s) above, original parent not alive")
240 {
241 Children = new Drawable[] { invisibleContainer, new ProxyVisualiser(proxy, true) }
242 };
243 }
244
245 private Drawable generateProxyBelowBoxParentNotAlive(int proxyCount)
246 {
247 var box = new Box { RelativeSizeAxes = Axes.Both };
248
249 var invisibleContainer = new NonAliveContainer
250 {
251 Anchor = Anchor.Centre,
252 Origin = Anchor.Centre,
253 Size = new Vector2(50),
254 Child = new Container
255 {
256 RelativeSizeAxes = Axes.Both,
257 Child = box
258 }
259 };
260
261 var proxy = box.CreateProxy();
262 for (int i = 1; i < proxyCount; i++)
263 proxy = proxy.CreateProxy();
264
265 return new Visualiser($"{proxyCount} proxy(s) below, original parent not alive")
266 {
267 Children = new Drawable[] { new ProxyVisualiser(proxy, false), invisibleContainer }
268 };
269 }
270
271 private Drawable generateProxyAboveParentOriginalIndirectlyMaskedAway(int proxyCount)
272 {
273 var box = new Box
274 {
275 Size = new Vector2(50),
276 Anchor = Anchor.Centre,
277 Origin = Anchor.Centre,
278 Position = new Vector2(-50)
279 };
280
281 var proxy = box.CreateProxy();
282 for (int i = 1; i < proxyCount; i++)
283 proxy = proxy.CreateProxy();
284
285 return new Visualiser($"{proxyCount} proxy(s) above, proxy masked")
286 {
287 Children = new Drawable[]
288 {
289 box,
290 new Container
291 {
292 Anchor = Anchor.Centre,
293 Origin = Anchor.Centre,
294 Size = new Vector2(100),
295 Masking = true,
296 CornerRadius = 20,
297 Children = new Drawable[]
298 {
299 new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.Yellow.Opacity(0.2f) },
300 new ProxyVisualiser(proxy, true)
301 }
302 }
303 }
304 };
305 }
306
307 private Drawable generateProxyBelowParentOriginalIndirectlyMaskedAway(int proxyCount)
308 {
309 var box = new Box
310 {
311 Size = new Vector2(50),
312 Anchor = Anchor.Centre,
313 Origin = Anchor.Centre,
314 Position = new Vector2(-50)
315 };
316
317 var proxy = box.CreateProxy();
318 for (int i = 1; i < proxyCount; i++)
319 proxy = proxy.CreateProxy();
320
321 return new Visualiser($"{proxyCount} proxy(s) below, proxy masked")
322 {
323 Children = new Drawable[]
324 {
325 new Container
326 {
327 Anchor = Anchor.Centre,
328 Origin = Anchor.Centre,
329 Size = new Vector2(100),
330 Masking = true,
331 CornerRadius = 20,
332 Children = new Drawable[]
333 {
334 new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.Yellow.Opacity(0.2f) },
335 new ProxyVisualiser(proxy, false)
336 }
337 },
338 box
339 }
340 };
341 }
342
343 private Drawable generateOpaqueProxyAboveOpaqueBox()
344 {
345 var box = new Box
346 {
347 Anchor = Anchor.Centre,
348 Origin = Anchor.Centre,
349 Size = new Vector2(50),
350 };
351
352 var proxy = box.CreateProxy();
353
354 return new Visualiser("proxy above opaque box")
355 {
356 Children = new Drawable[]
357 {
358 box,
359 new ProxyVisualiser(proxy, false, 1.0f)
360 }
361 };
362 }
363
364 private class NonPresentContainer : Container
365 {
366 private bool isPresent = true;
367 public override bool IsPresent => isPresent;
368
369 public override bool UpdateSubTree()
370 {
371 // We want to be present for updates
372 isPresent = true;
373
374 var result = base.UpdateSubTree();
375 if (!result)
376 return false;
377
378 // We want to not be present for draw
379 isPresent = false;
380 return true;
381 }
382 }
383
384 private class NonAliveContainer : Container
385 {
386 protected internal override bool ShouldBeAlive => false;
387 public override bool DisposeOnDeathRemoval => false;
388 }
389
390 private class Visualiser : Container
391 {
392 protected override Container<Drawable> Content => content;
393 private readonly Container content;
394
395 public Visualiser(string description)
396 {
397 Size = new Vector2(300);
398
399 InternalChildren = new Drawable[]
400 {
401 content = new Container { RelativeSizeAxes = Axes.Both },
402 new Container
403 {
404 RelativeSizeAxes = Axes.Both,
405 Masking = true,
406 BorderColour = Color4.Gray,
407 BorderThickness = 2,
408 Child = new Box
409 {
410 RelativeSizeAxes = Axes.Both,
411 Alpha = 0,
412 AlwaysPresent = true
413 }
414 },
415 new SpriteText
416 {
417 Anchor = Anchor.TopCentre,
418 Origin = Anchor.TopCentre,
419 Y = 10,
420 Text = description
421 }
422 };
423 }
424 }
425
426 private class ProxyVisualiser : CompositeDrawable
427 {
428 private readonly Drawable original;
429 private readonly Drawable overlay;
430
431 public ProxyVisualiser(Drawable proxy, bool proxyIsBelow, float boxAlpha = 0.5f)
432 {
433 RelativeSizeAxes = Axes.Both;
434
435 original = proxy.Original;
436
437 while (original != (original = original.Original))
438 {
439 }
440
441 if (proxyIsBelow)
442 AddInternal(proxy);
443
444 AddInternal(overlay = new Container
445 {
446 Colour = proxyIsBelow ? Color4.Red : Color4.Green,
447 Children = new Drawable[]
448 {
449 new Box
450 {
451 RelativeSizeAxes = Axes.Both,
452 Alpha = boxAlpha,
453 },
454 new SpriteText
455 {
456 Anchor = Anchor.BottomCentre,
457 Origin = Anchor.BottomCentre,
458 Colour = Color4.Black,
459 Text = "proxy"
460 }
461 }
462 });
463
464 if (!proxyIsBelow)
465 AddInternal(proxy);
466 }
467
468 protected override void Update()
469 {
470 base.Update();
471
472 var aabb = ToLocalSpace(original.ScreenSpaceDrawQuad).AABBFloat.Inflate(15);
473 overlay.Position = aabb.Location;
474 overlay.Size = aabb.Size;
475 }
476 }
477 }
478}