From 2be6b29f21ba571d9d7783f714eadddaa584ba42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 31 May 2024 11:50:04 +0200 Subject: [PATCH] Implement time remaining display for daily challenge screen --- ...estSceneDailyChallengeTimeRemainingRing.cs | 86 +++++++++++ .../DailyChallenge/DailyChallenge.cs | 10 +- .../DailyChallengeTimeRemainingRing.cs | 136 ++++++++++++++++++ 3 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs create mode 100644 osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs new file mode 100644 index 0000000000..9e21214c11 --- /dev/null +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Online.Rooms; +using osu.Game.Overlays; +using osu.Game.Screens.OnlinePlay.DailyChallenge; + +namespace osu.Game.Tests.Visual.DailyChallenge +{ + public partial class TestSceneDailyChallengeTimeRemainingRing : OsuTestScene + { + private readonly Bindable room = new Bindable(new Room()); + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) + { + Model = { BindTarget = room } + }; + + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + + [Test] + public void TestBasicAppearance() + { + DailyChallengeTimeRemainingRing ring = null!; + + AddStep("create content", () => Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + ring = new DailyChallengeTimeRemainingRing + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }); + AddSliderStep("adjust width", 0.1f, 1, 1, width => + { + if (ring.IsNotNull()) + ring.Width = width; + }); + AddSliderStep("adjust height", 0.1f, 1, 1, height => + { + if (ring.IsNotNull()) + ring.Height = height; + }); + AddStep("just started", () => + { + room.Value.StartDate.Value = DateTimeOffset.Now.AddMinutes(-1); + room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + }); + AddStep("midway through", () => + { + room.Value.StartDate.Value = DateTimeOffset.Now.AddHours(-12); + room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + }); + AddStep("nearing end", () => + { + room.Value.StartDate.Value = DateTimeOffset.Now.AddDays(-1).AddMinutes(8); + room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + }); + AddStep("already ended", () => + { + room.Value.StartDate.Value = DateTimeOffset.Now.AddDays(-2); + room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + }); + AddSliderStep("manual progress", 0f, 1f, 0f, progress => + { + var startedTimeAgo = TimeSpan.FromHours(24) * progress; + room.Value.StartDate.Value = DateTimeOffset.Now - startedTimeAgo; + room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + }); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index 5740825ca2..e2927617f8 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -161,7 +162,10 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { new Drawable?[] { - null, + new DailyChallengeTimeRemainingRing + { + RelativeSizeAxes = Axes.Both, + }, null, // Middle column (leaderboard) new GridContainer @@ -171,7 +175,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { new Drawable[] { - new OverlinedHeader("Leaderboard") + new SectionHeader("Leaderboard") }, [leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }], }, @@ -191,7 +195,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { new Drawable[] { - new OverlinedHeader("Chat") + new SectionHeader("Chat") }, [new MatchChatDisplay(room) { RelativeSizeAxes = Axes.Both }] }, diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs new file mode 100644 index 0000000000..ccd073331e --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs @@ -0,0 +1,136 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Threading; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.DailyChallenge +{ + public partial class DailyChallengeTimeRemainingRing : OnlinePlayComposite + { + private CircularProgress progress = null!; + private OsuSpriteText timeText = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new DrawSizePreservingFillContainer + { + RelativeSizeAxes = Axes.Both, + TargetDrawSize = new Vector2(200), + Strategy = DrawSizePreservationStrategy.Minimum, + Children = new Drawable[] + { + new CircularProgress + { + Size = new Vector2(180), + InnerRadius = 0.1f, + Progress = 1, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colourProvider.Background5, + }, + progress = new CircularProgress + { + Size = new Vector2(180), + InnerRadius = 0.1f, + Progress = 1, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + Children = new[] + { + timeText = new OsuSpriteText + { + Text = "00:00:00", + Font = OsuFont.TorusAlternate.With(size: 40), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new OsuSpriteText + { + Text = "remaining", + Font = OsuFont.Default.With(size: 20), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + StartDate.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + EndDate.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + updateState(); + FinishTransforms(true); + } + + private ScheduledDelegate? scheduledUpdate; + + private void updateState() + { + scheduledUpdate?.Cancel(); + scheduledUpdate = null; + + const float transition_duration = 300; + + if (StartDate.Value == null || EndDate.Value == null || EndDate.Value < DateTimeOffset.Now) + { + timeText.Text = TimeSpan.Zero.ToString(@"hh\:mm\:ss"); + progress.Progress = 0; + timeText.FadeColour(colours.Red2, transition_duration, Easing.OutQuint); + progress.FadeColour(colours.Red2, transition_duration, Easing.OutQuint); + return; + } + + var roomDuration = EndDate.Value.Value - StartDate.Value.Value; + var remaining = EndDate.Value.Value - DateTimeOffset.Now; + + timeText.Text = remaining.ToString(@"hh\:mm\:ss"); + progress.Progress = remaining.TotalSeconds / roomDuration.TotalSeconds; + + if (remaining < TimeSpan.FromMinutes(15)) + { + timeText.Colour = progress.Colour = colours.Red1; + timeText + .FadeColour(colours.Red1) + .Then().FlashColour(colours.Red0, transition_duration, Easing.OutQuint); + progress + .FadeColour(colours.Red1) + .Then().FlashColour(colours.Red0, transition_duration, Easing.OutQuint); + } + else + { + timeText.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + progress.FadeColour(colourProvider.Highlight1, transition_duration, Easing.OutQuint); + } + + scheduledUpdate = Scheduler.AddDelayed(updateState, 1000); + } + } +}