You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Context: found while checking CodeRabbit's nested-scroll finding on #106.
Observation
On macOS, ScrollView.scrollWheel(with:) sends each scroll event either to the scroll view itself or to a parent scroll view, so that scrolling chains to the parent at the edge. A private ScrollSession decides the target when a trackpad gesture begins (phase == .began) and keeps it for every later event until the next gesture begins.
Mouse wheel events have no phase, so they never begin a session:
Before any trackpad gesture, a wheel event gets a new session's default target, the scroll view itself, so the mouse wheel never chains to the parent.
After a trackpad gesture, every wheel event reuses that gesture's target. If the gesture went to the parent, the mouse wheel over the inner scroll view keeps going to the parent, even where the inner view can scroll, until the next trackpad gesture begins.
In a probe, an inner scroll view at the bottom end of its range, nested in a parent that could scroll in every direction, got a one-line wheel event toward the top. With no earlier gesture, the inner view scrolled from 200 to 190. After a trackpad gesture toward the bottom had gone to the parent, the same wheel event went to the parent too, and the inner view stayed at 200.
This affects nested ComposeUI scroll views that scroll along the same axis, on a Mac used with both a trackpad and a mouse.
Direction
Route a wheel event on its own instead of by the last gesture's target. Momentum events, which have an empty phase but a non-empty momentumPhase, belong to the gesture before them and should keep its target. Decide between:
Route each wheel event, with both phases empty, by the same rule as a gesture's beginning. The mouse wheel then chains at the edge like the trackpad, which is new: today it never chains unless a trackpad gesture set the target.
Always let the scroll view handle a wheel event, as it does today before any gesture. The mouse wheel doesn't chain, but no longer inherits a gesture's target.
To do
Pick an option, and route wheel events accordingly.
Tests: a wheel event after a gesture that went to the parent, a momentum event after a gesture, and the chosen behavior at the edge. A wheel scroll applies on the next run loop turn, so check the offset with toEventually. Synthetic trackpad gestures can be built from CGEvents with scrollWheelEventScrollPhase set, as the routing tests added in [scroll] keep a set contentOffset as set on macOS, instead of clamping it into the scrollable range #106 do.
Related: #102 (half-pixel tolerance for the routing comparisons), #106.
Context: found while checking CodeRabbit's nested-scroll finding on #106.
Observation
On macOS,
ScrollView.scrollWheel(with:)sends each scroll event either to the scroll view itself or to a parent scroll view, so that scrolling chains to the parent at the edge. A privateScrollSessiondecides the target when a trackpad gesture begins (phase == .began) and keeps it for every later event until the next gesture begins.Mouse wheel events have no phase, so they never begin a session:
In a probe, an inner scroll view at the bottom end of its range, nested in a parent that could scroll in every direction, got a one-line wheel event toward the top. With no earlier gesture, the inner view scrolled from 200 to 190. After a trackpad gesture toward the bottom had gone to the parent, the same wheel event went to the parent too, and the inner view stayed at 200.
This affects nested ComposeUI scroll views that scroll along the same axis, on a Mac used with both a trackpad and a mouse.
Direction
Route a wheel event on its own instead of by the last gesture's target. Momentum events, which have an empty
phasebut a non-emptymomentumPhase, belong to the gesture before them and should keep its target. Decide between:To do
toEventually. Synthetic trackpad gestures can be built fromCGEvents withscrollWheelEventScrollPhaseset, as the routing tests added in [scroll] keep a set contentOffset as set on macOS, instead of clamping it into the scrollable range #106 do.Related: #102 (half-pixel tolerance for the routing comparisons), #106.