diff --git a/doc/README.md b/doc/README.md index 44ecc03..b38985b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -76,10 +76,13 @@ directory: `/storage/emulated/0/Notes`. ## Screen coordinates -Three coordinate spaces are in play; mixing them up is the #1 cause of -tap-test errors. (Measured 2026-08-16 on `pad_avd`; re-derive with -`adb shell wm size`, `adb shell wm density`, `dumpsys window`, and a fresh -screenshot with the keyboard open if the device config ever changes.) +The app has several coordinate spaces (screen/display/app-local px, app dp, +text-local, window-relative glyph bytes, absolute file bytes); mixing them +up is the #1 cause of tap-test and cursor bugs. (Measured 2026-08-16 on +`pad_avd`; the 128 px offset and the 2.625 scale are device-config facts — +re-derive with `adb shell wm size`, `adb shell wm density`, `dumpsys +window`, and a fresh screenshot with the keyboard open if the device config +ever changes.) | space | size | used by | |--------------|-----------|------------------------------------------| @@ -94,6 +97,44 @@ Conversions: - screen → app-local: **Y − 128** (surface starts below the status bar; X unchanged) - app-local px → pt: **÷ 2.625** (density 420) +Full pipeline, screen → file byte (each hop has exactly one place it +happens; the app is internally consistent, so errors only appear at the +hops, not inside a space): + +1. **screen px → window px → app dp** — OS/Gio. Taps you inject land here; + this is the only space `adb`/screenshots touch. +2. **app dp → text-local dp** — the tap/drag handlers + (`localX = x − EditorRegion.X`, `localY = tapLocalY(...)`). + **Y adds only the sub-line scroll remainder** (`ScrollOffset mod + lineHeight`), never the full scroll: the visible glyph layout is + window-relative, so adding the full scroll maps a tap to a line far + below the window (the Phase 7 tap-to-position bug). +3. **text-local dp → window-relative glyph byte** — + `visualLine = y / lineHeight`, then glyph x-search within that line + group. `GlyphLayout.ByteOffsets` are relative to the **top of the + visible window** (`IMEWindowStartByte`), not the file start. +4. **window-relative byte → absolute file byte** — add/subtract the window + base (`IMEWindowStartByte`, the `glyphBase()` helper) at each cursor + boundary (tap, Home, End, vertical move, selection handles). Forgetting + this hop is what snapped the cursor to the window top on scrolled files. + +Selection / menu / IME spaces (all app dp unless noted): + +- `EditorState.SelectionStart/End`, `SelectionAnchor`, `CursorPosition`: + **absolute file bytes** (architecture.md §6.3). +- `ui.TextField.SelectionStart/End`: **window-relative** bytes into the + visible `Value` (−1 = none) — rendering + IME use only. +- `SelectionDragEvent.X/Y`: finger position in **app dp** (renderer reports + the finger; logic converts, exactly like a tap — §6.3a). +- `MenuRect`: app dp; `MenuItem.X/Y`: **menu-local** (relative to the menu + origin — `Menu.Draw` adds the origin back when drawing; a tap is inside + the menu iff it is inside `MenuRect`, item index = + `(x − MenuRect.X) / itemW`). +- IME `key.EditEvent.Range`: **window-relative rune indices** (§6.4). + +Invariants: architecture.md §6.2 (window base), §6.3 (selection/caret), +§6.3a (touch selection), §6.4 (IME). + **The tap rule (covers ~95% of needs):** to tap something visible, read its (x, y) off the PNG you are looking at, multiply both by **1.2**, and `input tap x y`. Screenshot px *are* screen px — no 128 offset involved. diff --git a/doc/architecture.md b/doc/architecture.md index 68e3e98..1638952 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -391,6 +391,11 @@ Only the visible byte range is shaped and drawn each frame: 4. `e.Frame(&ops)` flushes ops. - After draw, `CheckGestures` turns raw pointer/gesture state into `[]ui.InputEvent` (each carrying its own handler) for the logic channel. + Tap/double-tap/long-press/selection-drag event positions are **app-local + Dp** (window px ÷ `PxPerDp` — the same space as element `Region`s, and + `MenuRect`); the logic side converts them to text coordinates with the + stored `EditorRegion` + scroll (README.md §Screen coordinates, pipeline + hop 2). - **Units:** `ui.Dp`/`ui.Px` convert via the current `PxPerDp` scale (`ToDp`/`ToPx`). The window is 390×844 dp; the real pixel size arrives via `ConfigEvent`.