From 2901ec9f261d8d9963f964ef114f5c3b06063267 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Tue, 11 Feb 2020 20:05:26 -0800 Subject: [PATCH 1/8] Select specific difficulties using their icons --- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 ++++- .../Select/Carousel/CarouselBeatmapSet.cs | 5 +++- .../Carousel/DrawableCarouselBeatmapSet.cs | 28 +++++++++++++++++-- osu.Game/Screens/Select/SongSelect.cs | 1 + 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 592e26adc2..1777527ced 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -53,6 +53,11 @@ public class BeatmapCarousel : CompositeDrawable /// public Action SelectionChanged; + /// + /// Raised when user finalises beatmap selection using + /// + public Action SelectionFinalised; + public override bool HandleNonPositionalInput => AllowSelection; public override bool HandlePositionalInput => AllowSelection; @@ -577,7 +582,7 @@ private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet); + var set = new CarouselBeatmapSet(beatmapSet, this); foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 301d0d4dae..66ee4d2aee 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -15,8 +15,9 @@ public class CarouselBeatmapSet : CarouselGroupEagerSelect public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; + public BeatmapCarousel Carousel; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet) + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, BeatmapCarousel carousel) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -24,6 +25,8 @@ public CarouselBeatmapSet(BeatmapSetInfo beatmapSet) .Where(b => !b.Hidden) .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); + + Carousel = carousel; } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 699e01bca7..536fca9e6f 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -33,10 +34,13 @@ public class DrawableCarouselBeatmapSet : DrawableCarouselItem, IHasContextMenu private DialogOverlay dialogOverlay; private readonly BeatmapSetInfo beatmapSet; + private BeatmapCarousel carousel; + public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) : base(set) { beatmapSet = set.BeatmapSet; + carousel = set.Carousel; } [BackgroundDependencyLoader(true)] @@ -117,7 +121,7 @@ private IEnumerable getDifficultyIcons() return beatmaps.Count > maximum_difficulty_icons ? (IEnumerable)beatmaps.GroupBy(b => b.Beatmap.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) - : beatmaps.Select(b => new FilterableDifficultyIcon(b)); + : beatmaps.Select(b => new FilterableDifficultyIcon(b, carousel)); } public MenuItem[] ContextMenuItems @@ -210,12 +214,32 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - public FilterableDifficultyIcon(CarouselBeatmap item) + private BeatmapCarousel carousel; + private BeatmapInfo info; + + public FilterableDifficultyIcon(CarouselBeatmap item, BeatmapCarousel carousel) : base(item.Beatmap) { filtered.BindTo(item.Filtered); filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); + + this.carousel = carousel; + info = item.Beatmap; + } + + protected override bool OnClick(ClickEvent e) + { + if(e.AltPressed || carousel.SelectedBeatmap == info) + { + Schedule(() => carousel.SelectionFinalised?.Invoke(info)); + } + else + { + carousel.SelectBeatmap(info); + } + + return true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 5037081b5e..bfa693cf3d 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -155,6 +155,7 @@ private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, S Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, SelectionChanged = updateSelectedBeatmap, + SelectionFinalised = beatmapInfo => { FinaliseSelection(beatmapInfo); }, BeatmapSetsChanged = carouselBeatmapsLoaded, }, } From b126c002924788acd7dd62cc410821db081da0fa Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Wed, 12 Feb 2020 19:05:08 -0800 Subject: [PATCH 2/8] Use dependency loader to get SongSelect instance --- osu.Game/Screens/Select/BeatmapCarousel.cs | 7 +---- .../Select/Carousel/CarouselBeatmapSet.cs | 5 +--- .../Carousel/DrawableCarouselBeatmapSet.cs | 27 ++++++++++--------- osu.Game/Screens/Select/SongSelect.cs | 3 +-- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 1777527ced..592e26adc2 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -53,11 +53,6 @@ public class BeatmapCarousel : CompositeDrawable /// public Action SelectionChanged; - /// - /// Raised when user finalises beatmap selection using - /// - public Action SelectionFinalised; - public override bool HandleNonPositionalInput => AllowSelection; public override bool HandlePositionalInput => AllowSelection; @@ -582,7 +577,7 @@ private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet, this); + var set = new CarouselBeatmapSet(beatmapSet); foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 66ee4d2aee..301d0d4dae 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -15,9 +15,8 @@ public class CarouselBeatmapSet : CarouselGroupEagerSelect public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; - public BeatmapCarousel Carousel; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, BeatmapCarousel carousel) + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -25,8 +24,6 @@ public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, BeatmapCarousel carousel) .Where(b => !b.Hidden) .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); - - Carousel = carousel; } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 536fca9e6f..fd13bdc3eb 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -34,18 +34,20 @@ public class DrawableCarouselBeatmapSet : DrawableCarouselItem, IHasContextMenu private DialogOverlay dialogOverlay; private readonly BeatmapSetInfo beatmapSet; - private BeatmapCarousel carousel; + private SongSelect songSelect; public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) : base(set) { beatmapSet = set.BeatmapSet; - carousel = set.Carousel; } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) + private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { + if(songSelect != null) + this.songSelect = songSelect; + restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; if (beatmapOverlay != null) @@ -121,7 +123,7 @@ private IEnumerable getDifficultyIcons() return beatmaps.Count > maximum_difficulty_icons ? (IEnumerable)beatmaps.GroupBy(b => b.Beatmap.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) - : beatmaps.Select(b => new FilterableDifficultyIcon(b, carousel)); + : beatmaps.Select(b => new FilterableDifficultyIcon(b, songSelect, songSelect.Carousel)); } public MenuItem[] ContextMenuItems @@ -214,32 +216,33 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); + private SongSelect songSelect; private BeatmapCarousel carousel; private BeatmapInfo info; - public FilterableDifficultyIcon(CarouselBeatmap item, BeatmapCarousel carousel) + public FilterableDifficultyIcon(CarouselBeatmap item, SongSelect songSelect, BeatmapCarousel carousel) : base(item.Beatmap) { filtered.BindTo(item.Filtered); filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); + this.songSelect = songSelect; this.carousel = carousel; info = item.Beatmap; } protected override bool OnClick(ClickEvent e) { - if(e.AltPressed || carousel.SelectedBeatmap == info) + if(!filtered.Value) { - Schedule(() => carousel.SelectionFinalised?.Invoke(info)); - } - else - { - carousel.SelectBeatmap(info); + carousel?.SelectBeatmap(info); + + if (e.AltPressed) + songSelect?.FinaliseSelection(); } - return true; + return base.OnClick(e); } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bfa693cf3d..463a17989a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -80,7 +80,7 @@ public abstract class SongSelect : OsuScreen, IKeyBindingHandler protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - protected BeatmapCarousel Carousel { get; private set; } + public BeatmapCarousel Carousel { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -155,7 +155,6 @@ private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, S Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, SelectionChanged = updateSelectedBeatmap, - SelectionFinalised = beatmapInfo => { FinaliseSelection(beatmapInfo); }, BeatmapSetsChanged = carouselBeatmapsLoaded, }, } From f8b69fe63285186a2e6b661e875353696e86ce1f Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Wed, 12 Feb 2020 20:11:39 -0800 Subject: [PATCH 3/8] Remove unnecessary carousel variable, fix code formatting --- .../Carousel/DrawableCarouselBeatmapSet.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index fd13bdc3eb..80dae575ff 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -45,7 +45,7 @@ public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) [BackgroundDependencyLoader(true)] private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { - if(songSelect != null) + if (songSelect != null) this.songSelect = songSelect; restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); @@ -123,7 +123,7 @@ private IEnumerable getDifficultyIcons() return beatmaps.Count > maximum_difficulty_icons ? (IEnumerable)beatmaps.GroupBy(b => b.Beatmap.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) - : beatmaps.Select(b => new FilterableDifficultyIcon(b, songSelect, songSelect.Carousel)); + : beatmaps.Select(b => new FilterableDifficultyIcon(b, songSelect)); } public MenuItem[] ContextMenuItems @@ -216,11 +216,10 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - private SongSelect songSelect; - private BeatmapCarousel carousel; - private BeatmapInfo info; + private readonly SongSelect songSelect; + private readonly BeatmapInfo info; - public FilterableDifficultyIcon(CarouselBeatmap item, SongSelect songSelect, BeatmapCarousel carousel) + public FilterableDifficultyIcon(CarouselBeatmap item, SongSelect songSelect) : base(item.Beatmap) { filtered.BindTo(item.Filtered); @@ -228,15 +227,14 @@ public FilterableDifficultyIcon(CarouselBeatmap item, SongSelect songSelect, Bea filtered.TriggerChange(); this.songSelect = songSelect; - this.carousel = carousel; info = item.Beatmap; } protected override bool OnClick(ClickEvent e) { - if(!filtered.Value) + if (!filtered.Value) { - carousel?.SelectBeatmap(info); + songSelect?.Carousel.SelectBeatmap(info); if (e.AltPressed) songSelect?.FinaliseSelection(); From ad0de2796427ee9025d01afe82fe062d9a0a2b8e Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Wed, 12 Feb 2020 22:11:26 -0800 Subject: [PATCH 4/8] Safer dependency injection and accessibility levels --- osu.Game/OsuGame.cs | 47 +++++++++++-------- .../Carousel/DrawableCarouselBeatmapSet.cs | 26 +++++----- osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ff3dee55af..3d6b93c87d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -311,22 +311,12 @@ public void ShowChannel(string channel) => waitForReady(() => channelManager, _ public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); /// - /// Present a beatmap at song select immediately. + /// Present a specific beatmap difficulty at song select immediately. /// The user should have already requested this interactively. /// /// The beatmap to select. - public void PresentBeatmap(BeatmapSetInfo beatmap) + public void PresentBeatmap(BeatmapInfo beatmap) { - var databasedSet = beatmap.OnlineBeatmapSetID != null - ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) - : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); - - if (databasedSet == null) - { - Logger.Log("The requested beatmap could not be loaded.", LoggingTarget.Information); - return; - } - PerformFromScreen(screen => { // we might already be at song select, so a check is required before performing the load to solo. @@ -334,19 +324,36 @@ public void PresentBeatmap(BeatmapSetInfo beatmap) menuScreen.LoadToSolo(); // we might even already be at the song - if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash) - { + if (Beatmap.Value.BeatmapInfo.Hash == beatmap.Hash) return; - } - // Use first beatmap available for current ruleset, else switch ruleset. - var first = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); - - Ruleset.Value = first.Ruleset; - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); + Ruleset.Value = beatmap.Ruleset; + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(beatmap); }, validScreens: new[] { typeof(PlaySongSelect) }); } + /// + /// + /// Instead of selecting a specific difficulty, this will select the first difficulty of the current ruleset in a beatmapset, + /// or the first difficulty of the set if there is none. + /// + /// The beatmapset to select. + public void PresentBeatmap(BeatmapSetInfo beatmapSet) + { + var databasedSet = beatmapSet.OnlineBeatmapSetID != null + ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID) + : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmapSet.Hash); + + if (databasedSet == null) + { + Logger.Log("The requested beatmap could not be loaded.", LoggingTarget.Information); + return; + } + + var first = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); + PresentBeatmap(first); + } + /// /// Present a score's replay immediately. /// The user should have already requested this interactively. diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 80dae575ff..9e4f31b15b 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -34,8 +34,6 @@ public class DrawableCarouselBeatmapSet : DrawableCarouselItem, IHasContextMenu private DialogOverlay dialogOverlay; private readonly BeatmapSetInfo beatmapSet; - private SongSelect songSelect; - public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) : base(set) { @@ -43,11 +41,8 @@ public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) } [BackgroundDependencyLoader(true)] - private void load(SongSelect songSelect, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) + private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { - if (songSelect != null) - this.songSelect = songSelect; - restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; if (beatmapOverlay != null) @@ -123,7 +118,7 @@ private IEnumerable getDifficultyIcons() return beatmaps.Count > maximum_difficulty_icons ? (IEnumerable)beatmaps.GroupBy(b => b.Beatmap.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) - : beatmaps.Select(b => new FilterableDifficultyIcon(b, songSelect)); + : beatmaps.Select(b => new FilterableDifficultyIcon(b)); } public MenuItem[] ContextMenuItems @@ -216,17 +211,17 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - private readonly SongSelect songSelect; + private OsuGame game; + private SongSelect songSelect; private readonly BeatmapInfo info; - public FilterableDifficultyIcon(CarouselBeatmap item, SongSelect songSelect) + public FilterableDifficultyIcon(CarouselBeatmap item) : base(item.Beatmap) { filtered.BindTo(item.Filtered); filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); - this.songSelect = songSelect; info = item.Beatmap; } @@ -234,7 +229,7 @@ protected override bool OnClick(ClickEvent e) { if (!filtered.Value) { - songSelect?.Carousel.SelectBeatmap(info); + game.PresentBeatmap(info); if (e.AltPressed) songSelect?.FinaliseSelection(); @@ -242,6 +237,15 @@ protected override bool OnClick(ClickEvent e) return base.OnClick(e); } + + [BackgroundDependencyLoader] + private void load(OsuGame game, SongSelect songSelect) + { + this.game = game; + + if (songSelect != null) + this.songSelect = songSelect; + } } public class FilterableGroupedDifficultyIcon : GroupedDifficultyIcon diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 463a17989a..5037081b5e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -80,7 +80,7 @@ public abstract class SongSelect : OsuScreen, IKeyBindingHandler protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - public BeatmapCarousel Carousel { get; private set; } + protected BeatmapCarousel Carousel { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; From c871f07d2ef9f55d1c91f9ac6138fd2d162e7ed3 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Thu, 13 Feb 2020 17:14:46 -0800 Subject: [PATCH 5/8] Use CarouselBeatmap action to select beatmap --- osu.Game/OsuGame.cs | 55 ++++++++----------- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 + .../Select/Carousel/CarouselBeatmap.cs | 5 ++ .../Carousel/DrawableCarouselBeatmapSet.cs | 23 +------- 4 files changed, 34 insertions(+), 51 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3d6b93c87d..79616ef97c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -311,38 +311,15 @@ public void ShowChannel(string channel) => waitForReady(() => channelManager, _ public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); /// - /// Present a specific beatmap difficulty at song select immediately. + /// Present a beatmap at song select immediately. /// The user should have already requested this interactively. /// /// The beatmap to select. - public void PresentBeatmap(BeatmapInfo beatmap) + public void PresentBeatmap(BeatmapSetInfo beatmap) { - PerformFromScreen(screen => - { - // we might already be at song select, so a check is required before performing the load to solo. - if (screen is MainMenu) - menuScreen.LoadToSolo(); - - // we might even already be at the song - if (Beatmap.Value.BeatmapInfo.Hash == beatmap.Hash) - return; - - Ruleset.Value = beatmap.Ruleset; - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(beatmap); - }, validScreens: new[] { typeof(PlaySongSelect) }); - } - - /// - /// - /// Instead of selecting a specific difficulty, this will select the first difficulty of the current ruleset in a beatmapset, - /// or the first difficulty of the set if there is none. - /// - /// The beatmapset to select. - public void PresentBeatmap(BeatmapSetInfo beatmapSet) - { - var databasedSet = beatmapSet.OnlineBeatmapSetID != null - ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID) - : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmapSet.Hash); + var databasedSet = beatmap.OnlineBeatmapSetID != null + ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) + : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); if (databasedSet == null) { @@ -350,8 +327,24 @@ public void PresentBeatmap(BeatmapSetInfo beatmapSet) return; } - var first = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); - PresentBeatmap(first); + PerformFromScreen(screen => + { + // we might already be at song select, so a check is required before performing the load to solo. + if (screen is MainMenu) + menuScreen.LoadToSolo(); + + // we might even already be at the song + if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash) + { + return; + } + + // Use first beatmap available for current ruleset, else switch ruleset. + var first = databasedSet.Beatmaps.Find(b => b.Ruleset.Equals(Ruleset.Value)) ?? databasedSet.Beatmaps.First(); + + Ruleset.Value = first.Ruleset; + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); + }, validScreens: new[] { typeof(PlaySongSelect) }); } /// @@ -453,7 +446,7 @@ private void currentTrackCompleted() => Schedule(() => /// /// The action to perform once we are in the correct state. /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. - protected void PerformFromScreen(Action action, IEnumerable validScreens = null) + public void PerformFromScreen(Action action, IEnumerable validScreens = null) { performFromMainMenuTask?.Cancel(); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 592e26adc2..f6e0e6bf70 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -592,6 +592,8 @@ private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) scrollPositionCache.Invalidate(); } }; + + c.Select = () => SelectBeatmap(c.Beatmap); } return set; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 2ffb73f226..116053b4f2 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -13,6 +13,11 @@ public class CarouselBeatmap : CarouselItem { public readonly BeatmapInfo Beatmap; + /// + /// Select this beatmap on the carousel. + /// + public Action Select; + public CarouselBeatmap(BeatmapInfo beatmap) { Beatmap = beatmap; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 9e4f31b15b..5ef0e8a018 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -211,9 +211,7 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - private OsuGame game; - private SongSelect songSelect; - private readonly BeatmapInfo info; + private readonly Action select; public FilterableDifficultyIcon(CarouselBeatmap item) : base(item.Beatmap) @@ -222,30 +220,15 @@ public FilterableDifficultyIcon(CarouselBeatmap item) filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); - info = item.Beatmap; + select = item.Select; } protected override bool OnClick(ClickEvent e) { - if (!filtered.Value) - { - game.PresentBeatmap(info); - - if (e.AltPressed) - songSelect?.FinaliseSelection(); - } + select?.Invoke(); return base.OnClick(e); } - - [BackgroundDependencyLoader] - private void load(OsuGame game, SongSelect songSelect) - { - this.game = game; - - if (songSelect != null) - this.songSelect = songSelect; - } } public class FilterableGroupedDifficultyIcon : GroupedDifficultyIcon From 368e6f9579492d6321c89850cf4fe114a0337c83 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Thu, 13 Feb 2020 17:47:16 -0800 Subject: [PATCH 6/8] Use CarouselBeatmap.State to select --- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 -- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 5 ----- .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 8 ++++---- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 79616ef97c..ff3dee55af 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -446,7 +446,7 @@ private void currentTrackCompleted() => Schedule(() => /// /// The action to perform once we are in the correct state. /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. - public void PerformFromScreen(Action action, IEnumerable validScreens = null) + protected void PerformFromScreen(Action action, IEnumerable validScreens = null) { performFromMainMenuTask?.Cancel(); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index f6e0e6bf70..592e26adc2 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -592,8 +592,6 @@ private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) scrollPositionCache.Invalidate(); } }; - - c.Select = () => SelectBeatmap(c.Beatmap); } return set; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 116053b4f2..2ffb73f226 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -13,11 +13,6 @@ public class CarouselBeatmap : CarouselItem { public readonly BeatmapInfo Beatmap; - /// - /// Select this beatmap on the carousel. - /// - public Action Select; - public CarouselBeatmap(BeatmapInfo beatmap) { Beatmap = beatmap; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 5ef0e8a018..572580f7c1 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -211,7 +211,7 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - private readonly Action select; + private readonly CarouselBeatmap item; public FilterableDifficultyIcon(CarouselBeatmap item) : base(item.Beatmap) @@ -220,14 +220,14 @@ public FilterableDifficultyIcon(CarouselBeatmap item) filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); - select = item.Select; + this.item = item; } protected override bool OnClick(ClickEvent e) { - select?.Invoke(); + item.State.Value = CarouselItemState.Selected; - return base.OnClick(e); + return true; } } From 8e1ecddb1dbd05a81b71ef14f513bdd733fd2807 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Fri, 14 Feb 2020 17:23:24 -0800 Subject: [PATCH 7/8] Add testing --- .../SongSelect/TestScenePlaySongSelect.cs | 40 +++++++++++++++++++ .../Carousel/DrawableCarouselBeatmapSet.cs | 7 ++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 80192b9ebc..2b6d7cce58 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -463,6 +463,46 @@ public void TestHideSetSelectsCorrectBeatmap() AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); } + [Test] + public void TestDifficultyIconSelecting() + { + int? previousID = null; + addRulesetImportStep(0); + createSongSelect(); + + AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); + AddStep("Click on a difficulty", () => + { + InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() + .First(icon => icon.Item.Beatmap != songSelect.Carousel.SelectedBeatmap)); + + InputManager.PressButton(MouseButton.Left); + InputManager.ReleaseButton(MouseButton.Left); + }); + AddAssert("Selected beatmap changed", () => songSelect.Carousel.SelectedBeatmap.ID != previousID); + + AddStep("Filter some difficulties", () => songSelect.Carousel.Filter(new FilterCriteria + { + BPM = new FilterCriteria.OptionalRange + { + Min = songSelect.Carousel.SelectedBeatmapSet.MaxBPM, + IsLowerInclusive = true + } + })); + AddUntilStep("Wait for filter", () => songSelect.Carousel.ChildrenOfType().Any(icon => icon.Item.Filtered.Value)); + + AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); + AddStep("Click on a filtered difficulty", () => + { + InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() + .First(icon => icon.Item.Filtered.Value)); + + InputManager.PressButton(MouseButton.Left); + InputManager.ReleaseButton(MouseButton.Left); + }); + AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); + } + private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id)); private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait(); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 572580f7c1..4b98c4eeb2 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -211,7 +211,7 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - private readonly CarouselBeatmap item; + public readonly CarouselBeatmap Item; public FilterableDifficultyIcon(CarouselBeatmap item) : base(item.Beatmap) @@ -220,12 +220,13 @@ public FilterableDifficultyIcon(CarouselBeatmap item) filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); - this.item = item; + Item = item; } protected override bool OnClick(ClickEvent e) { - item.State.Value = CarouselItemState.Selected; + if (!filtered.Value) + Item.State.Value = CarouselItemState.Selected; return true; } From 8c81f1e684ce4e4b32531f8fb4807b6cc087f002 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Sat, 15 Feb 2020 17:51:55 -0800 Subject: [PATCH 8/8] Move CarouselBeatmap access to private, test using indices --- .../SongSelect/TestScenePlaySongSelect.cs | 44 +++++++++++++++---- .../Carousel/DrawableCarouselBeatmapSet.cs | 6 +-- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 2b6d7cce58..369ae346dc 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -466,36 +466,53 @@ public void TestHideSetSelectsCorrectBeatmap() [Test] public void TestDifficultyIconSelecting() { - int? previousID = null; addRulesetImportStep(0); createSongSelect(); - AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); + DrawableCarouselBeatmapSet set = null; + AddStep("Find the DrawableCarouselBeatmapSet", () => + { + set = songSelect.Carousel.ChildrenOfType().First(); + }); + + DrawableCarouselBeatmapSet.FilterableDifficultyIcon difficultyIcon = null; + AddStep("Find an icon", () => + { + difficultyIcon = set.ChildrenOfType() + .First(icon => getDifficultyIconIndex(set, icon) != getCurrentBeatmapIndex()); + }); AddStep("Click on a difficulty", () => { - InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() - .First(icon => icon.Item.Beatmap != songSelect.Carousel.SelectedBeatmap)); + InputManager.MoveMouseTo(difficultyIcon); InputManager.PressButton(MouseButton.Left); InputManager.ReleaseButton(MouseButton.Left); }); - AddAssert("Selected beatmap changed", () => songSelect.Carousel.SelectedBeatmap.ID != previousID); + AddAssert("Selected beatmap correct", () => getCurrentBeatmapIndex() == getDifficultyIconIndex(set, difficultyIcon)); + double? maxBPM = null; AddStep("Filter some difficulties", () => songSelect.Carousel.Filter(new FilterCriteria { BPM = new FilterCriteria.OptionalRange { - Min = songSelect.Carousel.SelectedBeatmapSet.MaxBPM, + Min = maxBPM = songSelect.Carousel.SelectedBeatmapSet.MaxBPM, IsLowerInclusive = true } })); - AddUntilStep("Wait for filter", () => songSelect.Carousel.ChildrenOfType().Any(icon => icon.Item.Filtered.Value)); + DrawableCarouselBeatmapSet.FilterableDifficultyIcon filteredIcon = null; + AddStep("Get filtered icon", () => + { + var filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.Find(b => b.BPM < maxBPM); + int filteredBeatmapIndex = getBeatmapIndex(filteredBeatmap.BeatmapSet, filteredBeatmap); + filteredIcon = set.ChildrenOfType().ElementAt(filteredBeatmapIndex); + }); + + int? previousID = null; AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); AddStep("Click on a filtered difficulty", () => { - InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() - .First(icon => icon.Item.Filtered.Value)); + InputManager.MoveMouseTo(filteredIcon); InputManager.PressButton(MouseButton.Left); InputManager.ReleaseButton(MouseButton.Left); @@ -503,6 +520,15 @@ public void TestDifficultyIconSelecting() AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); } + private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info); + + private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmap); + + private int getDifficultyIconIndex(DrawableCarouselBeatmapSet set, DrawableCarouselBeatmapSet.FilterableDifficultyIcon icon) + { + return set.ChildrenOfType().ToList().FindIndex(i => i == icon); + } + private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id)); private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait(); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 4b98c4eeb2..3d958dc67f 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -211,7 +211,7 @@ public class FilterableDifficultyIcon : DifficultyIcon { private readonly BindableBool filtered = new BindableBool(); - public readonly CarouselBeatmap Item; + private readonly CarouselBeatmap item; public FilterableDifficultyIcon(CarouselBeatmap item) : base(item.Beatmap) @@ -220,13 +220,13 @@ public FilterableDifficultyIcon(CarouselBeatmap item) filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.TriggerChange(); - Item = item; + this.item = item; } protected override bool OnClick(ClickEvent e) { if (!filtered.Value) - Item.State.Value = CarouselItemState.Selected; + item.State.Value = CarouselItemState.Selected; return true; }