diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs new file mode 100644 index 000000000000..271fbde5c353 --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -0,0 +1,151 @@ +// 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 System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Tests.Visual.SongSelect +{ + public class TestSceneBeatmapMetadataDisplay : OsuTestScene + { + private BeatmapMetadataDisplay display; + + [Resolved] + private BeatmapManager manager { get; set; } + + [Cached(typeof(BeatmapDifficultyCache))] + private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); + + [Test] + public void TestLocal([Values("Beatmap", "Some long title and stuff")] + string title, + [Values("Trial", "Some1's very hardest difficulty")] + string version) + { + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = title, + }, + Version = version, + StarDifficulty = RNG.NextDouble(0, 10), + } + })); + } + + [Test] + public void TestDelayedStarRating() + { + AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true); + + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = "Heavy beatmap", + }, + Version = "10k objects", + StarDifficulty = 99.99f, + } + })); + + AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false); + } + + [Test] + public void TestRandomFromDatabase() + { + showMetadataForBeatmap(() => + { + var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + if (allBeatmapSets.Count == 0) + return manager.DefaultBeatmap; + + var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; + var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; + + return manager.GetWorkingBeatmap(randomBeatmap); + }); + } + + private void showMetadataForBeatmap(Func getBeatmap) + { + AddStep("setup display", () => + { + var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); + + OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; + + Remove(testDifficultyCache); + + Children = new Drawable[] + { + testDifficultyCache, + display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0f, + } + }; + + display.FadeIn(400, Easing.OutQuint); + }); + + AddWaitStep("wait a bit", 5); + + AddStep("finish loading", () => display.Loading = false); + } + + private class TestBeatmapDifficultyCache : BeatmapDifficultyCache + { + private TaskCompletionSource calculationBlocker; + + private bool blockCalculation; + + public bool BlockCalculation + { + get => blockCalculation; + set + { + if (value == blockCalculation) + return; + + blockCalculation = value; + + if (value) + calculationBlocker = new TaskCompletionSource(); + else + calculationBlocker?.SetResult(false); + } + } + + public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + { + if (blockCalculation) + await calculationBlocker.Task; + + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); + } + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 53d82c385d29..6ed623d0c063 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -103,8 +103,8 @@ protected override void LoadComplete() /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, - CancellationToken cancellationToken = default) + public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, + [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index c56344a8fb92..fd1150650c24 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play @@ -25,7 +27,7 @@ public class BeatmapMetadataDisplay : Container { private readonly WorkingBeatmap beatmap; private readonly Bindable> mods; - private readonly Drawable facade; + private readonly Drawable logoFacade; private LoadingSpinner loading; public IBindable> Mods => mods; @@ -41,19 +43,24 @@ public bool Loading } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) { this.beatmap = beatmap; - this.facade = facade; + this.logoFacade = logoFacade; this.mods = new Bindable>(); this.mods.BindTo(mods); } + private IBindable starDifficulty; + + private FillFlowContainer versionFlow; + private StarRatingDisplay starRatingDisplay; + [BackgroundDependencyLoader] - private void load() + private void load(BeatmapDifficultyCache difficultyCache) { - var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var metadata = beatmap.BeatmapInfo.Metadata; AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -66,7 +73,7 @@ private void load() Direction = FillDirection.Vertical, Children = new[] { - facade.With(d => + logoFacade.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; @@ -107,16 +114,30 @@ private void load() loading = new LoadingLayer(true) } }, - new OsuSpriteText + versionFlow = new FillFlowContainer { - Text = beatmap?.BeatmapInfo?.Version, - Font = OsuFont.GetFont(size: 26, italics: true), - Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, - Margin = new MarginPadding + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + Margin = new MarginPadding { Bottom = 40 }, + Children = new Drawable[] { - Bottom = 40 - }, + new OsuSpriteText + { + Text = beatmap?.BeatmapInfo?.Version, + Font = OsuFont.GetFont(size: 26, italics: true), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + starRatingDisplay = new StarRatingDisplay(default) + { + Alpha = 0f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + } }, new GridContainer { @@ -159,9 +180,38 @@ private void load() } }; + starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); + Loading = true; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (starDifficulty.Value != null) + { + starRatingDisplay.Current.Value = starDifficulty.Value.Value; + starRatingDisplay.Show(); + } + else + { + starRatingDisplay.Hide(); + + starDifficulty.ValueChanged += d => + { + Debug.Assert(d.NewValue != null); + + starRatingDisplay.Current.Value = d.NewValue.Value; + + versionFlow.AutoSizeDuration = 300; + versionFlow.AutoSizeEasing = Easing.OutQuint; + + starRatingDisplay.FadeIn(300, Easing.InQuint); + }; + } + } + private class MetadataLineLabel : OsuSpriteText { public MetadataLineLabel(string text)