IME: fix post-commit caret for replacement commits; trace IME pushes
ApplyIMECommitToModel computed the caret pushed to the IME right after a commit as Range.End + len(text). That is correct for a plain insertion (Start == End) but wrong for a replacement — the case a swipe/autocorrect over a selection produces: the inserted text starts at Range.Start, so the caret is Range.Start + len(text). A 4-rune replacement (the 'samet' autocorrect on the phone) pushed the caret 4 runes past the inserted text, desynchronizing the IME's caret from ours at exactly the moment its local model was being updated; the persistent 4-rune drift that followed (every commit 4 runes ahead, then the empty-fix-up loop) matches this. The logic side (applyIMECommitBytes) already computed the correct caret; this aligns the renderer's immediate push with it. Also log every IME push (snippet restarts, selection updates, commit pushes) from the renderer: the phone incident's logcat rotated before it could be re-analyzed, so the next occurrence must be reconstructable from what we actually pushed to the IME.
This commit is contained in:
parent
cb8ebc003f
commit
51344b9a5b
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// maxInt32 is a large value used as MaxWidth for single-line text layout.
|
// maxInt32 is a large value used as MaxWidth for single-line text layout.
|
||||||
|
|
@ -330,7 +331,15 @@ func (r *Renderer) ApplyIMECommitToModel(gtx layout.Context, ev key.EditEvent) {
|
||||||
r.imeHoldActive = true
|
r.imeHoldActive = true
|
||||||
r.imeHoldEditSeq = uint64(r.lastEditSeq)
|
r.imeHoldEditSeq = uint64(r.lastEditSeq)
|
||||||
r.imeHoldFrames = 0
|
r.imeHoldFrames = 0
|
||||||
caret := ev.Range.End + utf8.RuneCountInString(ev.Text)
|
// The post-commit caret is at the END of the inserted text, which
|
||||||
|
// starts at Range.Start: for an insertion (Start == End) that is
|
||||||
|
// Range.End + len, but for a replacement (a swipe/autocorrect over a
|
||||||
|
// selection) it is Range.Start + len — Range.End + len would land
|
||||||
|
// (End-Start) runes past the inserted text and desynchronize the IME's
|
||||||
|
// caret from ours (observed on the phone: a 4-rune replacement left
|
||||||
|
// every subsequent commit 4 runes ahead of the caret).
|
||||||
|
caret := ev.Range.Start + utf8.RuneCountInString(ev.Text)
|
||||||
|
log.Printf("IME PUSH commit-range=[%d,%d) text=%q -> sel caret=%d", ev.Range.Start, ev.Range.End, ev.Text, caret)
|
||||||
r.lastSelStart, r.lastSelCaret = -1, caret
|
r.lastSelStart, r.lastSelCaret = -1, caret
|
||||||
r.lastIMECaretPos = r.imeCaretPos
|
r.lastIMECaretPos = r.imeCaretPos
|
||||||
gtx.Execute(key.SelectionCmd{
|
gtx.Execute(key.SelectionCmd{
|
||||||
|
|
@ -405,6 +414,7 @@ func (r *Renderer) FlushIME(gtx layout.Context, tf TextField) {
|
||||||
// this runs — see ApplyIMECommitToModel — so a normal keystroke
|
// this runs — see ApplyIMECommitToModel — so a normal keystroke
|
||||||
// never reaches this branch; it is the re-anchoring path: scroll,
|
// never reaches this branch; it is the re-anchoring path: scroll,
|
||||||
// tap, file switch, snapped commit, external edit.)
|
// tap, file switch, snapped commit, external edit.)
|
||||||
|
log.Printf("IME PUSH snippet=[%d,%d) len=%d (restart)", snippet.Range.Start, snippet.Range.End, utf8.RuneCountInString(snippet.Text))
|
||||||
r.lastSelStart, r.lastSelCaret = -1, -1
|
r.lastSelStart, r.lastSelCaret = -1, -1
|
||||||
gtx.Execute(key.SnippetCmd{Tag: tf.id, Snippet: snippet})
|
gtx.Execute(key.SnippetCmd{Tag: tf.id, Snippet: snippet})
|
||||||
}
|
}
|
||||||
|
|
@ -428,6 +438,7 @@ func (r *Renderer) FlushIME(gtx layout.Context, tf TextField) {
|
||||||
r.lastSelStart = selStart
|
r.lastSelStart = selStart
|
||||||
r.lastSelCaret = selEnd
|
r.lastSelCaret = selEnd
|
||||||
r.lastIMECaretPos = r.imeCaretPos
|
r.lastIMECaretPos = r.imeCaretPos
|
||||||
|
log.Printf("IME PUSH sel=[%d,%d)", selStart, selEnd)
|
||||||
rng := key.Range{Start: selStart, End: selEnd}
|
rng := key.Range{Start: selStart, End: selEnd}
|
||||||
if selStart < 0 {
|
if selStart < 0 {
|
||||||
rng = key.Range{Start: selEnd, End: selEnd}
|
rng = key.Range{Start: selEnd, End: selEnd}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user