diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-16 12:18:01 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 2109098400fa37d7b448f232c848a0411c318591 (patch) | |
| tree | 90955d94035e689eb3c112d3b635be7b00036dff /test/macos-snapshots/momentum.snap | |
| parent | 556c5b50bbfd4a6aaae192377d7810f610fc7d9e (diff) | |
| download | pardes-2109098400fa37d7b448f232c848a0411c318591.tar.gz pardes-2109098400fa37d7b448f232c848a0411c318591.zip | |
Give macOS scrolling momentum
A swipe that was still moving when the fingers lifted stopped dead. The dial
next to it has thrown properly since pardes_rotate_end, and this is that same
curve on the scroll accumulator: velocity sampled between events off the
monotonic clock, weighted toward the newest sample because a flick is decided
by how the hand was moving when it left, and a fling that ramps up from zero
at the floor rather than switching on at it.
A coast stops at the end of a document rather than spinning its remaining
velocity against the edge, which is why spendScroll now reports whether the
pane moved and why paneAt is public. Only a step that delivered a press can
report an edge — the many steps between two rows cross nothing.
Nothing suppresses AppKit's own momentum, and nothing needs to: its momentum
events are ordinary pardes_scroll calls, every one of which cancels the coast
before spending its travel, so on a real trackpad the system takes the gesture
over about a frame after the lift and the tail it ends on is below the floor.
This is the path for devices AppKit does not fling for — and the only one a
script can reach, since NSEvent phases have no public constructor.
momentum.snap asserts both halves, which is why it needs the long scrollback:
a flick with no document left proves nothing. A slow swipe is byte-identical
after the release; a hard one coasts twenty-four rows further on its own, and
a finger back on the pad stops it there.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Diffstat (limited to 'test/macos-snapshots/momentum.snap')
| -rw-r--r-- | test/macos-snapshots/momentum.snap | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/test/macos-snapshots/momentum.snap b/test/macos-snapshots/momentum.snap new file mode 100644 index 00000000..1c578e68 --- /dev/null +++ b/test/macos-snapshots/momentum.snap @@ -0,0 +1,73 @@ +# Scroll momentum: a swipe still moving when the fingers lift keeps scrolling, +# in proportion to how hard it was thrown. +# +# The same shape as the dial's fling in rotate.snap, and asserted the same way, +# because the contract is the same one: momentum ramps up from zero at the +# floor (scroll_fling_floor in src/macos.zig, 8 rows/second) rather than +# switching on at it. The `gap_ms` argument is what makes the two cases +# different — it is a real sleep before the event, so libpardes measures a real +# speed off its monotonic clock rather than reading back-to-back script lines +# as an impossibly fast hand. +# +# Momentum on a real Apple trackpad comes from AppKit, not from here: its +# momentum events are ordinary pardes_scroll calls and every one of them +# cancels the coast. This is the path for the devices AppKit does not fling +# for, and it is the only one a script can reach at all, since NSEvent phases +# have no public constructor. +start 30 100 +wait 8000 Newcol Joincol +wait 8000 $ +stable 700 20000 +# Something long enough to coast ALONG. A flick that runs out of document has +# nothing left to prove: at the top of a short scrollback the hardest possible +# throw and no throw at all leave the same screen. Brace expansion rather than +# seq, so the hermetic world needs nothing on PATH. +text printf 'LINE %s\n' {1..200} +key enter +wait 10000 LINE 200 +stable 700 10000 +snap home + +# ---- PLACED ---- +# +# Half a row every 100 ms is 5 rows/second, under the floor, so lifting the +# fingers changes nothing: the swipe itself has moved what it moved and that is +# where it stops. This is the case that has to hold — a slow deliberate scroll +# that kept sliding afterwards would be unusable. +scroll_begin +scroll -0.5 10 10 0 100 +scroll -0.5 10 10 0 100 +scroll -0.5 10 10 0 100 +stable 700 15000 +snap placed +scroll_end +stable 700 15000 +# Byte-identical to `placed`, which is the whole assertion. +snap placed_released + +# ---- THROWN ---- +# +# The same hand moving six times as far in a twentieth of the time is hundreds +# of rows a second, so the release keeps the view travelling after the fingers +# are gone. +# +# `snap` and then `scroll_end` with NO `stable` between them, which is not +# impatience: a release is only a throw if it arrives while the hand is still +# moving, and a 700 ms wait here would be a hand that stopped — which is +# exactly what PLACED above already proves. +scroll_begin +scroll -3 10 10 0 5 +scroll -3 10 10 0 5 +scroll -3 10 10 0 5 +snap thrown +scroll_end +stable 700 15000 +# Further back through the scrollback than `thrown`, and that distance is the +# coast: nothing but the fling moved the view between these two captures. +snap coasted + +# A finger back on the pad catches it. Begin re-zeros the velocity and the +# bank, so a second throw cannot inherit the first one's speed. +scroll_begin +stable 700 15000 +snap caught |
