diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs index 857691a399..4fbc8fabfb 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelectCurrentSelectionInvalidated.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Screens.Select.Filter; using osu.Game.Screens.SelectV2; +using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelectV2 { @@ -198,6 +199,42 @@ namespace osu.Game.Tests.Visual.SongSelectV2 assertPanelSelected(0); } + [Test] + public void TestDebounceNotBypassedOnUpdate() + { + BeatmapInfo? selectedBefore = null; + BeatmapInfo? selectedBeatmapDuringDebounce = null; + + // we're testing the song select side debounce, so let's make filtering immediate + AddStep("set filter debounce delay to zero", () => Carousel.DebounceDelay = 0); + + WaitForFiltering(); + + AddUntilStep("wait for global beatmap selection", () => !Beatmap.IsDefault); + + AddStep("store selection", () => selectedBefore = Beatmap.Value.BeatmapInfo); + + AddStep("traverse to next panel and update simultaneously", () => + { + InputManager.Key(Key.Right); + + Beatmaps.Delete(Beatmaps.GetAllUsableBeatmapSets().Last()); + + // check selection during debounce + Scheduler.AddDelayed(() => selectedBeatmapDuringDebounce = Beatmap.Value.BeatmapInfo, Screens.SelectV2.SongSelect.SELECTION_DEBOUNCE / 2f); + }); + + WaitForFiltering(); + + AddUntilStep("wait for pre-debounce selection", () => selectedBeatmapDuringDebounce, () => Is.Not.Null); + + AddAssert("selection during debounce didn't change", () => selectedBeatmapDuringDebounce, () => Is.EqualTo(selectedBefore)); + + // Due to nunit runs having limited precision this tends to fail when headless, even though you'd expect the previous step to fail. + // Interactively, things fail as expected. + AddUntilStep("selection has changed after debounce", () => selectedBeatmapDuringDebounce, () => Is.Not.EqualTo(Beatmap.Value.BeatmapInfo)); + } + private void waitForFiltering(int filterCount = 1) { AddUntilStep("wait for filter count", () => Carousel.FilterCount, () => Is.EqualTo(filterCount)); diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs index 0e3b6b3a61..7e99efe987 100644 --- a/osu.Game/Screens/SelectV2/SongSelect.cs +++ b/osu.Game/Screens/SelectV2/SongSelect.cs @@ -467,7 +467,6 @@ namespace osu.Game.Screens.SelectV2 /// - Immediately update the selection the carousel. /// - After , update the global beatmap. This in turn causes song select visuals (title, details, leaderboard) to update. /// This debounce is intended to avoid high overheads from churning lookups while a user is changing selection via rapid keyboard operations. - /// To complete the operation immediately, call . /// /// The beatmap to be selected. private void queueBeatmapSelection(BeatmapInfo beatmap) @@ -488,15 +487,6 @@ namespace osu.Game.Screens.SelectV2 }, SELECTION_DEBOUNCE); } - /// - /// If any pending selection exists from , run it immediately. - /// - private void finaliseBeatmapSelection() - { - if (selectionDebounce?.State == ScheduledDelegate.RunState.Waiting) - selectionDebounce?.RunTask(); - } - private bool ensureGlobalBeatmapValid() { if (!this.IsCurrentScreen()) @@ -548,6 +538,12 @@ namespace osu.Game.Screens.SelectV2 finaliseBeatmapSelection(); return validSelection; + + void finaliseBeatmapSelection() + { + if (selectionDebounce?.State == ScheduledDelegate.RunState.Waiting) + selectionDebounce?.RunTask(); + } } private bool checkBeatmapValidForSelection(BeatmapInfo beatmap, FilterCriteria? criteria) @@ -789,7 +785,12 @@ namespace osu.Game.Screens.SelectV2 // but also in this case we want support for formatting a number within a string). filterControl.StatusText = count != 1 ? $"{count:#,0} matches" : $"{count:#,0} match"; - ensureGlobalBeatmapValid(); + // If there's already a selection update in progress, let's not interrupt it. + // Interrupting could cause the debounce interval to be reduced. + // + // `ensureGlobalBeatmapValid` is run post-selection which will resolve any pending incompatibilities (see `Beatmap` bindable callback). + if (selectionDebounce?.State != ScheduledDelegate.RunState.Waiting) + ensureGlobalBeatmapValid(); updateWedgeVisibility(); }