diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 779d72190d..a21647712d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -9,7 +9,6 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Chat; using osuTK.Input; @@ -207,7 +206,28 @@ namespace osu.Game.Tests.Visual.Online } [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(); @@ -314,9 +334,9 @@ namespace osu.Game.Tests.Visual.Online { } - protected DrawableChannel DrawableChannel => InternalChildren.OfType().First(); + public DrawableChannel DrawableChannel => InternalChildren.OfType().First(); - protected UserTrackingScrollContainer ScrollContainer => (UserTrackingScrollContainer)((Container)DrawableChannel.Child).Child; + public ChannelScrollContainer ScrollContainer => (ChannelScrollContainer)((Container)DrawableChannel.Child).Child; public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child; diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs index 0561051e35..44afaf77ea 100644 --- a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -25,8 +25,6 @@ namespace osu.Game.Graphics.Containers /// public bool UserScrolling { get; private set; } - public void CancelUserScroll() => UserScrolling = false; - public UserTrackingScrollContainer() { } diff --git a/osu.Game/Overlays/Chat/ChannelScrollContainer.cs b/osu.Game/Overlays/Chat/ChannelScrollContainer.cs new file mode 100644 index 0000000000..58b2b9a075 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelScrollContainer.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Chat +{ + /// + /// An with functionality to automatically scroll whenever the maximum scrollable distance increases. + /// + public class ChannelScrollContainer : OsuScrollContainer + { + /// + /// The chat will be automatically scrolled to end if and only if + /// the distance between the current scroll position and the end of the scroll + /// is less than this value. + /// + private const float auto_scroll_leniency = 10f; + + /// + /// Whether to keep this container scrolled to end on new content. + /// + /// + /// This is specifically controlled by whether the latest scroll operation made the container scrolled to end. + /// + private bool trackNewContent = true; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (trackNewContent && !IsScrolledToEnd()) + ScrollToEnd(); + } + + private void updateTrackState() => trackNewContent = IsScrolledToEnd(auto_scroll_leniency); + + // todo: we may eventually want this encapsulated in a "OnScrollChange" event handler method provided by ScrollContainer. + // important to note that this intentionally doesn't consider OffsetScrollPosition, but could make it do so with side changes. + + #region Scroll handling + + protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = null) + { + base.OnUserScroll(value, animated, distanceDecay); + updateTrackState(); + } + + public new void ScrollIntoView(Drawable d, bool animated = true) + { + base.ScrollIntoView(d, animated); + updateTrackState(); + } + + public new void ScrollToStart(bool animated = true, bool allowDuringDrag = false) + { + base.ScrollToStart(animated, allowDuringDrag); + updateTrackState(); + } + + public new void ScrollToEnd(bool animated = true, bool allowDuringDrag = false) + { + base.ScrollToEnd(animated, allowDuringDrag); + updateTrackState(); + } + + #endregion + } +} diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 41e70bbfae..6220beeb82 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -11,9 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; @@ -236,52 +234,5 @@ namespace osu.Game.Overlays.Chat }; } } - - /// - /// An with functionality to automatically scroll whenever the maximum scrollable distance increases. - /// - private class ChannelScrollContainer : UserTrackingScrollContainer - { - /// - /// The chat will be automatically scrolled to end if and only if - /// the distance between the current scroll position and the end of the scroll - /// is less than this value. - /// - private const float auto_scroll_leniency = 10f; - - private float? lastExtent; - - protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) - { - base.OnUserScroll(value, animated, distanceDecay); - lastExtent = null; - } - - protected override void 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. - bool requiresScrollUpdate = !UserScrolling && (lastExtent == null || Precision.AlmostBigger(ScrollableExtent, lastExtent.Value)); - - if (requiresScrollUpdate) - { - // Schedule required to allow FillFlow to be the correct size. - Schedule(() => - { - if (!UserScrolling) - { - if (Current < ScrollableExtent) - ScrollToEnd(); - lastExtent = ScrollableExtent; - } - }); - } - } - } } }