diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs index 0a7ca5a6bb..10bc069cfc 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs @@ -102,23 +102,15 @@ namespace osu.Game.Screens.SelectV2 switch (model) { case GroupDefinition group: - if (lastSelectedGroup != null) - setVisibilityOfGroupItems(lastSelectedGroup, false); - - // Collapsing an open group. + // Special case – collapsing an open group. if (lastSelectedGroup == group) { + setVisibilityOfGroupItems(lastSelectedGroup, false); lastSelectedGroup = null; return false; } - lastSelectedGroup = group; - - setVisibilityOfGroupItems(group, true); - - // In stable, you can kinda select a group (expand without changing selection) - // For simplicity, let's not do that for now and handle similar to a beatmap set header. - CurrentSelection = grouping.GroupItems[group].First().Model; + setVisibleGroup(group); return false; case BeatmapSetInfo setInfo: @@ -127,28 +119,52 @@ namespace osu.Game.Screens.SelectV2 return false; case BeatmapInfo beatmapInfo: - if (lastSelectedBeatmap != null) - setVisibilityOfSetItems(lastSelectedBeatmap.BeatmapSet!, false); - lastSelectedBeatmap = beatmapInfo; // If we have groups, we need to account for them. - if (grouping.GroupItems.Count > 0) + if (Criteria.SplitOutDifficulties) { // Find the containing group. There should never be too many groups so iterating is efficient enough. - var group = grouping.GroupItems.Single(kvp => kvp.Value.Any(i => ReferenceEquals(i.Model, beatmapInfo))).Key; - setVisibilityOfGroupItems(group, true); + GroupDefinition group = grouping.GroupItems.Single(kvp => kvp.Value.Any(i => ReferenceEquals(i.Model, beatmapInfo))).Key; + + setVisibleGroup(group); } else - setVisibilityOfSetItems(beatmapInfo.BeatmapSet!, true); + { + setVisibleSet(beatmapInfo); + } - // Ensure the group containing this beatmap is also visible. - // TODO: need to update visibility of correct group? return true; } return true; } + protected override bool CheckValidForGroupSelection(CarouselItem item) + { + switch (item.Model) + { + case BeatmapSetInfo: + return true; + + case BeatmapInfo: + return Criteria.SplitOutDifficulties; + + case GroupDefinition: + return false; + + default: + throw new ArgumentException($"Unsupported model type {item.Model}"); + } + } + + private void setVisibleGroup(GroupDefinition group) + { + if (lastSelectedGroup != null) + setVisibilityOfGroupItems(lastSelectedGroup, false); + lastSelectedGroup = group; + setVisibilityOfGroupItems(group, true); + } + private void setVisibilityOfGroupItems(GroupDefinition group, bool visible) { if (grouping.GroupItems.TryGetValue(group, out var items)) @@ -158,6 +174,14 @@ namespace osu.Game.Screens.SelectV2 } } + private void setVisibleSet(BeatmapInfo beatmapInfo) + { + if (lastSelectedBeatmap != null) + setVisibilityOfSetItems(lastSelectedBeatmap.BeatmapSet!, false); + lastSelectedBeatmap = beatmapInfo; + setVisibilityOfSetItems(beatmapInfo.BeatmapSet!, true); + } + private void setVisibilityOfSetItems(BeatmapSetInfo set, bool visible) { if (grouping.SetItems.TryGetValue(set, out var items)) diff --git a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs index 9ecf735980..951b010564 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs @@ -56,11 +56,7 @@ namespace osu.Game.Screens.SelectV2 { starGroup = (int)Math.Floor(b.StarRating); group = new GroupDefinition($"{starGroup} - {++starGroup} *"); - diffItems.Add(new CarouselItem(group) - { - DrawHeight = GroupPanel.HEIGHT, - IsGroupSelectionTarget = true - }); + diffItems.Add(new CarouselItem(group) { DrawHeight = GroupPanel.HEIGHT }); } if (!groupItems.TryGetValue(group!, out var related)) @@ -70,7 +66,6 @@ namespace osu.Game.Screens.SelectV2 diffItems.Add(item); item.IsVisible = false; - item.IsGroupSelectionTarget = true; } return diffItems; @@ -92,7 +87,6 @@ namespace osu.Game.Screens.SelectV2 newItems.Add(new CarouselItem(b.BeatmapSet!) { DrawHeight = BeatmapSetPanel.HEIGHT, - IsGroupSelectionTarget = true }); } @@ -104,7 +98,6 @@ namespace osu.Game.Screens.SelectV2 newItems.Add(item); lastItem = item; - item.IsGroupSelectionTarget = false; item.IsVisible = false; } diff --git a/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs b/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs index 37e8b88f71..06e3ad3426 100644 --- a/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapSetPanel.cs @@ -67,7 +67,6 @@ namespace osu.Game.Screens.SelectV2 base.PrepareForUse(); Debug.Assert(Item != null); - Debug.Assert(Item.IsGroupSelectionTarget); var beatmapSetInfo = (BeatmapSetInfo)Item.Model; diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Screens/SelectV2/Carousel.cs index 7184aaa866..a76b6efee9 100644 --- a/osu.Game/Screens/SelectV2/Carousel.cs +++ b/osu.Game/Screens/SelectV2/Carousel.cs @@ -168,6 +168,13 @@ namespace osu.Game.Screens.SelectV2 protected Drawable? GetMaterialisedDrawableForItem(CarouselItem item) => scroll.Panels.SingleOrDefault(p => ((ICarouselPanel)p).Item == item); + /// + /// When a user is traversing the carousel via group selection keys, assert whether the item provided is a valid target. + /// + /// The candidate item. + /// Whether the provided item is a valid group target. If false, more panels will be checked in the user's requested direction until a valid target is found. + protected virtual bool CheckValidForGroupSelection(CarouselItem item) => true; + /// /// Called when an item is "selected". /// @@ -373,7 +380,7 @@ namespace osu.Game.Screens.SelectV2 // make sure to go back to the group header this item belongs to, so that the block below doesn't find it and stop too early. if (isGroupSelection && direction < 0) { - while (!carouselItems[selectionIndex].IsGroupSelectionTarget) + while (!CheckValidForGroupSelection(carouselItems[selectionIndex])) selectionIndex--; } @@ -394,7 +401,11 @@ namespace osu.Game.Screens.SelectV2 bool attemptSelection(CarouselItem item) { - if (!item.IsVisible || (isGroupSelection && !item.IsGroupSelectionTarget)) + // Keyboard (non-group) selection should only consider visible items. + if (!isGroupSelection && !item.IsVisible) + return false; + + if (isGroupSelection && !CheckValidForGroupSelection(item)) return false; if (isGroupSelection) @@ -427,8 +438,9 @@ namespace osu.Game.Screens.SelectV2 currentKeyboardSelection = new Selection(model); currentSelection = currentKeyboardSelection; - selectionValid.Invalidate(); } + + selectionValid.Invalidate(); } private void setKeyboardSelection(object? model) diff --git a/osu.Game/Screens/SelectV2/CarouselItem.cs b/osu.Game/Screens/SelectV2/CarouselItem.cs index 2cb96a3d7f..13d5c840cf 100644 --- a/osu.Game/Screens/SelectV2/CarouselItem.cs +++ b/osu.Game/Screens/SelectV2/CarouselItem.cs @@ -29,11 +29,6 @@ namespace osu.Game.Screens.SelectV2 /// public float DrawHeight { get; set; } = DEFAULT_HEIGHT; - /// - /// Whether this item should be a valid target for user group selection hotkeys. - /// - public bool IsGroupSelectionTarget { get; set; } - /// /// Whether this item is visible or collapsed (hidden). /// diff --git a/osu.Game/Screens/SelectV2/GroupPanel.cs b/osu.Game/Screens/SelectV2/GroupPanel.cs index e837d8a32f..882d77cb8d 100644 --- a/osu.Game/Screens/SelectV2/GroupPanel.cs +++ b/osu.Game/Screens/SelectV2/GroupPanel.cs @@ -79,7 +79,6 @@ namespace osu.Game.Screens.SelectV2 base.PrepareForUse(); Debug.Assert(Item != null); - Debug.Assert(Item.IsGroupSelectionTarget); GroupDefinition group = (GroupDefinition)Item.Model;