1.1 Delete the dead code up front: ui/gesture.rs (pinch node, with its
create_gesture registration and Pimpl::Gesture variant — the new
module takes over its path) and win/gesture.rs (GestureProcessor
and the old GestureAction, with the dead gesture_proc dispatch in
Window::handle_touch); then create bin/app/src/ui/gesture/ module
with GestureConstants (slop 10px, tap 300ms, sys long-press timeout,
20ms move period, 40ms sample window), the GestureAction stream
(Down/Up/Tap/LongPress/DragStart/DragMove/DragEnd { vel }),
and the GestureSet/config types (Axes, Direction, min_travel);
repoint the ui::GestureAction re-export and the dead
UIObject::handle_gesture signature to the new types; verify
make compile-dev in bin/app
1.2 Implement the recognizer library as pure state machines (tap,
long-press, drag lifecycle with 40ms velocity sampling) taking
GestureConstants; verify unit tests pass covering: tap within/beyond
slop, tap duration bound, long-press firing during hold, long-press
cancelled by movement, throttled delivery vs full sampling, and
DragEnd velocity from the sample window
1.3 Implement gesture arbitration rules (first-resolved-wins, tap vs
drag via slop, cascade cancellation); verify unit tests cover
descendant-tap-wins-within-slop and ancestor-drag-wins-beyond-slop
2. Session and dispatch
2.1 Implement GestureSession: fed from the Stage thread at the gfx
touch entry before any sync claiming; resolves the hit-test target
chain at Down (priority order, sticky ownership until Up/cancel);
runs version-guarded long-press timers on the executor; throttles
DragMove delivery to 20ms; ignores secondary touch ids; verify unit
tests pass for sticky ownership, priority ordering, and cancellation
teardown
2.2 Add gesture_set()/gesture_hit_test() defaults to UIObject and
implement Layer/ScrollLayer gesture forwarding with
coordinate translation (mirroring handle_touch translation); verify
make compile-dev and a unit test pinning local-coordinate delivery
through a nested layer
2.3 Route EMULATE_TOUCH mouse-emulated touches through the session so
desktop emulation produces real gestures; verify an emulated tap
triggers the gesture path on a desktop build with emulate-android
enabled
3. Widget migrations
3.1 Migrate Button: Tap fires the click signal; delete
handle_touch/handle_touch_sync and the mouse_btn_held gate; mouse
path unchanged; verify emulated tap clicks on desktop with no double
activation
3.2 Migrate TokenTable: row click on Tap; delete the touch-to-mouse
simulation; verify emulated row tap fires row_click
3.3 Migrate EmojiPicker: 1:1 clamped scroll on vertical drag, emoji
activation on Tap; resolve the flick-inertia open question (adopt or
preserve dead-stop) and record the choice; verify scroll clamps at
bounds and tap selects under emulation
3.4 Migrate Menu: LongPress enters edit mode (single fire during
hold), Tap selects/deletes, reorder drag armed at Down on the
reorder handle; delete TouchInfo/DragInfo and the long-press task
juggling; verify edit mode, reorder commit, and item selection under
emulation
3.5 Migrate ChatView: 1:1 scroll on drag, inertia fed from DragEnd
velocity, grab-stops-inertia on DragStart, LongPress for line
select / URL toast, Tap forwarding to message content (URLs, file
downloads) and line-toggle; delete TouchInfo, the
touch_hold_version timer, and end_touch_phase; first coordinate
with app-chatview whether this task or chatview2 performs the
migration; verify scroll, flick, grab-stop, and URL tap under
emulation
3.6 Migrate BaseEdit (hybrid): Down arms selection-handle grab and
word-select/cursor state, min_travel: 0. drag drives handle movement
and vertical scroll, LongPress selects word + shows action menu,
Tap sets cursor + requests focus, Up finalizes; delete
TouchStateAction and the sync/async phase split; verify word-select,
handle drag, cursor tap, and focus under emulation
4. Cleanup and verification
4.1 Delete the old paths: handle_touch/handle_touch_sync from
UIObject and all implementors, and the four widget TouchInfo
state machines; verify make compile-dev with no remaining
references to the removed APIs or the old GestureAction
4.2 Verify make compile-apk succeeds
4.3 On-device feel pass: chat scroll/flick/grab-stop, URL tap, line
selection, edit word-select and selection handles (latency check —
if handle drag feels laggy, implement the documented
handle_gesture_sync fallback for Down/DragMove), menu
edit-mode/reorder, emoji scroll, button taps
4.4 Cross-check every scenario in specs/gesture/spec.md against unit
tests and the on-device pass; record any scenario lacking coverage