Commit Graph

5 Commits

Author SHA1 Message Date
d8b5bc704b Handle drags: 1:1 finger tracking with cross-flip; fix caret/taps on empty lines
Selection handles now track the finger 1:1 (anchor grab point + displacement)
instead of snapping by whole lines, and crossing the opposite handle flips
the selection (native behaviour) instead of clearing it.

Caret and tap/handle line resolution use VisualLineStarts instead of the
min-Y baseline: the window's first visual line may be an empty line with no
recorded glyphs, which used to draw boundary carets one line too low per
leading empty line and land taps/dragged handles one line below the finger.
New exported ui.CaretPoint centralises byte->insertion-point mapping.

The off-screen caret no longer clamps to the window edge: EditorLayout ships
the true (possibly negative / past-end) window-relative cursor and the
renderer skips the caret when the cursor is outside the shaped window, so
scrolling past the caret no longer makes it jump onto the top/bottom line.

IME/router replay fixes: key.FocusCmd is issued only on a focus transition
(a per-frame no-op still takes the immediate-command path and re-queues all
pointer events), and the key.SelectionCmd IME sync is deferred while a
handle drag is in progress (each push re-injected the drag into every
gesture). Handle drags forward only Grabbed events; a tap inside a handle
grab box is a no-op.

Also: key.FocusEvent no longer logs as unexpected in main; dead code removed
(worker taskWrapper, browser applyXxxResult stubs, scrollIndex, mock_setup
sortModeKey/lineSpan helpers); mock FileSystem.ListPaths prefix match uses
strings.HasPrefix; build scripts run the new scripts/check.sh static gate
(go vet + staticcheck). Tests: caret_point_test, touch_selection updates
(flip/empty-line cases), off-window caret e2e, selection drag e2e grab step.
2026-08-19 22:34:12 -04:00
110c92e3fa Selection handles: capture base line at grab (fix jitter runaway)
The relative-line mapping resolved the anchor's base line from the
anchor's CURRENT byte on every drag event. Once the anchor crossed a
line, its own movement fed back into its target: a finger jittering
near a line boundary added one more line per event and raced the
anchor to the bottom of the file (reported: tiny vertical movements
jump the anchor off-screen).

Capture the anchor's visual line once at the grab (SelDragPressLine)
and compute the target from that fixed base. A jitter test
(TestSelDrag_EndHandle_JitterNearLineBoundary_Stable) pins the
runaway: mutation-verified against the per-event re-resolution.
2026-08-18 15:13:05 -04:00
7239b1e7ae Handle drags: restore cross-line selection via relative line mapping
The line-lock from the previous fix made anchors immovable across lines —
dragging a handle vertically no longer extended the selection, losing the
native multi-line behaviour.

New contract: a handle anchor moves RELATIVE to its own visual line. The
finger's vertical displacement from the grab position, in whole visual line
heights, selects the target line (under half a line: the anchor's own
line); the finger's x is projected onto that line. A stationary or
horizontal drag never moves the anchor off its line (the grab-box press
stays safe — the original bug is gone), while a deliberate vertical drag
walks the anchor across lines. The mapping is relative, not the finger's
absolute line, because the 48dp grab box is centred below the line: a low
press tracking the finger's absolute line would first drag the anchor the
wrong way, through a collapse, before reaching the anchor's line.

Verified on device: end handle dragged down extends the selection across
the newline; start handle dragged up extends it onto the previous line;
horizontal drags still shrink along the line without clearing. Tests
mutation-verified against both the pre-fix mapping and the
absolute-finger-line variant. Docs section 17 updated.
2026-08-18 13:39:25 -04:00
275a78efaa Fix selection-handle drags, menu anchoring, and left-edge back-gesture theft
Three user-reported selection bugs, one root cause each:

