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.Threading;
5using NUnit.Framework;
6using osu.Framework.Allocation;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.Shapes;
10using osu.Framework.Graphics.Sprites;
11using osuTK;
12using osuTK.Graphics;
13
14namespace osu.Framework.Tests.Visual.Drawables
15{
16 public class TestSceneModelBackedDrawableWithLoading : FrameworkTestScene
17 {
18 private TestModelBackedDrawable backedDrawable;
19
20 private void createModelBackedDrawable(bool immediate) =>
21 Child = backedDrawable = new TestModelBackedDrawable(immediate)
22 {
23 Anchor = Anchor.Centre,
24 Origin = Anchor.Centre,
25 Size = new Vector2(200),
26 };
27
28 [Test]
29 public void TestNonImmediateTransform()
30 {
31 AddStep("setup", () => createModelBackedDrawable(false));
32 addUsageSteps();
33 }
34
35 [Test]
36 public void TestTransformImmediately()
37 {
38 AddStep("setup", () => createModelBackedDrawable(true));
39 addUsageSteps();
40 }
41
42 private void addUsageSteps()
43 {
44 TestDrawableModel drawableModel = null;
45
46 AddStep("load first model", () => backedDrawable.Model = new TestModel(drawableModel = new TestDrawableModel()));
47 AddWaitStep("wait a bit", 5);
48 AddStep("finish load", () => drawableModel.AllowLoad.Set());
49 AddWaitStep("wait a bit", 5);
50 AddStep("load second model", () => backedDrawable.Model = new TestModel(drawableModel = new TestDrawableModel()));
51 AddWaitStep("wait a bit", 5);
52 AddStep("finish load", () => drawableModel.AllowLoad.Set());
53 }
54
55 private class TestModelBackedDrawable : ModelBackedDrawable<TestModel>
56 {
57 public new TestModel Model
58 {
59 set => base.Model = value;
60 }
61
62 private readonly bool immediate;
63 private readonly Drawable spinner;
64
65 public TestModelBackedDrawable(bool immediate)
66 {
67 this.immediate = immediate;
68
69 CornerRadius = 5;
70 Masking = true;
71
72 AddRangeInternal(new[]
73 {
74 new Box
75 {
76 Colour = new Color4(0.1f, 0.1f, 0.1f, 1),
77 RelativeSizeAxes = Axes.Both,
78 Depth = float.MaxValue
79 },
80 spinner = new LoadingSpinner
81 {
82 Anchor = Anchor.Centre,
83 Origin = Anchor.Centre,
84 Size = new Vector2(20),
85 Alpha = 0,
86 Depth = float.MinValue
87 }
88 });
89 }
90
91 protected override bool TransformImmediately => immediate;
92
93 protected override double TransformDuration => 500;
94
95 protected override Drawable CreateDrawable(TestModel model) => model?.Drawable;
96
97 protected override void OnLoadStarted()
98 {
99 base.OnLoadStarted();
100
101 if (!immediate)
102 DisplayedDrawable?.FadeTo(0, 300, Easing.OutQuint);
103
104 spinner.FadeIn(300, Easing.OutQuint);
105 }
106
107 protected override void OnLoadFinished()
108 {
109 base.OnLoadFinished();
110
111 spinner.FadeOut(250, Easing.OutQuint);
112 }
113 }
114
115 private class TestModel
116 {
117 public readonly Drawable Drawable;
118
119 public TestModel(TestDrawableModel drawable)
120 {
121 Drawable = drawable;
122 }
123 }
124
125 private class TestDrawableModel : CompositeDrawable
126 {
127 private static int id = 1;
128
129 public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim();
130
131 protected Color4 BackgroundColour
132 {
133 get
134 {
135 switch (id % 5)
136 {
137 default:
138 return Color4.SkyBlue;
139
140 case 1:
141 return Color4.Tomato;
142
143 case 2:
144 return Color4.DarkGreen;
145
146 case 3:
147 return Color4.MediumPurple;
148
149 case 4:
150 return Color4.DarkOrchid;
151 }
152 }
153 }
154
155 public TestDrawableModel()
156 {
157 RelativeSizeAxes = Axes.Both;
158
159 InternalChildren = new Drawable[]
160 {
161 new Box
162 {
163 RelativeSizeAxes = Axes.Both,
164 Colour = BackgroundColour
165 },
166 new SpriteText
167 {
168 Anchor = Anchor.Centre,
169 Origin = Anchor.Centre,
170 Text = $"Model {id++}"
171 }
172 };
173 }
174
175 [BackgroundDependencyLoader]
176 private void load()
177 {
178 AllowLoad.Wait();
179 }
180 }
181
182 private class LoadingSpinner : CompositeDrawable
183 {
184 private readonly SpriteIcon icon;
185
186 public LoadingSpinner()
187 {
188 InternalChild = icon = new SpriteIcon
189 {
190 Anchor = Anchor.Centre,
191 Origin = Anchor.Centre,
192 RelativeSizeAxes = Axes.Both,
193 Icon = FontAwesome.Solid.Spinner
194 };
195 }
196
197 protected override void LoadComplete()
198 {
199 base.LoadComplete();
200 icon.Spin(2000, RotationDirection.Clockwise);
201 }
202 }
203 }
204}