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 osu.Framework.Graphics;
6using osu.Framework.Graphics.Containers;
7using osu.Framework.Graphics.Shapes;
8using osu.Framework.Graphics.Sprites;
9using osu.Framework.Input.Events;
10using osu.Framework.Timing;
11using osuTK;
12using osuTK.Graphics;
13
14namespace osu.Framework.Tests.Visual.Clocks
15{
16 public abstract class TestSceneClock : FrameworkTestScene
17 {
18 private readonly FillFlowContainer fill;
19
20 protected TestSceneClock()
21 {
22 Child = fill = new FillFlowContainer
23 {
24 Spacing = new Vector2(5),
25 Direction = FillDirection.Full,
26 RelativeSizeAxes = Axes.Both,
27 };
28
29 AddStep("clear all", () =>
30 {
31 fill.Clear();
32 lastClock = null;
33 AddClock(Clock);
34 });
35 }
36
37 private IClock lastClock;
38
39 protected IClock AddClock(IClock clock)
40 {
41 if (lastClock != null && clock is ISourceChangeableClock framed)
42 framed.ChangeSource(lastClock);
43
44 fill.Add(new VisualClock(lastClock = clock));
45
46 return clock;
47 }
48
49 public class VisualClock : CompositeDrawable
50 {
51 private readonly IClock clock;
52 private readonly SpriteText time;
53 private readonly SpriteText rate;
54
55 private bool zeroed = true;
56
57 private const float width = 200;
58
59 private readonly Box bg;
60 private readonly Box hand;
61
62 public VisualClock(IClock clock)
63 {
64 this.clock = clock;
65
66 Size = new Vector2(width);
67 CornerRadius = width / 2;
68 Masking = true;
69
70 BorderColour = Color4.White;
71 BorderThickness = 5;
72
73 InternalChildren = new Drawable[]
74 {
75 bg = new Box
76 {
77 Colour = clock is IAdjustableClock ? Color4.Tomato : Color4.Navy,
78 RelativeSizeAxes = Axes.Both,
79 },
80 new SpriteText
81 {
82 Text = clock.GetType().Name,
83 Anchor = Anchor.Centre,
84 Origin = Anchor.Centre,
85 Y = -25,
86 },
87 time = new SpriteText
88 {
89 Anchor = Anchor.Centre,
90 Origin = Anchor.Centre,
91 Y = 25,
92 },
93 rate = new SpriteText
94 {
95 Anchor = Anchor.Centre,
96 Origin = Anchor.Centre,
97 Font = new FontUsage(size: 14),
98 Y = 40,
99 },
100 hand = new Box
101 {
102 Colour = Color4.White,
103 Anchor = Anchor.Centre,
104 Origin = Anchor.BottomCentre,
105 Size = new Vector2(2, width / 2)
106 },
107 };
108 }
109
110 protected override bool OnClick(ClickEvent e)
111 {
112 if (clock is IAdjustableClock adjustable)
113 {
114 if (adjustable.IsRunning)
115 adjustable.Stop();
116 else
117 adjustable.Start();
118 }
119
120 return true;
121 }
122
123 protected override bool OnScroll(ScrollEvent e)
124 {
125 if (clock is IAdjustableClock adjustable)
126 adjustable.Rate += e.ScrollDelta.Y / 1000;
127
128 return base.OnScroll(e);
129 }
130
131 protected override void Update()
132 {
133 base.Update();
134
135 var lastTime = clock.CurrentTime;
136
137 (clock as IFrameBasedClock)?.ProcessFrame();
138
139 var timespan = TimeSpan.FromMilliseconds(clock.CurrentTime);
140 time.Text = $"{timespan.Minutes:00}:{timespan.Seconds:00}:{timespan.Milliseconds:00}";
141 rate.Text = $"{clock.Rate:N2}x";
142
143 if (clock.CurrentTime != lastTime)
144 BorderColour = clock.CurrentTime >= lastTime ? Color4.White : Color4.Red;
145
146 Colour = clock.IsRunning ? Color4.White : Color4.Gray;
147
148 hand.Rotation = (float)(clock.CurrentTime / 1000) * 360 % 360;
149
150 if (hand.Rotation < 180)
151 {
152 if (!zeroed)
153 {
154 zeroed = true;
155 bg.FlashColour(Color4.White, 500, Easing.OutQuint);
156 }
157 }
158 else
159 {
160 zeroed = false;
161 }
162 }
163 }
164 }
165}