mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-15 03:20:27 +00:00
Fix song select navigation with page up/down (#36293)
Resolves #36099 This PR fixes keyboard navigation in the beatmap select carousel for lazer by implementing page-wise traversal with the Page Up and Page Down keys and changing it from only scrolling to actually selecting items. **Changes:** - Added handling for `TraversalType.Page` in the keyboard traversal switch. - Implemented `traverseKeyboardPage(int direction)` method to move the selection by approximately one "page" of visible items, accounting for partially obscured items like the search bar. Also it does not wrap around (like the current PageUp/Down functionality). - Added new key bindings: - `PageUp` → SelectPreviousPage - `PageDown` → SelectNextPage The code may be very explicit for the scroll logic with the page keys, so I would appreciate some feedback when the PR is reviewed. The naming of the keybinds may need to be adjusted. `Next page` and `previous page` may be somewhat misleading. **Behavior after the change:** - Pressing Page Up/Down now moves the selection by a page of items. - After navigating, pressing Left/Right selects the navigated song instead of moving relative to the previous position. **See:** https://www.youtube.com/watch?v=JXmKAhhKiCc --------- Signed-off-by: Linus Genz <linuslinuxgenz@gmail.com> Co-authored-by: Dean Herbert <pe@ppy.sh>
This commit is contained in:
@@ -33,6 +33,9 @@ namespace osu.Game.Graphics.Carousel
|
||||
/// </summary>
|
||||
protected partial class ScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public Action? OnPageUp { get; init; }
|
||||
public Action? OnPageDown { get; init; }
|
||||
|
||||
public readonly Container Panels;
|
||||
|
||||
public void SetLayoutHeight(float height) => Panels.Height = height;
|
||||
@@ -127,6 +130,22 @@ namespace osu.Game.Graphics.Carousel
|
||||
|
||||
protected override bool IsDragging => base.IsDragging || AbsoluteScrolling;
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.PageUp:
|
||||
OnPageUp?.Invoke();
|
||||
return true;
|
||||
|
||||
case Key.PageDown:
|
||||
OnPageDown?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
|
||||
@@ -317,6 +317,8 @@ namespace osu.Game.Graphics.Carousel
|
||||
{
|
||||
Masking = false,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OnPageUp = () => Scheduler.AddOnce(traverseFromKey, new TraversalOperation(TraversalType.Page, -1)),
|
||||
OnPageDown = () => Scheduler.AddOnce(traverseFromKey, new TraversalOperation(TraversalType.Page, 1)),
|
||||
};
|
||||
|
||||
Items.BindCollectionChanged((_, args) =>
|
||||
@@ -538,26 +540,30 @@ namespace osu.Game.Graphics.Carousel
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void traverseFromKey(TraversalOperation traversal)
|
||||
private void traverseFromKey(TraversalOperation traversal)
|
||||
{
|
||||
switch (traversal.Type)
|
||||
{
|
||||
switch (traversal.Type)
|
||||
{
|
||||
case TraversalType.Keyboard:
|
||||
traverseKeyboardSelection(traversal.Direction);
|
||||
break;
|
||||
case TraversalType.Keyboard:
|
||||
traverseKeyboardSelection(traversal.Direction);
|
||||
break;
|
||||
|
||||
case TraversalType.Set:
|
||||
traverseSetSelection(traversal.Direction);
|
||||
break;
|
||||
case TraversalType.Page:
|
||||
traverseKeyboardPage(traversal.Direction);
|
||||
break;
|
||||
|
||||
case TraversalType.Group:
|
||||
traverseGroupSelection(traversal.Direction);
|
||||
break;
|
||||
case TraversalType.Set:
|
||||
traverseSetSelection(traversal.Direction);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
case TraversalType.Group:
|
||||
traverseGroupSelection(traversal.Direction);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,6 +571,7 @@ namespace osu.Game.Graphics.Carousel
|
||||
{
|
||||
Keyboard,
|
||||
Set,
|
||||
Page,
|
||||
Group
|
||||
}
|
||||
|
||||
@@ -622,6 +629,59 @@ namespace osu.Game.Graphics.Carousel
|
||||
} while (newIndex != originalIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a page-wise keyboard traversal in the carousel, moving the selection by approximately one "page" of items.
|
||||
/// </summary>
|
||||
/// <param name="direction">Positive for downwards, negative for upwards.</param>
|
||||
private void traverseKeyboardPage(int direction)
|
||||
{
|
||||
if (carouselItems == null || carouselItems.Count == 0)
|
||||
return;
|
||||
|
||||
int startIndex = currentKeyboardSelection.Index ?? (direction > 0 ? carouselItems.Count - 1 : 0);
|
||||
|
||||
// Compute the number of visible panels to treat as one page.
|
||||
// Reduced by 50% to account for the search bar covering the top items.
|
||||
int visiblePanelsCount = Math.Max(1, Scroll.Panels.Count / 2);
|
||||
int visibleCount = 0;
|
||||
int i = startIndex;
|
||||
|
||||
while (i >= 0 && i < carouselItems.Count)
|
||||
{
|
||||
i += direction;
|
||||
|
||||
if (i < 0 || i >= carouselItems.Count)
|
||||
break;
|
||||
|
||||
var item = carouselItems[i];
|
||||
|
||||
if (!item.IsVisible)
|
||||
continue;
|
||||
|
||||
visibleCount++;
|
||||
|
||||
if (visibleCount >= visiblePanelsCount)
|
||||
{
|
||||
setKeyboardSelection(item.Model);
|
||||
ScrollToSelection();
|
||||
playTraversalSound();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are at the beginning or end and there are not enough items left to scroll through a complete page, then we go to the last or first item.
|
||||
var fallback = direction > 0
|
||||
? carouselItems.LastOrDefault(x => x.IsVisible)
|
||||
: carouselItems.FirstOrDefault(x => x.IsVisible);
|
||||
|
||||
if (fallback != null && !CheckModelEquality(fallback.Model, currentKeyboardSelection.Model))
|
||||
{
|
||||
setKeyboardSelection(fallback.Model);
|
||||
ScrollToSelection();
|
||||
playTraversalSound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select the next valid group selection relative to a current selection.
|
||||
/// This is generally for keyboard based traversal.
|
||||
|
||||
Reference in New Issue
Block a user