1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-13 20:33:11 +08:00

Refactor channel scrolling container to handle manual scrolls resiliently

This commit is contained in:
Salman Ahmed 2022-03-04 23:23:58 +03:00
parent 4de66bb1c6
commit 5b3ffb12b7
3 changed files with 49 additions and 18 deletions

View File

@ -207,7 +207,28 @@ namespace osu.Game.Tests.Visual.Online
} }
[Test] [Test]
public void TestUserScrollOverride() public void TestOverrideChatScrolling()
{
fillChat();
sendMessage();
checkScrolledToBottom();
AddStep("Scroll to start", () => chatDisplay.ScrollContainer.ScrollToStart());
checkNotScrolledToBottom();
sendMessage();
checkNotScrolledToBottom();
AddStep("Scroll to bottom", () => chatDisplay.ScrollContainer.ScrollToEnd());
checkScrolledToBottom();
sendMessage();
checkScrolledToBottom();
}
[Test]
public void TestOverrideChatScrollingByUser()
{ {
fillChat(); fillChat();
@ -314,9 +335,9 @@ namespace osu.Game.Tests.Visual.Online
{ {
} }
protected DrawableChannel DrawableChannel => InternalChildren.OfType<DrawableChannel>().First(); public DrawableChannel DrawableChannel => InternalChildren.OfType<DrawableChannel>().First();
protected UserTrackingScrollContainer ScrollContainer => (UserTrackingScrollContainer)((Container)DrawableChannel.Child).Child; public UserTrackingScrollContainer ScrollContainer => (UserTrackingScrollContainer)((Container)DrawableChannel.Child).Child;
public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child; public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child;

View File

@ -25,8 +25,6 @@ namespace osu.Game.Graphics.Containers
/// </summary> /// </summary>
public bool UserScrolling { get; private set; } public bool UserScrolling { get; private set; }
public void CancelUserScroll() => UserScrolling = false;
public UserTrackingScrollContainer() public UserTrackingScrollContainer()
{ {
} }
@ -38,26 +36,37 @@ namespace osu.Game.Graphics.Containers
protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default)
{ {
UserScrolling = true;
base.OnUserScroll(value, animated, distanceDecay); base.OnUserScroll(value, animated, distanceDecay);
OnScrollChange(true);
} }
public new void ScrollIntoView(Drawable target, bool animated = true) public new void ScrollIntoView(Drawable target, bool animated = true)
{ {
UserScrolling = false;
base.ScrollIntoView(target, animated); base.ScrollIntoView(target, animated);
OnScrollChange(false);
} }
public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null)
{ {
UserScrolling = false;
base.ScrollTo(value, animated, distanceDecay); base.ScrollTo(value, animated, distanceDecay);
OnScrollChange(false);
}
public new void ScrollToStart(bool animated = true, bool allowDuringDrag = false)
{
base.ScrollToStart(animated, allowDuringDrag);
OnScrollChange(false);
} }
public new void ScrollToEnd(bool animated = true, bool allowDuringDrag = false) public new void ScrollToEnd(bool animated = true, bool allowDuringDrag = false)
{ {
UserScrolling = false;
base.ScrollToEnd(animated, allowDuringDrag); base.ScrollToEnd(animated, allowDuringDrag);
OnScrollChange(false);
} }
/// <summary>
/// Invoked when any scroll has been performed either automatically or by user.
/// </summary>
protected virtual void OnScrollChange(bool byUser) => UserScrolling = byUser;
} }
} }

View File

@ -249,31 +249,32 @@ namespace osu.Game.Overlays.Chat
/// </summary> /// </summary>
private const float auto_scroll_leniency = 10f; private const float auto_scroll_leniency = 10f;
private bool trackNewContent = true;
private float? lastExtent; private float? lastExtent;
protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) protected override void OnScrollChange(bool byUser)
{ {
base.OnUserScroll(value, animated, distanceDecay); base.OnScrollChange(byUser);
lastExtent = null;
if (byUser)
lastExtent = null;
trackNewContent = IsScrolledToEnd(auto_scroll_leniency);
} }
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
// If the user has scrolled to the bottom of the container, we should resume tracking new content.
if (UserScrolling && IsScrolledToEnd(auto_scroll_leniency))
CancelUserScroll();
// If the user hasn't overridden our behaviour and there has been new content added to the container, we should update our scroll position to track it. // If the user hasn't overridden our behaviour and there has been new content added to the container, we should update our scroll position to track it.
bool requiresScrollUpdate = !UserScrolling && (lastExtent == null || Precision.AlmostBigger(ScrollableExtent, lastExtent.Value)); bool requiresScrollUpdate = trackNewContent && (lastExtent == null || Precision.AlmostBigger(ScrollableExtent, lastExtent.Value));
if (requiresScrollUpdate) if (requiresScrollUpdate)
{ {
// Schedule required to allow FillFlow to be the correct size. // Schedule required to allow FillFlow to be the correct size.
Schedule(() => Schedule(() =>
{ {
if (!UserScrolling) if (trackNewContent)
{ {
if (Current < ScrollableExtent) if (Current < ScrollableExtent)
ScrollToEnd(); ScrollToEnd();