b87f5a597e9761d256f993901c18e3084a8c83ca
Iris, from the phone (docs/IRIS_TODO.md, 2026-09-07 night): "sometimes when I try to catch it while it's still moving (particularly if I drag) then it fails to stop & snap to where finger is." The fling did stop on the down -- `Selection::drag` has cancelled it since the fling landed -- but the *gesture* then went through `DRAG_SLOP` like any other press, so for the first few frames the finger was down and the content under it did not move. Compose does not do that: `scrollable`'s `startDragImmediately` is `isScrollInProgress`, and the drag starts on the DOWN with no slop. So `DragArbiter::press_start` takes a `PressState` -- what the target looked like when the press landed, `already_selected` and `scrolling` -- and a press on moving content enters `Panning` immediately. A catch that is released without ever moving is `Released(None)`: not a `Tapped`, because Compose consumes that DOWN and no click detector under it sees the gesture, so stopping a fling must not also follow the link it landed on; and not a velocity, because there is none to hand on. The moment it moves anything it is an ordinary pan release again and flings normally. `DragGesture::starts_press` is the one rule for "this frame opens a press", read by `handle` and by `Selection::drag` -- which has to prepare its list (cancel the fling, report whether there was one) on exactly the frames `handle` will call `press_start` on, including the recovery frames where no `PressStart` ever arrived. It deliberately does **not** special-case `PressStart` to true, which is the defect the layer-1 test found: one touch-down reaches every sensor under the finger, and a transcript row's block and the tool row containing it drive the same shared `DragGesture`, so `handle` sees one `PressStart` twice. Restarting on the second delivery re-read `PressState` after the first had already acted on it -- the fling was cancelled by then, `scrolling` came back false, and every catch quietly became an ordinary slop-waiting press again. Tests. Layer 1, `transcript-fixture/tests/catch_a_fling.rs`: the recorded 120Hz flick, 150ms of fling, then a down and three 2px moves -- the content tracks the finger sample for sample (`a_press_on_a_flinging_list_pins_the_content_to_the_finger`, which fails at the parent commit with "the content 0.0px"); a catch released without moving neither taps nor flings; and the half this had no reason to touch, `the_same_small_drag_on_a_settled_list_moves_nothing` -- 6px total is inside `DRAG_SLOP`, so making every press pin the content would pass the first test and take the slop away from every ordinary one. Unit, in `sense.rs`: the catch pans from the first sample, the same press on settled content stays undecided, a catch that drags still flings, and the double-delivered `PressStart` stays one press.
Languages
Rust
53%
Kotlin
44.4%
Shell
2.6%