Docs: full coordinate pipeline reference (screen px -> file byte)
doc/README.md §Screen coordinates: add the complete 4-hop pipeline (screen px -> app dp -> text-local dp -> window-relative glyph byte -> absolute file byte), the tapLocalY sub-line-remainder rule, the glyphBase window-base rule, and the selection/menu/IME coordinate spaces (absolute file bytes vs window-relative TextField selection vs app-dp drag events vs menu-local MenuItem offsets). Cross-reference the architecture.md invariants. doc/architecture.md §8: state that CheckGestures input-event positions are app-local Dp (same space as element Regions / MenuRect) and that logic converts them to text coordinates.
This commit is contained in:
parent
2a16c0017c
commit
7f2d3144eb
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user