From f156cb797df13c31e43856e58037cec9b490bf7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 16:40:09 +0900 Subject: [PATCH] Improve nested scroll behaviour --- osu.Game/Overlays/Mods/ModColumn.cs | 60 ++++++++++++++++++++++- osu.Game/Overlays/Mods/ModSelectScreen.cs | 1 + 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 46a562ac98..513dd2ca3f 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -151,9 +151,10 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - new OsuScrollContainer + new NestedVerticalScrollContainer { RelativeSizeAxes = Axes.Both, + ClampExtension = 100, ScrollbarOverlapsContent = false, Child = panelFlow = new FillFlowContainer { @@ -194,6 +195,63 @@ namespace osu.Game.Overlays.Mods } } + /// + /// A scroll container that handles the case of vertically scrolling content inside a larger horizontally scrolling parent container. + /// + private class NestedVerticalScrollContainer : OsuScrollContainer + { + private OsuScrollContainer? parentScrollContainer; + + protected override void LoadComplete() + { + base.LoadComplete(); + + parentScrollContainer = findClosestParent(); + } + + protected override bool OnScroll(ScrollEvent e) + { + if (parentScrollContainer == null) + return base.OnScroll(e); + + // If not on screen, handle scroll but also allow parent to scroll at the same time. + bool topRightOutsideOfView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight) == false; + bool bottomLeftOutsideOfView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft) == false; + + if (topRightOutsideOfView || bottomLeftOutsideOfView) + { + base.OnScroll(e); + return false; + } + + bool scrollingPastEnd = e.ScrollDelta.Y < 0 && IsScrolledToEnd(); + bool scrollingPastStart = e.ScrollDelta.Y > 0 && Target <= 0; + + // Same deal if at one of the two extents of the view. + if (scrollingPastStart || scrollingPastEnd) + { + base.OnScroll(e); + return false; + } + + return base.OnScroll(e); + } + + // TODO: remove when https://github.com/ppy/osu-framework/pull/5092 is available. + private T? findClosestParent() where T : class, IDrawable + { + Drawable cursor = this; + + while ((cursor = cursor.Parent) != null) + { + if (cursor is T match) + return match; + } + + return default; + } + } + private void createHeaderText() { IEnumerable headerTextWords = ModType.Humanize(LetterCasing.Title).Split(' '); diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 5197cb6c3e..1d6f784133 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -106,6 +106,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both, Masking = false, + ClampExtension = 100, ScrollbarOverlapsContent = false, Child = columnFlow = new ModColumnContainer {