diff --git a/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs b/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs index 2f3aa9dc0f..4ca6c5a549 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs @@ -15,10 +15,12 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Online.Leaderboards; using osu.Game.Overlays; using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; @@ -33,6 +35,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 protected BeatmapManager Beatmaps { get; private set; } = null!; protected RealmRulesetStore Rulesets { get; private set; } = null!; protected OsuConfigManager Config { get; private set; } = null!; + protected ScoreManager ScoreManager { get; private set; } = null!; private RealmDetachedBeatmapStore beatmapStore = null!; @@ -51,6 +54,9 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Cached(typeof(INotificationOverlay))] private readonly INotificationOverlay notificationOverlay = new NotificationOverlay(); + [Cached] + protected readonly LeaderboardManager LeaderboardManager = new LeaderboardManager(); + protected SongSelectTestScene() { Children = new Drawable[] @@ -60,6 +66,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + LeaderboardManager, new Toolbar { State = { Value = Visibility.Visible }, @@ -90,6 +97,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 dependencies.Cache(Realm); dependencies.Cache(Beatmaps = new BeatmapManager(LocalStorage, Realm, null, Dependencies.Get(), Resources, Dependencies.Get(), Beatmap.Default)); dependencies.Cache(Config = new OsuConfigManager(LocalStorage)); + dependencies.Cache(ScoreManager = new ScoreManager(Rulesets, () => Beatmaps, LocalStorage, Realm, API, Config)); dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore()); diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs index 76a1683985..5d96ebaa85 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs @@ -8,12 +8,19 @@ using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Online.API; +using osu.Game.Online.Leaderboards; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Screens.SelectV2; using osuTK.Input; using FooterButtonMods = osu.Game.Screens.SelectV2.FooterButtonMods; using FooterButtonOptions = osu.Game.Screens.SelectV2.FooterButtonOptions; @@ -22,6 +29,55 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { public partial class TestSceneSongSelect : SongSelectTestScene { + [Test] + public void TestResultsScreenWhenClickingLeaderboardScore() + { + LoadSongSelect(); + ImportBeatmapForRuleset(0); + + AddAssert("beatmap imported", () => Beatmaps.GetAllUsableBeatmapSets().Any(), () => Is.True); + + // song select should automatically select the beatmap for us but this is not implemented yet. + // todo: remove when that's the case. + AddAssert("no beatmap selected", () => Beatmap.IsDefault); + AddStep("select beatmap", () => Beatmap.Value = Beatmaps.GetWorkingBeatmap(Beatmaps.GetAllUsableBeatmapSets().Single().Beatmaps.First())); + AddAssert("beatmap selected", () => !Beatmap.IsDefault); + + AddStep("import score", () => + { + var beatmapInfo = Beatmaps.GetAllUsableBeatmapSets().Single().Beatmaps.First(); + ScoreManager.Import(new ScoreInfo + { + Hash = Guid.NewGuid().ToString(), + BeatmapHash = beatmapInfo.Hash, + BeatmapInfo = beatmapInfo, + Ruleset = new OsuRuleset().RulesetInfo, + User = new GuestUser(), + }); + }); + + AddStep("select ranking tab", () => + { + InputManager.MoveMouseTo(SongSelect.ChildrenOfType>().Last()); + InputManager.Click(MouseButton.Left); + }); + + // probably should be done via dropdown menu instead of forcing this way? + AddStep("set local scope", () => + { + var current = LeaderboardManager.CurrentCriteria!; + LeaderboardManager.FetchWithCriteria(new LeaderboardCriteria(current.Beatmap, current.Ruleset, BeatmapLeaderboardScope.Local, null)); + }); + + AddUntilStep("wait for score panel", () => SongSelect.ChildrenOfType().Any()); + AddStep("click score panel", () => + { + InputManager.MoveMouseTo(SongSelect.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for results screen", () => Stack.CurrentScreen is ResultsScreen); + } + #region Hotkeys [Test] diff --git a/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs b/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs index b8c4d07d04..036bacb5e9 100644 --- a/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs @@ -50,6 +50,9 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + [Resolved] + private ISongSelect? songSelect { get; set; } + private Container placeholderContainer = null!; private Placeholder? placeholder; @@ -244,6 +247,7 @@ namespace osu.Game.Screens.SelectV2 Rank = i + 1, IsPersonalBest = s.OnlineID == userScore?.OnlineID, SelectedMods = { BindTarget = mods }, + Action = () => onLeaderboardScoreClicked(s), }), loadedScores => { int delay = 200; @@ -279,6 +283,7 @@ namespace osu.Game.Screens.SelectV2 IsPersonalBest = true, Rank = userScore.Position, SelectedMods = { BindTarget = mods }, + Action = () => onLeaderboardScoreClicked(userScore), }; scoresScroll.TransformTo(nameof(scoresScroll.Padding), new MarginPadding { Bottom = personal_best_height }, 300, Easing.OutQuint); @@ -308,6 +313,8 @@ namespace osu.Game.Screens.SelectV2 scoresScroll.TransformTo(nameof(scoresScroll.Padding), new MarginPadding(), 300, Easing.OutQuint); } + private void onLeaderboardScoreClicked(ScoreInfo score) => songSelect?.PresentScore(score); + private LeaderboardState displayedState; protected void SetState(LeaderboardState state) diff --git a/osu.Game/Screens/SelectV2/FooterButtonOptions.cs b/osu.Game/Screens/SelectV2/FooterButtonOptions.cs index c7800b44c3..5b646312d2 100644 --- a/osu.Game/Screens/SelectV2/FooterButtonOptions.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonOptions.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.SelectV2 private IBindable beatmap { get; set; } = null!; [Resolved] - private ISongSelectBeatmapActions? beatmapActions { get; set; } + private ISongSelect? songSelect { get; set; } [BackgroundDependencyLoader] private void load(OsuColour colour) @@ -51,7 +51,7 @@ namespace osu.Game.Screens.SelectV2 public Framework.Graphics.UserInterface.Popover GetPopover() => new Popover(this, beatmap.Value) { ColourProvider = colourProvider, - BeatmapActions = beatmapActions + SongSelect = songSelect }; } } diff --git a/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs b/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs index ca43bc3fe5..9dc50b87d4 100644 --- a/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.SelectV2 // Can't use DI for these due to popover being initialised from a footer button which ends up being on the global // PopoverContainer. - public ISongSelectBeatmapActions? BeatmapActions { get; init; } + public ISongSelect? SongSelect { get; init; } public required OverlayColourProvider ColourProvider { get; init; } public Popover(FooterButtonOptions footerButton, WorkingBeatmap beatmap) @@ -59,20 +59,20 @@ namespace osu.Game.Screens.SelectV2 }; addHeader(CommonStrings.General); - addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => BeatmapActions?.ManageCollections()); + addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => SongSelect?.ManageCollections()); addHeader(SongSelectStrings.ForAllDifficulties, beatmap.BeatmapSetInfo.ToString()); - addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => BeatmapActions?.Delete(beatmap.BeatmapSetInfo), colours.Red1); + addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => SongSelect?.Delete(beatmap.BeatmapSetInfo), colours.Red1); addHeader(SongSelectStrings.ForSelectedDifficulty, beatmap.BeatmapInfo.DifficultyName); // TODO: replace with "remove from played" button when beatmap is already played. - addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, () => BeatmapActions?.MarkPlayed(beatmap.BeatmapInfo)); - addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => BeatmapActions?.ClearScores(beatmap.BeatmapInfo), colours.Red1); + addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, () => SongSelect?.MarkPlayed(beatmap.BeatmapInfo)); + addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => SongSelect?.ClearScores(beatmap.BeatmapInfo), colours.Red1); - if (BeatmapActions?.EditingAllowed == true) - addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => BeatmapActions.Edit(beatmap.BeatmapInfo)); + if (SongSelect?.EditingAllowed == true) + addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => SongSelect.Edit(beatmap.BeatmapInfo)); - addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => BeatmapActions?.Hide(beatmap.BeatmapInfo)); + addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => SongSelect?.Hide(beatmap.BeatmapInfo)); } protected override void LoadComplete() diff --git a/osu.Game/Screens/SelectV2/ISongSelectBeatmapActions.cs b/osu.Game/Screens/SelectV2/ISongSelect.cs similarity index 87% rename from osu.Game/Screens/SelectV2/ISongSelectBeatmapActions.cs rename to osu.Game/Screens/SelectV2/ISongSelect.cs index 388967bc4f..6c5954d82e 100644 --- a/osu.Game/Screens/SelectV2/ISongSelectBeatmapActions.cs +++ b/osu.Game/Screens/SelectV2/ISongSelect.cs @@ -2,13 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; +using osu.Game.Scoring; namespace osu.Game.Screens.SelectV2 { /// /// Actions exposed by song select which are used by subcomponents to perform top-level operations. /// - public interface ISongSelectBeatmapActions + public interface ISongSelect { /// /// Requests the user for confirmation to delete the given beatmap set. @@ -44,5 +45,10 @@ namespace osu.Game.Screens.SelectV2 /// Hides a beatmap from user's vision. /// void Hide(BeatmapInfo beatmap); + + /// + /// Present the provided score at the results screen. + /// + void PresentScore(ScoreInfo score); } } diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs index 61d16c8bf5..ba93403036 100644 --- a/osu.Game/Screens/SelectV2/SongSelect.cs +++ b/osu.Game/Screens/SelectV2/SongSelect.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -23,9 +24,11 @@ using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Volume; using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; using osu.Game.Screens.Edit; using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; +using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; using osu.Game.Skinning; using osu.Game.Utils; @@ -39,8 +42,8 @@ namespace osu.Game.Screens.SelectV2 /// This screen is intended to house all components introduced in the new song select design to add transitions and examine the overall look. /// This will be gradually built upon and ultimately replace once everything is in place. /// - [Cached(typeof(ISongSelectBeatmapActions))] - public abstract partial class SongSelect : OsuScreen, IKeyBindingHandler, ISongSelectBeatmapActions + [Cached(typeof(ISongSelect))] + public abstract partial class SongSelect : OsuScreen, IKeyBindingHandler, ISongSelect { private const float logo_scale = 0.4f; private const double fade_duration = 300; @@ -401,6 +404,18 @@ namespace osu.Game.Screens.SelectV2 #endregion + /// + /// Opens results screen with the given score. + /// This assumes active beatmap and ruleset selection matches the score. + /// + public void PresentScore(ScoreInfo score) + { + Debug.Assert(Beatmap.Value.BeatmapInfo.Equals(score.BeatmapInfo)); + Debug.Assert(Ruleset.Value.Equals(score.Ruleset)); + + this.Push(new SoloResultsScreen(score)); + } + #region Beatmap management public virtual bool EditingAllowed => false;