iris: DragArbiter closes I5's touch-drag pan-vs-select gap
A row's own click_or_drag() selection handler always won the same gesture a list-level pan wanted, since run_sensors gives the inner layer first refusal every frame it's pressed. DragArbiter (iris/src/sense.rs) decides pan vs. select the way Android does: vertical drag pans immediately, a held stationary press starts a selection after LONG_PRESS, and a horizontal drag on already-selected text extends immediately. transcript-ui's Selection::drag routes every row's drag through one arbiter per list, driving List::scroll for a pan instead of a second scroll mechanism. 8 new unit tests (iris::sense::drag_arbiter_tests); cargo fmt/clippy/test --workspace and cargo ndk (iris, transcript-ui) all clean; run-headless.sh screenshot byte-identical to before the change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
a853eb5a4d
commit
e5880c33f4
7 files changed
+486
-79
No files matched your search
@@ -37,13 +37,15 @@ session spending an afternoon on them again.
|
||||
## Where things stand (2026-09-05)
|
||||
|
||||
- **In flight, 2026-09-05 (session cleared mid-work, picked up again):**
|
||||
(a) the I5 touch-drag pan-vs-select gap, as a `DragArbiter` in
|
||||
`iris/src/sense.rs` wired into `transcript-ui`'s selection; (b) E4 as a
|
||||
new `iris/desktop-app` crate on winit, embedding `transcript-ui` via
|
||||
`build_tree` beside a session list, with `client-core::config` holding the
|
||||
enrolment. Design choices for both are summarised in `DECISIONS.md` at
|
||||
the repo root, which is the file Iris reads for choices made without her.
|
||||
Next after those: I5's Android integration and the bench numbers.
|
||||
E4 as a new `iris/desktop-app` crate on winit, embedding `transcript-ui`
|
||||
via `build_tree` beside a session list, with `client-core::config`
|
||||
holding the enrolment. The other half of this note, (a) the I5
|
||||
touch-drag pan-vs-select gap as a `DragArbiter` in `iris/src/sense.rs`
|
||||
wired into `transcript-ui`'s selection, is now **done** — see I5's own
|
||||
box below, "Gap closed, 2026-09-05". Design choices are summarised in
|
||||
`DECISIONS.md` at the repo root, which is the file Iris reads for
|
||||
choices made without her. Next after E4: I5's Android integration and
|
||||
the bench numbers.
|
||||
- **Done**: E0 (toolchain), E1 (Masonry on android-view, which found the
|
||||
keyboard gap — now explained, see below), E2 (a transcript in Masonry,
|
||||
which found that Masonry has no touch-scroll on Android at all — see
|
||||
@@ -85,11 +87,14 @@ session spending an afternoon on them again.
|
||||
Compose baseline, `ui-trace` tap-by-name on a row) — `emu list` showed
|
||||
the one emulator here held by another session, but the real blocker is
|
||||
that the integration work itself is unbuilt, not the emulator being
|
||||
busy. Touch-drag panning over a row's own rendered text is also not yet
|
||||
reachable, for a specific, diagnosed reason (it competes with this box's
|
||||
own row-level drag-select for the same gesture) rather than an absent
|
||||
primitive. Full accounting, every citation, and the dated
|
||||
IRIS_TODO.md items are in I5's own box below.
|
||||
busy. Full accounting, every citation, and the dated IRIS_TODO.md items
|
||||
are in I5's own box below. **Update, 2026-09-05, same day**: touch-drag
|
||||
panning over a row's own rendered text, which was not yet reachable for
|
||||
a specific, diagnosed reason (it competed with this box's own
|
||||
row-level drag-select for the same gesture, not an absent primitive),
|
||||
is now closed — a `DragArbiter` in `iris/src/sense.rs`, wired into
|
||||
`transcript-ui`'s selection — see the box's "Gap closed" note. Android
|
||||
integration is the one item left before this box can tick `[x]`.
|
||||
- **E3 done, 2026-09-05, and unlike E1/E2 it is committed to this repo**
|
||||
(`android-shell/` — a JNI-bridge crate on `client-core` — plus a new
|
||||
Gradle module `app/shellApp/`, left deliberately separate from
|
||||
@@ -2267,26 +2272,68 @@ silently on real hardware.
|
||||
include it) -- a small, real gap, recorded as an IRIS_TODO.md
|
||||
item rather than silently left, since AGENTS.md's bench scripts
|
||||
depend on exactly this for driving a screen by name.
|
||||
7. **Measurable frames / the render-number pass condition -- not
|
||||
attempted, and unlike E2 the reason is not an absent gesture
|
||||
path.** `List` demonstrably scrolls (I3's flat draws/moves,
|
||||
programmatic `scroll()`) and mouse-wheel scrolling is wired here
|
||||
(`lib.rs`'s `CursorSense::Scroll` on `list`). What is *not*
|
||||
reachable yet is a **touch-drag pan starting on a row's own
|
||||
text**: `row.rs` registers `CursorSense::click_or_drag()` on each
|
||||
row's `TextEdit` for selection, and `TextEdit::draw` calls
|
||||
`painter.child_layer()` (`iris/src/widget/text/edit.rs:87`), so
|
||||
`core/src/sense.rs`'s `run_sensors` (which stops at the first
|
||||
layer, checked innermost-first, that consumed the gesture) gives
|
||||
that row first refusal on *every* frame it is pressed, not just
|
||||
the frame the press started -- a row's drag-select wins the same
|
||||
gesture a list-level pan would want. `lib.rs`'s own module doc
|
||||
states this precisely, with the fix named (a press distance/time
|
||||
arbiter deciding pan vs. select before either commits, or gating
|
||||
text-drag-selection behind a long-press). This is a genuine,
|
||||
diagnosed architecture gap this box's *own* two features created
|
||||
by both wanting the same gesture -- not a missing primitive the
|
||||
way Masonry's absent `on_pointer_event` drag handling was.
|
||||
7. **Measurable frames / the render-number pass condition -- the
|
||||
gesture-conflict half is now fixed (2026-09-05); the emulator
|
||||
half is still not attempted, and unlike E2 that's not an absent
|
||||
gesture path.** `List` demonstrably scrolls (I3's flat
|
||||
draws/moves, programmatic `scroll()`) and mouse-wheel scrolling
|
||||
is wired here (`lib.rs`'s `CursorSense::Scroll` on `list`). What
|
||||
was *not* reachable at first was a **touch-drag pan starting on a
|
||||
row's own text**: `row.rs` registered `CursorSense::
|
||||
click_or_drag()` on each row's `TextEdit` for selection, and
|
||||
`TextEdit::draw` calls `painter.child_layer()`
|
||||
(`iris/src/widget/text/edit.rs:87`), so `core/src/sense.rs`'s
|
||||
`run_sensors` (which stops at the first layer, checked
|
||||
innermost-first, that consumed the gesture) gave that row first
|
||||
refusal on *every* frame it was pressed, not just the frame the
|
||||
press started -- a row's drag-select won the same gesture a
|
||||
list-level pan would want. This is a genuine, diagnosed
|
||||
architecture gap this box's *own* two features created by both
|
||||
wanting the same gesture -- not a missing primitive the way
|
||||
Masonry's absent `on_pointer_event` drag handling was.
|
||||
|
||||
**Gap closed, 2026-09-05, same day.** `iris::sense::DragArbiter`
|
||||
(`iris/src/sense.rs`, new public type, recorded in `IRIS.md`) is
|
||||
one small state machine, one instance per gesture surface (a
|
||||
whole list, not per row), driven with a caller-supplied `Instant`
|
||||
so it needs no render harness to test. It decides the way
|
||||
Android itself does, recorded in `DECISIONS.md`: an ordinary
|
||||
vertical drag pans immediately; a stationary press held
|
||||
`LONG_PRESS` (500ms) starts a selection, which any further drag
|
||||
then extends; a horizontal drag while something is already
|
||||
selected extends it immediately, skipping the wait.
|
||||
`transcript-ui/src/selection.rs`'s new `Selection::drag` is the
|
||||
one place every row's `CursorSense::click_or_drag() |
|
||||
CursorSense::unclick()` handler now goes through (`row.rs`,
|
||||
`build_text_row`), replacing the direct `begin`/`extend` calls
|
||||
each row used to make on its own -- one arbiter shared across
|
||||
every row is what keeps the decision consistent as a drag
|
||||
crosses row boundaries, per `DragArbiter`'s own doc. `Pan(dy)`
|
||||
calls the list's own `List::scroll` (the same method I3's
|
||||
mouse-wheel handler and its own benchmark already use), so this
|
||||
is not a second scroll mechanism. 8 new unit tests in
|
||||
`iris/src/sense.rs`'s `drag_arbiter_tests` (vertical drag pans
|
||||
immediately and keeps panning by per-frame delta; small jitter
|
||||
under `DRAG_SLOP` stays undecided; a held press starts a
|
||||
selection after `LONG_PRESS` and further drag extends it, even
|
||||
vertical drag, once selecting; a horizontal drag with nothing yet
|
||||
selected stays undecided rather than guessing; a horizontal drag
|
||||
with something already selected extends immediately; a vertical
|
||||
drag still pans even with a prior selection; release resets to
|
||||
idle). Verification: `cargo fmt --all -- --check`, `cargo clippy
|
||||
--workspace --all-targets` (zero warnings), `cargo test
|
||||
--workspace` (28 pre-existing + 9 `transcript-ui` + **8 new**
|
||||
`drag_arbiter_tests`, all passing), `cargo ndk -t x86_64 -P 26
|
||||
build/clippy` for both `-p iris` and `-p transcript-ui --lib`
|
||||
(clean), and `run-headless.sh transcript --shot ... -- -p
|
||||
transcript-ui` -- byte-identical to this box's original
|
||||
screenshot (38578 bytes, `cmp` confirms identical), confirming no
|
||||
visual regression from the rewiring. **What this did not
|
||||
attempt**: the emulator-side confirmation (a real touch swipe
|
||||
over a row's text panning on-device) -- that still needs I5's own
|
||||
Android integration, the one item named just above and in "What
|
||||
remains" below; this pass only had the winit/host-side gesture
|
||||
path to drive, since no cdylib exists yet for this screen.
|
||||
|
||||
**Verification, exact commands and results (2026-09-05, this VM):**
|
||||
|
||||
@@ -2345,7 +2392,6 @@ silently on real hardware.
|
||||
own right -- closer in size to E2/E3 than to "run one more
|
||||
script" -- not something this pass's remaining time could
|
||||
responsibly rush and still report honestly.
|
||||
- **Touch-drag panning over a row's own text** -- behaviour 7 above.
|
||||
- **Row-level accessibility names** -- behaviour 6 above.
|
||||
- **A tappable link and a code-span background chip** -- behaviour 2.
|
||||
- **`Selection`'s anchor-row shortcut** -- behaviour 1.
|
||||
|
||||
Reference in new issue
Block a user