Repository navigation
scroll: Stop a smooth-scrolling wheel at the edge on macOS - #3781
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review of FindingsP2: the two resets of P3: a stale
This needs a hand switch inside the gap or during a fling. The worst case is one bounce, which is what every wheel does before this PR, and P3: a stall of 250 ms or more loses the bounce of a real fling ( Note: Note: Heuristic and API
What I ranAll on Linux, on a worktree verified at
Not verified
🤖 Noop (reviewer) · AI agent acting for @huacnlee |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks. Added the test for the pause in 04dded1, and while checking further I found and fixed a gap in 579e248. The gap. The fix. The rule is now "a touch or a lift", not only a lift: a smooth-scrolling wheel sends neither. Both callers pass on the phase of the packets they keep, through The reset on
I agree with leaving the two P3s. I updated the description for your two notes ( Checked by hand on macOS with a trackpad and a Mac Mouse Fix wheel, in a |
|
Re-review of FindingP3, small regression: a swipe that leans sideways for about 250 ms or more still stops flat at an end (
Probe with
The result is cosmetic, because the text still scrolls and nothing freezes. But it is the same symptom 579e248 set out to remove, and your 1,200-scenario probe may not have covered a lead this long. Change I tried. With only the pub(crate) fn touch_phase(&mut self, phase: TouchPhase, now: Instant) {
self.follow(now);
if phase != TouchPhase::Moved {
self.touched = true;
}
}The method doc would then need to say it notes any packet not handed to Earlier findings
API and namingThere is no public API change. What I ranOn Linux at
Not verifiedReal macOS and driver hardware, the story gallery, and end-to-end 🤖 Noop (reviewer) · AI agent acting for @huacnlee |
Description
Follows up #3316. Smooth-scrolling mouse drivers (Logi Options+, Mos, Mac Mouse Fix and similar) send precise deltas with no phase, which GPUI reports in the same shape as trackpad momentum. #3316 fixed the list freezing after such a wheel bounced, but the wheel still bounces at every edge.
NSScrollViewdoes not bounce it.I logged the raw
NSEvents on macOS 27 (4,064 events):phaseandmomentumPhasenone.momentumPhasebegan) started 0.1 to 13 ms after the finger'sended, in all 15 flings.NSScrollViewscrolled to its end by the Mac Mouse Fix wheel never left its bounds. Trackpad flings stretched it.GPUI does not pass
momentumPhaseon, but the stream tells the two apart: a trackpad sends a touch (Started) and a lift (Ended) around its momentum, and a smooth-scrolling wheel sends neither. On macOS,Overscrollnow treats phaseless packets as momentum only once the stream has had a touch or a lift. A pause ofGESTURE_GAPends the stream:ScrollBounce(wheel mode) andInputhandOverscrollonly packets that lean vertical, so a swipe whose first packet leans sideways never showed it a touch. Both now pass on the phase of the packets they keep, throughOverscroll::touch_phase; without that, such a swipe that turned vertical at an end stopped flat. Neither changes what it scrolls or passes on.A driver that imitates a trackpad still bounces, as in
NSScrollView: Mac Mouse Fix's trackpad mode sendsbegan,ended, then momentum 0.1 ms later. #3316's pause and inward rules are unchanged. Other platforms are unchanged, since I could not check whether their touchpads report a lift.ScrollBouncewith.scroll_wheel(false)uses its own physics rather thanOverscroll, so a smooth-scrolling wheel still bounces there.How to Test
overscroll::tests::a_smooth_scrolling_wheel_stops_at_the_edge(macOS) sends ten phaseless-40packets 8 ms apart into 30 px of text.assertion failed: !overscroll.packet(-40., TouchPhase::Moved, ...)).Two more macOS tests, each failing without its part of the fix:
a_wheel_after_a_pause_stops_at_the_edge: a lift, then a wheelGESTURE_GAPlater. Fails without the reset instart_gesture.a_swipe_that_touches_sideways_still_bounces: the touch arrives throughtouch_phase, then vertical packets run into the edge. Fails iftouch_phasedoes not mark the stream.Four existing
Overscrolltests fed momentum with no lift before it. They now send theEndedthat precedes momentum on macOS; their assertions are unchanged.Checks run:
cargo test -p gpui-base --features test-support --libon macOS: 1550 passed.cargo test -p gpui-component --lib: 752 passed.cargo clippy -p gpui-base --features test-support --all-targets -- --deny warnings: clean.cargo fmt --check: clean.ScrollBounce, bounce on and off, four kinds of parent, three start offsets, 25 gestures): only the four where a phaseless wheel runs into the top edge differ, and there it now stops instead of stretching 4.5 px.Verified by hand on macOS with a trackpad and a Mac Mouse Fix wheel, in a
TextViewand in a markdownInput: the wheel stops at the ends; a drag and a fling at an end stretch and bounce; and a swipe that starts leaning sideways and turns vertical at an end stretches. A wheel used within 250 ms of a trackpad lift can still bounce once, as your review noted.Not run: Logitech hardware, a Magic Mouse, the story gallery, Windows and Linux.
The diagnosis, fix and tests were written with Claude Code.
Checklist
🤖 Generated with Claude Code