Skip to content

Commit fa6a46d

Browse files
committed
fix: auto-scroll (for real this time?)
1 parent 055f657 commit fa6a46d

3 files changed

Lines changed: 36 additions & 29 deletions

File tree

PostCodeSerialMonitor.Tests/AutoScrollTrackerTests.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,39 @@ public void OnScrollChanged_ContentGrowsFasterThanScrollToEndCanCatchUp_StaysAut
1010
{
1111
var tracker = new AutoScrollTracker();
1212

13-
// Simulates a burst of fast-arriving log lines: new content arrives, so the caller asks
14-
// whether it should scroll to the end (as MainWindow does on every ItemsRepeater layout
15-
// update) and does so, which is what will trigger the ScrollChanged below.
16-
Assert.True(tracker.ShouldScrollToEnd());
17-
18-
// Before that resulting ScrollChanged is processed, more lines already grew the extent
19-
// further (104 -> 130) while the offset still reflects the smaller extent it was set
20-
// against (54). The user never touched the scrollbar, so auto-scroll should not detach.
13+
// Simulates a burst of fast-arriving log lines: ScrollToEnd() was called while the
14+
// extent was still 104 (viewport 50), setting the offset to 54. Before that resulting
15+
// ScrollChanged is processed, more lines already grew the extent further to 130. The
16+
// offset only ever moved forward, so this is not the user scrolling away.
2117
tracker.OnScrollChanged(offsetY: 54, extentHeight: 130, viewportHeight: 50);
2218

2319
Assert.True(tracker.AutoScroll);
2420
}
2521

2622
[Fact]
27-
public void OnScrollChanged_UserScrollsAwayWithoutScrollToEnd_Detaches()
23+
public void OnScrollChanged_UserDragsScrollbarUp_Detaches()
2824
{
2925
var tracker = new AutoScrollTracker();
26+
tracker.OnScrollChanged(offsetY: 76, extentHeight: 130, viewportHeight: 50); // starts at bottom
3027

31-
// No ShouldScrollToEnd() call precedes this - the user dragged the scrollbar up themselves.
32-
tracker.OnScrollChanged(offsetY: 0, extentHeight: 130, viewportHeight: 50);
28+
// The offset moves backward - only a manual scrollbar drag does that.
29+
tracker.OnScrollChanged(offsetY: 20, extentHeight: 130, viewportHeight: 50);
30+
31+
Assert.False(tracker.AutoScroll);
32+
}
33+
34+
[Fact]
35+
public void OnScrollChanged_UserDragsAwayAfterSustainedAutoScrolling_StillDetaches()
36+
{
37+
var tracker = new AutoScrollTracker();
38+
39+
// Content keeps growing, offset keeps chasing it forward - auto-scroll stays engaged.
40+
tracker.OnScrollChanged(offsetY: 50, extentHeight: 104, viewportHeight: 50);
41+
tracker.OnScrollChanged(offsetY: 76, extentHeight: 130, viewportHeight: 50);
42+
Assert.True(tracker.AutoScroll);
43+
44+
// The user then grabs the scrollbar and drags it up.
45+
tracker.OnScrollChanged(offsetY: 20, extentHeight: 130, viewportHeight: 50);
3346

3447
Assert.False(tracker.AutoScroll);
3548
}

PostCodeSerialMonitor/Utils/AutoScrollTracker.cs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,25 @@ public class AutoScrollTracker
88
// from item virtualization/resizing so autoscroll doesn't flicker on/off during normal updates.
99
private const double BottomThreshold = 4;
1010

11-
// Set right before we call ScrollToEnd() ourselves, so the ScrollChanged it triggers isn't
12-
// mistaken for the user scrolling away (which happens if content keeps growing between our
13-
// call and that event, leaving the offset behind a moving extent).
14-
private bool _isProgrammaticScroll;
11+
private double _lastOffsetY;
1512

1613
public bool AutoScroll { get; private set; } = true;
1714

18-
public bool ShouldScrollToEnd()
19-
{
20-
if (!AutoScroll)
21-
return false;
22-
23-
_isProgrammaticScroll = true;
24-
return true;
25-
}
26-
2715
public void OnScrollChanged(double offsetY, double extentHeight, double viewportHeight)
2816
{
29-
if (_isProgrammaticScroll)
17+
if (offsetY < _lastOffsetY)
18+
{
19+
// The offset moved backward. Our own auto-scroll (ScrollToEnd) never does that -
20+
// it only ever moves forward, chasing a growing extent - so this can only be the
21+
// user dragging the scrollbar up.
22+
AutoScroll = false;
23+
}
24+
else if (offsetY >= extentHeight - viewportHeight - BottomThreshold)
3025
{
31-
_isProgrammaticScroll = false;
32-
return;
26+
AutoScroll = true;
3327
}
3428

35-
AutoScroll = offsetY >= extentHeight - viewportHeight - BottomThreshold;
29+
_lastOffsetY = offsetY;
3630
}
3731

3832
public void Reset()

PostCodeSerialMonitor/Views/MainWindow.axaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ private void OnScrollChanged(object? sender, ScrollChangedEventArgs e)
5151

5252
private void OnItemsRepeaterLayoutUpdated(object? sender, EventArgs e)
5353
{
54-
if (_autoScroll.ShouldScrollToEnd() && _scrollViewer != null)
54+
if (_autoScroll.AutoScroll && _scrollViewer != null)
5555
{
5656
_scrollViewer.ScrollToEnd();
5757
}

0 commit comments

Comments
 (0)