1. Start handle ungrabbable at line start. Two interacting causes:
   a) The 48dp grab box straddles two visual lines; a finger in the
      lower half mapped (by y-to-line) to the neighbouring line, whose
      byte past the other handle clamped to a zero-length selection ->
      cleared on the first drag event. The cleared selection
      un-registered the drag op, so the router silently stopped
      delivering drag events (the observed 'stream cutoff'). Fix:
      handle drags now project the finger's x onto the anchor's own
      visual line (visualLineOfByte + textPosOnLineAtX); the anchor
      never crosses lines during a handle drag.
   b) A horizontal flick from the line-start handle (screen x~26px)
      started the system back gesture, which cancelled the touch
      stream. Fix: report the handle grab rects as system gesture
      exclusion rects (setSystemGestureExclusionRects, API 29+),
      marshalled to the UI thread via a PadExcl smali Runnable
      (generated identically by build_emu.sh/build_phone.sh).

2. End-handle drag downward made the menu chase the finger and cover
   the selection. Fix: the menu anchors to the STABLE end of the
   selection (the end not being dragged), so it stays parked by the
   selection start, clear of the finger and the highlighted text.

3. Menu above the selection vanished permanently when the selection
   was extended onto the top line. Fix: off-window anchors no longer
   hide the menu while any part of the selection is visible (keep-last
   rect, clamped); hiding happens only for fully off-window selections.

Also: registerDrag simplified (single shared drag path, body before
handles in z-order), debug logging removed, regression tests
(mutation-verified) for line projection and menu anchoring, docs
section 17. Verified on device: start-handle drag shrinks the word
without clearing or triggering back navigation; end-handle vertical
drag leaves menu/highlight/handles undisturbed; menu stays visible
with the selection at the top line.
2026-08-18 13:09:29 -04:00
2a16c0017c Touch selection (v1): long-press/double-tap word selection, drag handles, floating copy/cut/paste menu
Implement the Android-native touch selection model, verified on-device:
- long-press selects the word under the finger (blank -> caret + paste-only
  menu); double-tap selects the word; drag handles resize the selection,
  drag the highlighted body to move it; floating menu offers copy/cut/paste
  (selection) or paste (bare caret), closing on any item tap.
- Renderer reports finger positions (app-local Dp) as tap/double-tap/
  long-press/selection-drag events; the logic goroutine owns all geometry
  (EditorRegion, menu rect, hit-testing, handles) and the renderer only
  draws the frame snapshot.
- Long press: 400 ms still-press on the editor, cancelled by movement
  (non-grabbing raw pointer probe) or by a scroll/handle grab. The main
  loop keeps invalidating while a press is pending (Gio renders on demand;
  a stationary finger produces no frames).
- Clipboard crosses the goroutine boundary via buffered channels
  (clipboardSetChan/pasteReqChan logic->main, pasteChan main->logic);
  main executes the Gio ops and, on Android, invalidates after ReadCmd
  because a queued transfer.DataEvent schedules no frame of its own.

Renderer fixes found while validating on-device:
- clickReg was stored by value in a map; range yielded copies so per-frame
  press bookkeeping (long-press state) was silently discarded. Now pointers.
- On Android a tap's press+release arrive in the same frame and
  gesture.Click/Drag return one event per Update call; without draining
  each gesture's queue every frame the release was lost on an idle window
  and every menu tap was swallowed (needed a second tap to 'rescue' it).
  Click and drag loops now drain to exhaustion (scroll already does).
- pointer.Filter queries must name Kinds: a zero-kinds filter matches
  nothing (the press-probe query was dead).
- Menu.Draw offsets items by the menu origin; the clippable drawElement
  branch registers SelDrag (handles now draw for the TextField).

Tests: internal/editor/touch_selection_test.go (word range, long-press,
double-tap, tap/menu guards, handle drags, menu actions, selection edits)
and internal/test/e2e/touch_selection_e2e_test.go; full suite green under
-race. Docs: spec.md §2.2 + §7, architecture.md §6.3a, development_plan.md
Phases 8-9.
2026-08-17 08:57:55 -04:00