From 4a7ee6fafc75240a36a810e79f01eced48ecda5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Sep 2025 10:24:25 +0200 Subject: [PATCH] Hide object-typed `CurrentSelection` and expose strongly-typed alternatives instead --- .../Background/TestSceneUserDimBackgrounds.cs | 2 +- .../SongSelectV2/BeatmapCarouselTestScene.cs | 17 +++---- .../TestSceneBeatmapCarouselArtistGrouping.cs | 4 +- ...tSceneBeatmapCarouselDifficultyGrouping.cs | 6 +-- .../TestSceneBeatmapCarouselFiltering.cs | 20 ++++---- .../TestSceneBeatmapCarouselNoGrouping.cs | 12 ++--- .../TestSceneBeatmapCarouselRandom.cs | 16 +++--- .../TestSceneBeatmapCarouselScrolling.cs | 10 ++-- .../TestSceneBeatmapCarouselUpdateHandling.cs | 20 ++++---- ...neSongSelectCurrentSelectionInvalidated.cs | 2 +- osu.Game/Graphics/Carousel/Carousel.cs | 2 +- osu.Game/Screens/SelectV2/BeatmapCarousel.cs | 51 +++++++++++++------ osu.Game/Screens/SelectV2/SongSelect.cs | 6 +-- 13 files changed, 93 insertions(+), 75 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 58fb02c90c..3021589cdb 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -325,7 +325,7 @@ namespace osu.Game.Tests.Visual.Background private void setupUserSettings() { AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen()); - AddUntilStep("Song select has selection", () => songSelect.Carousel?.CurrentSelection != null); + AddUntilStep("Song select has selection", () => songSelect.Carousel?.CurrentGroupedBeatmap != null); AddStep("Set default user settings", () => { SelectedMods.Value = new[] { new OsuModNoFail() }; diff --git a/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs b/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs index b616055157..a180097863 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs @@ -118,14 +118,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2 RequestSelection = b => { BeatmapRequestedSelections.Push(b.Beatmap); - Carousel.CurrentSelection = b; + Carousel.CurrentGroupedBeatmap = b; }, RequestRecommendedSelection = groupedBeatmaps => { var recommendedBeatmap = BeatmapRecommendationFunction?.Invoke(groupedBeatmaps.Select(gb => gb.Beatmap)) ?? groupedBeatmaps.First().Beatmap; var recommendedGroupedBeatmap = groupedBeatmaps.First(gb => gb.Beatmap.Equals(recommendedBeatmap)); BeatmapSetRequestedSelections.Push(recommendedBeatmap.BeatmapSet!); - Carousel.CurrentSelection = recommendedGroupedBeatmap; + Carousel.CurrentGroupedBeatmap = recommendedGroupedBeatmap; }, BleedTop = 50, BleedBottom = 50, @@ -219,8 +219,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2 protected void Select() => AddStep("select", () => InputManager.Key(Key.Enter)); - protected void CheckNoSelection() => AddAssert("has no selection", () => Carousel.CurrentSelection, () => Is.Null); - protected void CheckHasSelection() => AddAssert("has selection", () => Carousel.CurrentSelection, () => Is.Not.Null); + protected void CheckNoSelection() => AddAssert("has no selection", () => Carousel.CurrentGroupedBeatmap, () => Is.Null); + protected void CheckHasSelection() => AddAssert("has selection", () => Carousel.CurrentGroupedBeatmap, () => Is.Not.Null); protected void CheckRequestPresentCount(int expected) => AddAssert($"check present count is {expected}", () => Carousel.RequestPresentBeatmapCount, () => Is.EqualTo(expected)); @@ -285,8 +285,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 // offset by one because the group itself is included in the items list. CarouselItem item = groupingFilter.GroupItems[groupDefinition].ElementAt(panel + 1); - return (Carousel.CurrentSelection as GroupedBeatmap)? - .Equals(item.Model as GroupedBeatmap) == true; + return Carousel.CurrentGroupedBeatmap?.Equals(item.Model as GroupedBeatmap) == true; }); } @@ -295,12 +294,12 @@ namespace osu.Game.Tests.Visual.SongSelectV2 if (diff != null) { AddUntilStep($"selected is set{set} diff{diff.Value}", - () => (Carousel.CurrentSelection as GroupedBeatmap)?.Beatmap, + () => Carousel.CurrentBeatmap, () => Is.EqualTo(BeatmapSets[set].Beatmaps[diff.Value])); } else { - AddUntilStep($"selected is set{set}", () => BeatmapSets[set].Beatmaps.Contains(((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap)); + AddUntilStep($"selected is set{set}", () => BeatmapSets[set].Beatmaps.Contains(Carousel.CurrentBeatmap!)); } } @@ -419,7 +418,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 tracked: {Carousel.ItemsTracked} displayable: {Carousel.DisplayableItems} displayed: {Carousel.VisibleItems} - selected: {Carousel.CurrentSelection} + selected: {Carousel.CurrentGroupedBeatmap} """); void createHeader(string text) diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs index 78b6985fdb..c34077889d 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 CheckHasSelection(); AddAssert("drawable selection non-null", () => selection, () => Is.Not.Null); - AddAssert("drawable selection matches carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentSelection)); + AddAssert("drawable selection matches carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); RemoveAllBeatmaps(); AddUntilStep("no drawable selection", GetSelectedPanel, () => Is.Null); @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddStep("add previous selection", () => BeatmapSets.Add(((GroupedBeatmap)selection!).Beatmap.BeatmapSet!)); - AddAssert("selection matches original carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentSelection)); + AddAssert("selection matches original carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); AddUntilStep("drawable selection restored", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(selection)); AddAssert("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True); diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs index c28860e368..58ecfcbf3b 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 CheckHasSelection(); AddAssert("drawable selection non-null", () => selection, () => Is.Not.Null); - AddAssert("drawable selection matches carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentSelection)); + AddAssert("drawable selection matches carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); RemoveAllBeatmaps(); AddUntilStep("no drawable selection", GetSelectedPanel, () => Is.Null); @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddStep("add previous selection", () => BeatmapSets.Add(((GroupedBeatmap)selection!).Beatmap.BeatmapSet!)); - AddAssert("selection matches original carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentSelection)); + AddAssert("selection matches original carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); AddUntilStep("drawable selection restored", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(selection)); AddAssert("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True); @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 } private void checkBeatmapIsKeyboardSelected() => - AddUntilStep("check keyboard selected group is beatmap", () => GetKeyboardSelectedPanel()?.Item?.Model, () => Is.EqualTo(Carousel.CurrentSelection)); + AddUntilStep("check keyboard selected group is beatmap", () => GetKeyboardSelectedPanel()?.Item?.Model, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); private void checkGroupKeyboardSelected(int index) => AddUntilStep($"check keyboard selected group is {index}", () => GetKeyboardSelectedPanel()?.Item?.Model, () => { diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs index b232d12e46..b1bd9fd3ed 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs @@ -130,14 +130,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextPanel(); Select(); - AddStep("record selection", () => selectedID = ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap.ID); + AddStep("record selection", () => selectedID = Carousel.CurrentBeatmap!.ID); for (int i = 0; i < 5; i++) { ApplyToFilterAndWaitForFilter("filter all", c => c.SearchText = Guid.NewGuid().ToString()); - AddAssert("selection not changed", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap.ID == selectedID); + AddAssert("selection not changed", () => Carousel.CurrentBeatmap!.ID == selectedID); ApplyToFilterAndWaitForFilter("remove filter", c => c.SearchText = string.Empty); - AddAssert("selection not changed", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap.ID == selectedID); + AddAssert("selection not changed", () => Carousel.CurrentBeatmap!.ID == selectedID); } } @@ -177,14 +177,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextSet(); - AddStep("record selection", () => selectedID = ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap.ID); + AddStep("record selection", () => selectedID = Carousel.CurrentBeatmap!.ID); for (int i = 0; i < 5; i++) { ApplyToFilterAndWaitForFilter("filter all", c => c.SearchText = Guid.NewGuid().ToString()); - AddAssert("selection not changed", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap.ID == selectedID); + AddAssert("selection not changed", () => Carousel.CurrentBeatmap!.ID == selectedID); ApplyToFilterAndWaitForFilter("remove filter", c => c.SearchText = string.Empty); - AddAssert("selection not changed", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap.ID == selectedID); + AddAssert("selection not changed", () => Carousel.CurrentBeatmap!.ID == selectedID); } } @@ -200,14 +200,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { int diff = i; - AddStep($"select diff {diff}", () => Carousel.CurrentSelection = chosenBeatmap = BeatmapSets[20].Beatmaps[diff]); - AddUntilStep("selection changed", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(chosenBeatmap)); + AddStep($"select diff {diff}", () => Carousel.CurrentBeatmap = chosenBeatmap = BeatmapSets[20].Beatmaps[diff]); + AddUntilStep("selection changed", () => Carousel.CurrentBeatmap, () => Is.EqualTo(chosenBeatmap)); SortBy(SortMode.Difficulty); - AddAssert("selection retained", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(chosenBeatmap)); + AddAssert("selection retained", () => Carousel.CurrentBeatmap, () => Is.EqualTo(chosenBeatmap)); SortBy(SortMode.Title); - AddAssert("selection retained", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(chosenBeatmap)); + AddAssert("selection retained", () => Carousel.CurrentBeatmap, () => Is.EqualTo(chosenBeatmap)); } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs index 0b0f93b3bc..c839a28055 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 CheckHasSelection(); AddAssert("drawable selection non-null", () => selection, () => Is.Not.Null); - AddAssert("drawable selection matches carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentSelection)); + AddAssert("drawable selection matches carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); RemoveAllBeatmaps(); AddUntilStep("no drawable selection", GetSelectedPanel, () => Is.Null); @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddStep("add previous selection", () => BeatmapSets.Add(((GroupedBeatmap)selection!).Beatmap.BeatmapSet!)); - AddAssert("selection matches original carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentSelection)); + AddAssert("selection matches original carousel selection", () => selection, () => Is.EqualTo(Carousel.CurrentGroupedBeatmap)); AddUntilStep("drawable selection restored", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(selection)); AddAssert("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True); } @@ -389,15 +389,15 @@ namespace osu.Game.Tests.Visual.SongSelectV2 private void checkSelectionIterating(bool isIterating) { - object? selection = null; + GroupedBeatmap? selection = null; for (int i = 0; i < 3; i++) { - AddStep("store selection", () => selection = Carousel.CurrentSelection); + AddStep("store selection", () => selection = Carousel.CurrentGroupedBeatmap); if (isIterating) - AddUntilStep("selection changed", () => Carousel.CurrentSelection != selection); + AddUntilStep("selection changed", () => Carousel.CurrentGroupedBeatmap != selection); else - AddUntilStep("selection not changed", () => Carousel.CurrentSelection == selection); + AddUntilStep("selection not changed", () => Carousel.CurrentGroupedBeatmap == selection); } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselRandom.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselRandom.cs index 9f31c875b6..ce68d587c8 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselRandom.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselRandom.cs @@ -50,12 +50,12 @@ namespace osu.Game.Tests.Visual.SongSelectV2 nextRandom(); ensureRandomDidNotRepeat(); - AddStep("store selection", () => originalSelected = ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap); + AddStep("store selection", () => originalSelected = Carousel.CurrentBeatmap!); SortAndGroupBy(SortMode.Artist, GroupMode.Difficulty); WaitForFiltering(); - AddAssert("selection not changed", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(originalSelected)); + AddAssert("selection not changed", () => Carousel.CurrentBeatmap, () => Is.EqualTo(originalSelected)); storeExpandedGroup(); @@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 nextRandom(); CheckHasSelection(); - AddStep("store selection", () => originalSelected = ((GroupedBeatmap)Carousel.CurrentSelection!)); + AddStep("store selection", () => originalSelected = Carousel.CurrentGroupedBeatmap!); AddStep("random then rewind", () => { @@ -290,7 +290,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 Carousel.PreviousRandom(); }); - AddAssert("selection not changed", () => Carousel.CurrentSelection, () => Is.EqualTo(originalSelected)); + AddAssert("selection not changed", () => Carousel.CurrentGroupedBeatmap, () => Is.EqualTo(originalSelected)); } [Test] @@ -305,20 +305,20 @@ namespace osu.Game.Tests.Visual.SongSelectV2 nextRandom(); CheckHasSelection(); - AddStep("store selection", () => originalSelected = (GroupedBeatmap)Carousel.CurrentSelection!); + AddStep("store selection", () => originalSelected = Carousel.CurrentGroupedBeatmap!); nextRandom(); - AddStep("store selection", () => postRandomSelection = (GroupedBeatmap)Carousel.CurrentSelection!); + AddStep("store selection", () => postRandomSelection = Carousel.CurrentGroupedBeatmap!); AddAssert("selection changed", () => originalSelected, () => Is.Not.SameAs(postRandomSelection)); AddStep("delete previous selection beatmaps", () => BeatmapSets.Remove(originalSelected!.Beatmap.BeatmapSet!)); WaitForFiltering(); - AddAssert("selection not changed", () => Carousel.CurrentSelection, () => Is.EqualTo(postRandomSelection)); + AddAssert("selection not changed", () => Carousel.CurrentGroupedBeatmap, () => Is.EqualTo(postRandomSelection)); prevRandomSet(); - AddAssert("selection not changed", () => Carousel.CurrentSelection, () => Is.EqualTo(postRandomSelection)); + AddAssert("selection not changed", () => Carousel.CurrentGroupedBeatmap, () => Is.EqualTo(postRandomSelection)); } private void nextRandom() => diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs index d05c874641..c1cee4e398 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { Quad positionBefore = default; - AddStep("select middle beatmap", () => Carousel.CurrentSelection = new GroupedBeatmap(null, BeatmapSets.ElementAt(BeatmapSets.Count - 2).Beatmaps.First())); + AddStep("select middle beatmap", () => Carousel.CurrentGroupedBeatmap = new GroupedBeatmap(null, BeatmapSets.ElementAt(BeatmapSets.Count - 2).Beatmaps.First())); WaitForScrolling(); @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { Quad positionBefore = default; - AddStep("select middle beatmap", () => Carousel.CurrentSelection = new GroupedBeatmap(null, BeatmapSets.ElementAt(BeatmapSets.Count - 2).Beatmaps.First())); + AddStep("select middle beatmap", () => Carousel.CurrentGroupedBeatmap = new GroupedBeatmap(null, BeatmapSets.ElementAt(BeatmapSets.Count - 2).Beatmaps.First())); WaitForScrolling(); AddStep("override scroll with user scroll", () => @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddStep("scroll to end", () => Scroll.ScrollToEnd(false)); - AddStep("select last beatmap", () => Carousel.CurrentSelection = new GroupedBeatmap(null, BeatmapSets.Last().Beatmaps.Last())); + AddStep("select last beatmap", () => Carousel.CurrentGroupedBeatmap = new GroupedBeatmap(null, BeatmapSets.Last().Beatmaps.Last())); WaitForScrolling(); @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { Quad positionBefore = default; - AddStep("select first beatmap", () => Carousel.CurrentSelection = new GroupedBeatmap(null, BeatmapSets.First().Beatmaps.First())); + AddStep("select first beatmap", () => Carousel.CurrentGroupedBeatmap = new GroupedBeatmap(null, BeatmapSets.First().Beatmaps.First())); WaitForScrolling(); @@ -108,7 +108,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { Quad positionBefore = default; - AddStep("select first beatmap", () => Carousel.CurrentSelection = new GroupedBeatmap(null, BeatmapSets.First().Beatmaps.First())); + AddStep("select first beatmap", () => Carousel.CurrentGroupedBeatmap = new GroupedBeatmap(null, BeatmapSets.First().Beatmaps.First())); WaitForScrolling(); AddStep("override scroll with user scroll", () => diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselUpdateHandling.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselUpdateHandling.cs index a331879684..d1cef3420a 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselUpdateHandling.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselUpdateHandling.cs @@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextSet(); WaitForSetSelection(1, 0); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); updateBeatmap(b => @@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 WaitForFiltering(); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); } @@ -205,14 +205,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextSet(); WaitForSetSelection(1, 0); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); updateBeatmap(b => b.DifficultyName = "new name"); assertDidFilter(); WaitForFiltering(); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); } @@ -222,14 +222,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextSet(); WaitForSetSelection(1, 0); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); updateBeatmap(b => b.OnlineID = b.OnlineID + 1); assertDidFilter(); WaitForFiltering(); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); } @@ -239,7 +239,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextSet(); WaitForSetSelection(1, 0); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); // Add another difficulty with same online ID. @@ -252,7 +252,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 WaitForFiltering(); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); } @@ -262,7 +262,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextSet(); WaitForSetSelection(1, 0); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); // Remove original selected difficulty, and add two difficulties with same name as selection. @@ -284,7 +284,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 WaitForFiltering(); - AddAssert("selection is updateable beatmap", () => ((GroupedBeatmap)Carousel.CurrentSelection!).Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); + AddAssert("selection is updateable beatmap", () => Carousel.CurrentBeatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); AddAssert("visible panel is updateable beatmap", () => (GetSelectedPanel()?.Item?.Model as GroupedBeatmap)?.Beatmap, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0])); } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs index c480d6ca7e..7c604eb37b 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 /// public partial class TestSceneSongSelectCurrentSelectionInvalidated : SongSelectTestScene { - private BeatmapInfo? selectedBeatmap => (Carousel.CurrentSelection as GroupedBeatmap)?.Beatmap; + private BeatmapInfo? selectedBeatmap => Carousel.CurrentBeatmap; private BeatmapSetInfo? selectedBeatmapSet => selectedBeatmap?.BeatmapSet; [SetUpSteps] diff --git a/osu.Game/Graphics/Carousel/Carousel.cs b/osu.Game/Graphics/Carousel/Carousel.cs index 5adc37ea40..0df183bb71 100644 --- a/osu.Game/Graphics/Carousel/Carousel.cs +++ b/osu.Game/Graphics/Carousel/Carousel.cs @@ -107,7 +107,7 @@ namespace osu.Game.Graphics.Carousel /// The selection is never reset due to not existing. It can be set to anything. /// If no matching carousel item exists, there will be no visually selected item while waiting for potential new item which matches. /// - public virtual object? CurrentSelection + protected object? CurrentSelection { get => currentSelection.Model; set diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs index 6c98630274..ab520525a5 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs @@ -295,28 +295,47 @@ namespace osu.Game.Screens.SelectV2 protected override bool ShouldActivateOnKeyboardSelection(CarouselItem item) => grouping.BeatmapSetsGroupedTogether && item.Model is GroupedBeatmap; - public override object? CurrentSelection + /// + /// The currently selected . + /// + /// + /// The selection is never reset due to not existing. It can be set to anything. + /// If no matching carousel item exists, there will be no visually selected item while waiting for potential new item which matches. + /// + public GroupedBeatmap? CurrentGroupedBeatmap { - get => base.CurrentSelection; + get => CurrentSelection as GroupedBeatmap; + set => CurrentSelection = value; + } + + /// + /// The currently selected . + /// + /// + /// This is a property mostly dedicated to external consumers who only care about showing some particular copy of a beatmap + /// (there could be multiple panels for one beatmap due to grouping). + /// Through this property, the carousel basically figures out what group to use internally. + /// + public BeatmapInfo? CurrentBeatmap + { + get => CurrentGroupedBeatmap?.Beatmap; set { - // this is a special pathway for external consumers who only care about showing some particular copy of a beatmap - // (there could be multiple panels for one beatmap due to grouping). - // in this pathway we basically figure out what group to use internally, and continue working with `GroupedBeatmap` all the way after that. - if (value is BeatmapInfo beatmapInfo) + if (value == null) { - if (CurrentSelection is GroupedBeatmap groupedBeatmap && beatmapInfo.Equals(groupedBeatmap.Beatmap)) - return; - - // it is not universally guaranteed that the carousel items will be materialised at the time this is set. - // therefore, in cases where it is known that they will not be, default to a null group. - // even if grouping is active, this will be rectified to a correct group on the next invocation of `HandleFilterCompleted()`. - value = IsLoaded && !IsFiltering - ? GetCarouselItems()?.Select(item => item.Model).OfType().FirstOrDefault(gb => gb.Beatmap.Equals(beatmapInfo)) - : new GroupedBeatmap(null, beatmapInfo); + CurrentGroupedBeatmap = null; + return; } - base.CurrentSelection = value; + if (CurrentGroupedBeatmap != null && value.Equals(CurrentGroupedBeatmap.Beatmap)) + return; + + // it is not universally guaranteed that the carousel items will be materialised at the time this is set. + // therefore, in cases where it is known that they will not be, default to a null group. + // even if grouping is active, this will be rectified to a correct group on the next invocation of `HandleFilterCompleted()`. + CurrentGroupedBeatmap = IsLoaded && !IsFiltering + ? GetCarouselItems()?.Select(item => item.Model).OfType().FirstOrDefault(gb => gb.Beatmap.Equals(value)) + : new GroupedBeatmap(null, value); } } diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs index 7597912ae6..9949f86808 100644 --- a/osu.Game/Screens/SelectV2/SongSelect.cs +++ b/osu.Game/Screens/SelectV2/SongSelect.cs @@ -479,7 +479,7 @@ namespace osu.Game.Screens.SelectV2 if (!this.IsCurrentScreen()) return; - carousel.CurrentSelection = groupedBeatmap; + carousel.CurrentGroupedBeatmap = groupedBeatmap; // Debounce consideration is to avoid beatmap churn on key repeat selection. selectionDebounce?.Cancel(); @@ -512,7 +512,7 @@ namespace osu.Game.Screens.SelectV2 if (validSelection) { - carousel.CurrentSelection = currentBeatmap.BeatmapInfo; + carousel.CurrentBeatmap = currentBeatmap.BeatmapInfo; return true; } @@ -535,7 +535,7 @@ namespace osu.Game.Screens.SelectV2 if (validBeatmaps.Any()) { - carousel.CurrentSelection = difficultyRecommender?.GetRecommendedBeatmap(validBeatmaps) ?? validBeatmaps.First(); + carousel.CurrentBeatmap = difficultyRecommender?.GetRecommendedBeatmap(validBeatmaps) ?? validBeatmaps.First(); return true; } }