iris: pin the nested-scroll axis rule, and record the capture fix in RUST.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b863f9f3df
commit
8310431497
2 files changed
+139
No files matched your search
@@ -7885,6 +7885,65 @@ arbiter may never have seen the `press_start` (`DragArbiter::update`'s doc
|
||||
records that recovery path for `Selection`). Say what the arbitration
|
||||
actually is rather than adding a second recovery.
|
||||
|
||||
#### Done, 2026-09-08 (b863f9f and the commit after it)
|
||||
|
||||
Both symptoms were one thing each, and neither was the arbitration: the
|
||||
axis test in `DragArbiter` was already right, so a vertical drag on a
|
||||
fence always did fall through. What was wrong was **how a gesture ends**.
|
||||
|
||||
- **The snap back while scrolling horizontally.** `scrollable_on`
|
||||
registered `click_or_drag() | unclick()`. A `Scroll` that commits to a
|
||||
pan takes pointer capture, and a captured widget's gesture ends with
|
||||
`CursorSense::Drop` in place of `PressEnd` -- which `should_run` only
|
||||
matches for a widget that registered `Drop`. It had not, so it never
|
||||
learned its own gesture had ended: the `DragArbiter` stayed `Panning`
|
||||
with `last` at the position the finger left, and the *next* drag's
|
||||
first frame was measured from there and applied in one step. Now
|
||||
`CursorSense::drag_senses()` (frames + `unclick` + `Drop` + `Cancel`)
|
||||
is what every widget driving a `DragGesture` registers, stated once
|
||||
rather than per call site. Test:
|
||||
`a_scroll_area_that_captured_the_pointer_learns_its_gesture_ended`,
|
||||
which fails with the old registration reporting exactly the symptom
|
||||
("a fresh touch-down moved the content by -40").
|
||||
- **The jump on a tap.** Taking pointer capture is a one-way door for
|
||||
everybody else: from that frame `run_sensors` delivers to the capturer
|
||||
alone, so anything else tracking the same press sees no `PressEnd` and
|
||||
no `Drop` either. The transcript's own `DragGesture` was therefore left
|
||||
`Undecided` at the origin of the gesture the fence stole, and the next
|
||||
touch anywhere on screen was measured from it -- `Undecided`'s slop
|
||||
test crossed instantly, and the list panned by the distance between two
|
||||
unrelated fingers. `CursorSense::Cancel` is the state that was missing:
|
||||
delivered exactly once to each loser of a capture race, the way Android
|
||||
sends `ACTION_CANCEL` and the web sends `pointercancel`. It is a
|
||||
separate sense from `Drop` deliberately -- `Drop` means "your gesture
|
||||
finished" and callers act on it (a fling, a tap, a link followed),
|
||||
which is precisely the wrong thing here. `GestureOutcome::Cancelled`
|
||||
is its half of the enum, and `attr.rs`'s `press_track` registers it
|
||||
too, since a `press_origin` left set has the same failure (a stray
|
||||
selection, or a keyboard summoned by a tap somewhere else). Test:
|
||||
`taking_the_pointer_cancels_everyone_else_tracking_the_press`.
|
||||
- **The direction the change had no reason to touch**, pinned so it stays
|
||||
true: `a_drag_pans_whichever_nested_scroll_area_owns_its_axis` drives a
|
||||
real X area inside a real Y one, both through `scrollable_on`'s own
|
||||
registration, and checks each drag moves one and leaves the other at 0.
|
||||
|
||||
**Where the pointer's state lives now.** Iris, on seeing this work:
|
||||
"never reach for atomics or locking stuff first, and if you genuinely
|
||||
think it's necessary, bring it up with me first", and "everything global
|
||||
should be stored in the general input handler, not in specific senses
|
||||
with locking stuff." So the captured widget and the pressed set are no
|
||||
longer a `std::sync::Mutex` on `UiRenderState` (a structure that merely
|
||||
happened to be reachable from every handler). They are `PointerInput`,
|
||||
the cursor senses' `Event::Global` -- owned by the event manager that
|
||||
runs the dispatch and reached by `&mut`, with `run_sensors` taking it out
|
||||
for the length of a pass the same way it already takes the active-sensor
|
||||
map, and a per-dispatch `PointerRequests` slot for what a handler asks
|
||||
of it. What had forced the lock in the first place was a `Data: Send`
|
||||
bound on `task_on` that nothing needs: the future it spawns takes
|
||||
`|_, rsc|` and never sees the event's data at all. **Still a `Mutex` and
|
||||
worth revisiting: `UiRenderState::last_input_at`**, for the same
|
||||
`&self` reason, untouched here.
|
||||
|
||||
### 2. "Opening the folded cards seems pretty buggy right now" -- and the
|
||||
### sanity check she asked for
|
||||
|
||||
|
||||
Reference in new issue
Block a user