Added: IME composing text preview with in-buffer pre-edit cursor (CJK) - #5242
Open
sng2c wants to merge 5 commits into
Open
Added: IME composing text preview with in-buffer pre-edit cursor (CJK)#5242sng2c wants to merge 5 commits into
sng2c wants to merge 5 commits into
Conversation
…n-composing keys The terminal only renders text that is committed and echoed back by the program in the pty, so in-progress IME composition (e.g. ㄱ → 가 → 간) was not visible until commit, and the preview followed the cursor when a non-composing key (arrows/ESC/Tab from extra-keys or hardware keyboard) was pressed mid-composition. - Override InputConnection.setComposingText() to capture the composing text and draw it as a preview overlay at the cursor via a new TerminalRenderer.renderComposingText(), reusing the same mTextPaint and drawTextRun path as normal rendering (same font family/fallback, width scaling and cell text style: bold/italic/colors/dim). - Finalize composing on non-modifier keys in onKeyDown(): write the composing code points to the pty directly, clear the local Editable, and reset the IME via invalidateInput (API 33+) / restartInput so it drops its composing buffer (avoids duplication on the next input).
…etting Show in-progress IME composing text (e.g. Hangul ㄱ→가→간) as an inline preview at the terminal cursor, and commit it through the InputConnection path (no direct pty write). The terminal only renders committed text, so without this the composing characters are invisible until commit. Opt-in via `enable-ime-composing` (default false; TYPE_NULL behavior unchanged = no regression). When enabled: - Use TYPE_TEXT_VARIATION_NORMAL|NO_SUGGESTIONS so the IME composes at word level (VISIBLE_PASSWORD is fatal for CJKV — it disables composing). - Store the BaseInputConnection and finalize in-progress composition via finishComposingText() when input bypasses the IME (extra-keys literals / hardware keys via onKeyDown/inputCodePoint). If the fake Editable is empty, copy the composing text into it so the commit is not lost. - Guard finishComposingText with `hadComposing` so restartInput's closeConnection does not double-commit. - restartInput after finalize so the IME discards its stale composing buffer; only after finalize (not every commit) so digit input is unaffected. - Render the IME pre-edit cursor (Selection within the composing span). Samsung keyboard is not supported (non-standard composing buffer: does not clear after commit, causing duplicates that cannot be fixed without breaking digit input). Gboard (AOSP LatinIME-based) works.
…toggle, CJKV wording - Do not finalize IME composing on modifier key events (shift/ctrl/alt/meta/ sym/fn) in onKeyDown. Pressing shift to build a CJKV double consonant (e.g. Korean ㅆ = shift+ㅅ for 했) previously aborted the in-progress composition (해): finalize committed it and the posted restartInput cleared the IME's composing buffer, so the following ㅅ entered detached. Guard with KeyEvent.isModifierKey(keyCode); non-modifier keys still finalize, so hardware-key/extra-keys behavior is unchanged. Digit input and committed text unaffected (verified on-device with a standard-compliant keyboard). - Move the enable flag from termux.properties (`enable-ime-composing`) to a SharedPreferences toggle in Settings -> Terminal I/O -> "IME Composing Preview" (`ime_composing_enabled`, default false). Returning from the Settings activity recreates the InputConnection, so the change applies without `termux-reload-settings`. Follows the existing convention that termux.properties and SharedPreferences keys are disjoint. - Describe the feature as CJKV and "standard-compliant keyboards" instead of naming specific IMEs.
# Conflicts: # terminal-view/src/main/java/com/termux/view/TerminalRenderer.java # terminal-view/src/main/java/com/termux/view/TerminalView.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Termux renders only text committed and echoed back from the pty, so in-progress
IME composition (Hangul/Pinyin/Kana) is invisible until commit. The previous
attempt (#5215) forced a commit on every non-modifier
onKeyDown— includingarrow keys — so the IME could not move the cursor within its own composing
buffer, making the pre-edit cursor unusable ([ghostty-org/ghostty#11837]). It
was withdrawn to be reworked.
Solution
An implementation of the interaction with the IME, not of any language.
Android IMEs deliver composing text over a span on the
InputConnection'sEditableand drive a pre-edit cursor throughSelection. The preview isdriven by exactly that spannable state — composing text + cursor position
within it — so it faithfully reflects the IME rather than inferring from key
events. No script-specific logic; works for any composing IME following the
standard
InputConnectioncontract.How it works
setComposingText()→ capture + render as overlay at cursor(
TerminalRenderer.renderComposingText()), reusingmTextPaint/drawTextRun.Underlined to mark pre-edit.
Selectionwithin composing span → cursor movementinside composing buffer visible (GNOME-Terminal-like).
setSelection()redraws on in-buffer move; finalizes only when cursor leavescomposing region.
commitText()/finishComposingText()→ pty via standard commit path.onKeyDown()finalizes only for IME-bypassing keys; modifier keys excluded(
KeyEvent.isModifierKey) — shift+ㅅ→ㅆ no longer aborts composition. EnablesCJK IME composing on both software and hardware keyboards (Bluetooth/USB);
previously (TYPE_NULL) hardware keyboards could not compose through the IME
at all.
Opt-in, no regression
Settings → Terminal I/O → "IME Composing Preview" (
ime_composing_enabled,default
false):Off =
TYPE_NULLunchanged. Returning from Settings recreatesInputConnection→ applies withouttermux-reload-settings.CJK demos
Vietnamese — future work
Gboard Vietnamese (Telex) uses
commitText-per-keystroke +replaceText(in-place replace, not composing). A terminal's display is the pty echo, not
an
Editable—replaceTextcan't backspace+rewrite the echoed glyph.Fixing requires reworking
InputConnectionto a real-Editablewith deltasync (like SwiftTerm-iOS
UITextInput). Out of scope; tracked as future work.Changed files
TerminalView.java—InputConnectionoverrides, pre-edit cursor, modifier guardTerminalRenderer.java—renderComposingText()overlayTerminalViewClient.java—shouldEnableImeComposing()callbacktermux-shared+app— wiring + Settings toggle (ime_composing_enabled)Reworks #5215.