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>
The equality check that was supposed to replace the read of
`CurrentSelectionItem` showed up as a hotspot in profiling.
The selection updating code looks a little stupid after this, but all in
the name of performance...
Addresses https://github.com/ppy/osu/issues/33443, maybe.
I considered adding tests but they'd likely be janky and take a long
time to write, so decided against until there's a demand for it.
This is probably where things get a little controversial.
There are some song select flows wherein song select just wants to
ensure sanity by authoritatively setting the global beatmap. The goal is
to change the beatmap immediately and instantly. Therefore it should
kind of be the carousel's job to figure out its grouping complications.
To that end, `CurrentSelection` is made virtual, and overridden in
`BeatmapCarousel` to perform a sort of reconciliation logic. If an
external component sets `CurrentSelection` to a `BeatmapInfo`, one of
the two following things happen:
- Nothing, if the current `GroupedBeatmap` is already a copy of the
beatmap that needs to be selected, or
- The carousel looks at its items, finds any first copy which matches
the beatmap that the external consumer wanted selected, and changes
selection to that instead.
Because of this it was possible for a scroll to not be able to scroll
to the panel it wanted to scroll to because its layout height was
too small for the target Y position of the panel that it wanted to
scroll to.
The scroll height was set in `updateDisplayedRange()` which is notably
after attempting to scroll to the current selection.