app: [macOS] don't discard IME session for consistent snippets

An IME session must be discarded when its text content no longer matches
the underlying text component content. However, the check for matching
was too pessimistic; the IME session would be discarded if the new
snippet from the text component was not equal to the snippet reported to
the IME. This change implements a refined check that only discards a
session if the content of the overlap between the new and old snippets
don't match.

Fixes an IME issue reported by Zhang Zj.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2025-02-23 19:00:50 +01:00
parent 14a9fbcc0d
commit 72a72a2bc2
2 changed files with 27 additions and 1 deletions
+26
View File
@@ -5,6 +5,7 @@ package app
import (
"unicode"
"unicode/utf16"
"unicode/utf8"
"gioui.org/io/input"
"gioui.org/io/key"
@@ -117,3 +118,28 @@ func (e *editorState) RunesIndex(chars int) int {
// Assume runes after snippets are one UTF-16 character each.
return runes + chars
}
// areSnippetsConsistent reports whether the content of the old snippet is
// consistent with the content of the new.
func areSnippetsConsistent(old, new key.Snippet) bool {
// Compute the overlapping range.
r := old.Range
r.Start = max(r.Start, new.Start)
r.End = max(r.End, r.Start)
r.End = min(r.End, new.End)
return snippetSubstring(old, r) == snippetSubstring(new, r)
}
func snippetSubstring(s key.Snippet, r key.Range) string {
for r.Start > s.Start && r.Start < s.End {
_, n := utf8.DecodeRuneInString(s.Text)
s.Text = s.Text[n:]
s.Start++
}
for r.End < s.End && r.End > s.Start {
_, n := utf8.DecodeLastRuneInString(s.Text)
s.Text = s.Text[:len(s.Text)-n]
s.End--
}
return s.Text
}
+1 -1
View File
@@ -534,7 +534,7 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
}
func (w *window) EditorStateChanged(old, new editorState) {
if old.Selection.Range != new.Selection.Range || old.Snippet != new.Snippet {
if old.Selection.Range != new.Selection.Range || !areSnippetsConsistent(old.Snippet, new.Snippet) {
C.discardMarkedText(w.view)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
}