From 792934f2c4ee5617c8a7ca68e8840d3c14400fb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Nov 2020 13:54:36 +0900 Subject: [PATCH] Allow scroll type to be specified This brings back the ability for the carousel to scroll in a classic way. It turns out this is generally what we want for "seek" operations like "random", else it's quite hard to get the expected animation. I did experiment with applying the animation after the pooled panels are retrieved, but in a best-case scenario there is still a gap where no panels are displayed during the random seek operation. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 57 +++++++++++++++------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 2012d47fd3..4ce87927a1 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Select Scroll.Clear(false); itemsCache.Invalidate(); - scrollPositionCache.Invalidate(); + ScrollToSelected(); // apply any pending filter operation that may have been delayed (see applyActiveCriteria's scheduling behaviour when BeatmapSetsLoaded is false). FlushPendingFilterOperations(); @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Select private readonly List visibleItems = new List(); private readonly Cached itemsCache = new Cached(); - private readonly Cached scrollPositionCache = new Cached(); + private PendingScrollOperation pendingScrollOperation = PendingScrollOperation.None; public Bindable RightClickScrollingEnabled = new Bindable(); @@ -462,7 +462,7 @@ namespace osu.Game.Screens.Select itemsCache.Invalidate(); if (alwaysResetScrollPosition || !Scroll.UserScrolling) - ScrollToSelected(); + ScrollToSelected(true); } } @@ -471,7 +471,12 @@ namespace osu.Game.Screens.Select /// /// Scroll to the current . /// - public void ScrollToSelected() => scrollPositionCache.Invalidate(); + /// + /// Whether the scroll position should immediately be shifted to the target, delegating animation to visible panels. + /// This should be true for operations like filtering - where panels are changing visibility state - to avoid large jumps in animation. + /// + public void ScrollToSelected(bool immediate = false) => + pendingScrollOperation = immediate ? PendingScrollOperation.Immediate : PendingScrollOperation.Standard; #region Key / button selection logic @@ -481,12 +486,12 @@ namespace osu.Game.Screens.Select { case Key.Left: if (!e.Repeat) - beginRepeatSelection(() => SelectNext(-1, true), e.Key); + beginRepeatSelection(() => SelectNext(-1), e.Key); return true; case Key.Right: if (!e.Repeat) - beginRepeatSelection(() => SelectNext(1, true), e.Key); + beginRepeatSelection(() => SelectNext(), e.Key); return true; } @@ -574,9 +579,8 @@ namespace osu.Game.Screens.Select updateYPositions(); // if there is a pending scroll action we apply it without animation and transfer the difference in position to the panels. - // due to this, scroll application needs to be run immediately after y position updates. - // if this isn't the case, the on-screen pooling / display logic following will fail briefly. - if (!scrollPositionCache.IsValid) + // this is intentionally applied before updating the visible range below, to avoid animating new items (sourced from pool) from locations off-screen, as it looks bad. + if (pendingScrollOperation != PendingScrollOperation.None) updateScrollPosition(); // This data is consumed to find the currently displayable range. @@ -795,17 +799,27 @@ namespace osu.Game.Screens.Select firstScroll = false; } - // in order to simplify animation logic, rather than using the animated version of ScrollTo, - // we take the difference in scroll height and apply to all visible panels. - // this avoids edge cases like when the visible panels is reduced suddenly, causing ScrollContainer - // to enter clamp-special-case mode where it animates completely differently to normal. - float scrollChange = scrollTarget.Value - Scroll.Current; + switch (pendingScrollOperation) + { + case PendingScrollOperation.Standard: + Scroll.ScrollTo(scrollTarget.Value); + break; - Scroll.ScrollTo(scrollTarget.Value, false); - scrollPositionCache.Validate(); + case PendingScrollOperation.Immediate: + // in order to simplify animation logic, rather than using the animated version of ScrollTo, + // we take the difference in scroll height and apply to all visible panels. + // this avoids edge cases like when the visible panels is reduced suddenly, causing ScrollContainer + // to enter clamp-special-case mode where it animates completely differently to normal. + float scrollChange = scrollTarget.Value - Scroll.Current; - foreach (var i in Scroll.Children) - i.Y += scrollChange; + Scroll.ScrollTo(scrollTarget.Value, false); + + foreach (var i in Scroll.Children) + i.Y += scrollChange; + break; + } + + pendingScrollOperation = PendingScrollOperation.None; } } @@ -849,6 +863,13 @@ namespace osu.Game.Screens.Select item.SetMultiplicativeAlpha(Math.Clamp(1.75f - 1.5f * dist, 0, 1)); } + private enum PendingScrollOperation + { + None, + Standard, + Immediate, + } + /// /// A carousel item strictly used for binary search purposes. ///