1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-13 00:42:55 +08:00

Improve selection flow using early exit and invalidation

This commit is contained in:
Dean Herbert 2025-01-28 22:53:17 +09:00
parent d5dc55149d
commit 764f799dcb
No known key found for this signature in database
3 changed files with 50 additions and 25 deletions

View File

@ -95,7 +95,7 @@ namespace osu.Game.Screens.SelectV2
private GroupDefinition? lastSelectedGroup; private GroupDefinition? lastSelectedGroup;
private BeatmapInfo? lastSelectedBeatmap; private BeatmapInfo? lastSelectedBeatmap;
protected override void HandleItemSelected(object? model) protected override bool HandleItemSelected(object? model)
{ {
base.HandleItemSelected(model); base.HandleItemSelected(model);
@ -104,6 +104,14 @@ namespace osu.Game.Screens.SelectV2
case GroupDefinition group: case GroupDefinition group:
if (lastSelectedGroup != null) if (lastSelectedGroup != null)
setVisibilityOfGroupItems(lastSelectedGroup, false); setVisibilityOfGroupItems(lastSelectedGroup, false);
// Collapsing an open group.
if (lastSelectedGroup == group)
{
lastSelectedGroup = null;
return false;
}
lastSelectedGroup = group; lastSelectedGroup = group;
setVisibilityOfGroupItems(group, true); setVisibilityOfGroupItems(group, true);
@ -111,21 +119,34 @@ namespace osu.Game.Screens.SelectV2
// In stable, you can kinda select a group (expand without changing selection) // 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. // For simplicity, let's not do that for now and handle similar to a beatmap set header.
CurrentSelection = grouping.GroupItems[group].First().Model; CurrentSelection = grouping.GroupItems[group].First().Model;
return; return false;
case BeatmapSetInfo setInfo: case BeatmapSetInfo setInfo:
// Selecting a set isn't valid let's re-select the first difficulty. // Selecting a set isn't valid let's re-select the first difficulty.
CurrentSelection = setInfo.Beatmaps.First(); CurrentSelection = setInfo.Beatmaps.First();
return; return false;
case BeatmapInfo beatmapInfo: case BeatmapInfo beatmapInfo:
if (lastSelectedBeatmap != null) if (lastSelectedBeatmap != null)
setVisibilityOfSetItems(lastSelectedBeatmap.BeatmapSet!, false); setVisibilityOfSetItems(lastSelectedBeatmap.BeatmapSet!, false);
lastSelectedBeatmap = beatmapInfo; lastSelectedBeatmap = beatmapInfo;
setVisibilityOfSetItems(beatmapInfo.BeatmapSet!, true); // If we have groups, we need to account for them.
break; if (grouping.GroupItems.Count > 0)
{
// 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);
}
else
setVisibilityOfSetItems(beatmapInfo.BeatmapSet!, true);
// Ensure the group containing this beatmap is also visible.
// TODO: need to update visibility of correct group?
return true;
} }
return true;
} }
private void setVisibilityOfGroupItems(GroupDefinition group, bool visible) private void setVisibilityOfGroupItems(GroupDefinition group, bool visible)

View File

@ -35,6 +35,9 @@ namespace osu.Game.Screens.SelectV2
public async Task<IEnumerable<CarouselItem>> Run(IEnumerable<CarouselItem> items, CancellationToken cancellationToken) => await Task.Run(() => public async Task<IEnumerable<CarouselItem>> Run(IEnumerable<CarouselItem> items, CancellationToken cancellationToken) => await Task.Run(() =>
{ {
setItems.Clear();
groupItems.Clear();
var criteria = getCriteria(); var criteria = getCriteria();
int starGroup = int.MinValue; int starGroup = int.MinValue;

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -170,9 +171,8 @@ namespace osu.Game.Screens.SelectV2
/// <summary> /// <summary>
/// Called when an item is "selected". /// Called when an item is "selected".
/// </summary> /// </summary>
protected virtual void HandleItemSelected(object? model) /// <returns>Whether the item should be selected.</returns>
{ protected virtual bool HandleItemSelected(object? model) => true;
}
/// <summary> /// <summary>
/// Called when an item is "deselected". /// Called when an item is "deselected".
@ -410,6 +410,8 @@ namespace osu.Game.Screens.SelectV2
#region Selection handling #region Selection handling
private readonly Cached selectionValid = new Cached();
private Selection currentKeyboardSelection = new Selection(); private Selection currentKeyboardSelection = new Selection();
private Selection currentSelection = new Selection(); private Selection currentSelection = new Selection();
@ -418,29 +420,21 @@ namespace osu.Game.Screens.SelectV2
if (currentSelection.Model == model) if (currentSelection.Model == model)
return; return;
var previousSelection = currentSelection; if (HandleItemSelected(model))
{
if (currentSelection.Model != null)
HandleItemDeselected(currentSelection.Model);
if (previousSelection.Model != null) currentKeyboardSelection = new Selection(model);
HandleItemDeselected(previousSelection.Model); currentSelection = currentKeyboardSelection;
selectionValid.Invalidate();
currentSelection = currentKeyboardSelection = new Selection(model); }
HandleItemSelected(currentSelection.Model);
// `HandleItemSelected` can alter `CurrentSelection`, which will recursively call `setSelection()` again.
// if that happens, the rest of this method should be a no-op.
if (currentSelection.Model != model)
return;
refreshAfterSelection();
scrollToSelection();
} }
private void setKeyboardSelection(object? model) private void setKeyboardSelection(object? model)
{ {
currentKeyboardSelection = new Selection(model); currentKeyboardSelection = new Selection(model);
selectionValid.Invalidate();
refreshAfterSelection();
scrollToSelection();
} }
/// <summary> /// <summary>
@ -525,6 +519,13 @@ namespace osu.Game.Screens.SelectV2
if (carouselItems == null) if (carouselItems == null)
return; return;
if (!selectionValid.IsValid)
{
refreshAfterSelection();
scrollToSelection();
selectionValid.Validate();
}
var range = getDisplayRange(); var range = getDisplayRange();
if (range != displayedRange) if (range != displayedRange)