Commit Graph

6 Commits

Author SHA1 Message Date
a83cc1a72f IME: guard distant commits; arm the resync on the first anomaly
The boundary trace (PADIME at EditorStateChanged) showed why a
desynchronized IME never heals itself: gioui's EditorReplace advances
its stored selection state when the commit arrives, so the
post-commit selection push is always deduplicated away and
updateSelection is never sent after a commit. A desynced IME is
therefore healed only by a snippet restart — and the resync was
armed only after THREE consecutive anomalies, while the phone
incident's first anomaly (a 5-rune autocorrect 19,000 runes from
the caret, width > 2) sailed through the old <=2-rune guard and
clobbered distant text before any resync could fire.

Now:
- the guard also snaps commits that are far from the caret AND
  outside the live selection (a legitimate commit is always local:
  at the caret, inside the selection, or a nearby correction);
  a distant commit lands at the caret instead of clobbering text;
- the resync is armed on the FIRST anomalous commit (snapped or
  distant), not after a streak; the streak counter is gone;
- imeSelectionRuneRange() lets the guard allow selection
  replacements anywhere.

TestRealFile_IMEForceResync rewritten for the new semantics and
extended with the distant-replacement case (must land at the
caret and arm the resync).
2026-09-13 22:31:28 -04:00
f54ca2f81a IME: map commits against the whole buffer; drop the renderer-side model
The renderer kept a mirror of the pushed IME snippet (the 'IME model')
to translate commit positions, but it transiently desynced from the
buffer on fling/tap sequences (observed as a few-byte mapping drift on
both the x86_64 emulator and the ARM phone), corrupting text. The model
string also sat on the main goroutine next to the JNI render path,
where the app observed states that were impossible for Go memory
(string contents changing between reads microseconds apart), pointing
at corruption in the native bridge layer.

Restructure along the lines of the Android InputConnection contract
and Gio's own reference editor (widget/editor.go):

- Commits carry absolute file runes (the pushed snippet's coordinate
  space) straight to the logic goroutine, which maps them to bytes
  against the WHOLE buffer (runeToByteWhole, an 8 KiB-step scan).
  Scrolling moves the window, not the buffer, so the mapping is exact
  mid-fling by construction — no mirror to desync.
- Drift guard in HandleIMECommit: a small commit (range <= 2 runes)
  is always anchored at the caret the IME was last told about; if the
  IME reports it ending elsewhere, its snippet text is stale (a
  dropped restartInput, as Gboard does during flings) and its
  position is in the stale text's coordinates — snap the commit to
  the cursor, the only position it cannot drift from.
- FlushIME simplifies to: push the snippet when the frame's
  (context+window) text differs from the last push (gioui dedupes
  against its own cache), force the selection re-push in the same
  frame. After a commit the frame text equals what the IME already
  holds locally, so the restart is naturally suppressed; a fling
  re-anchors the IME once per text change.
- Remove the renderer model (adoptFrame/ModelTranslate/
  ApplyIMEEdit/ApplyIMEKey/IMECaret), the IME freeze/settle
  machinery (IMEFrozen, markIMEScrollActive, imeSettleChan), and the
  window-relative imeRuneToByte.

Also fixed along the way (both found while chasing the corruption):

- real.ReadFileAt: loop over short reads. A single ReadAt on Android
  FUSE can return a short read, silently truncating a chunk and
  shifting every byte offset after it.
- logic: a late lazy-chunk result no longer clobbers a buffer that
  SetContent has already fully loaded.
- e2e: large-file IME test (1.6 MB file, fling + commit).
- app icon (scripts/make_icon.py + cmd/pad/appicon.png) so gogio
  builds the mipmap/adaptive icon set.

Verified: go vet + staticcheck, go test -race (all packages), and the
emulator scenario loop (open moby excerpt, fling to mid-file, tap,
type 'a', byte-compare the saved file) 75/75 clean.
2026-09-13 11:57:38 -04:00
4a3ba37075 Bump gioui.org v0.10.0 -> v0.10.2
Brings gioui.org/shader along to v1.0.9. This is the dependency set the
current build and on-device tests run against (go build, full test suite, and
the phone APK all pass with it).
2026-09-02 19:36:08 -04:00
b37e6bfecf Update to gioui v0.10.0. 2026-06-03 22:17:52 -04:00
93a7a0a0b2 fix: remove duplicate scale storage, read from State via ScaleProvider interface
- Remove Renderer.scale field and SetScale method
- Add ScaleProvider interface to avoid editor/ui import cycle
- Add Scale() method to State, rename Scale field to scale
- Renderer reads scale through r.scale.Scale()
- Fix icons.go blank import preventing embed.FS usage
- Update main.go to pass State as ScaleProvider to Renderer
2026-05-17 10:59:29 -04:00
6720359360 Add Gioui scaffolding with Label and ListView rendering
- Element interface with Region() and Visible() methods
- Label and ListView element types with constructors
- Renderer that clips and draws elements in slice order
- Main app loop with Gioui window and frame events

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-05-07 17:45:56 -04:00