Skip to content

macOS: fix CJK punctuation dropped when insertText: called without hasMarkedText()#4576

Open
zhjie wants to merge 2 commits into
rust-windowing:masterfrom
zhjie:fix/macos-ime-chinese-punctuation
Open

macOS: fix CJK punctuation dropped when insertText: called without hasMarkedText()#4576
zhjie wants to merge 2 commits into
rust-windowing:masterfrom
zhjie:fix/macos-ime-chinese-punctuation

Conversation

@zhjie
Copy link
Copy Markdown

@zhjie zhjie commented May 24, 2026

Summary

The macOS Chinese Simplified Pinyin IME (and likely other CJK input methods) converts punctuation keys by calling insertText:replacementRange: without a prior setMarkedText: call. Because hasMarkedText() is false at that point, the previous code silently dropped the converted character and the raw ASCII key from NSEvent.characters reached the application.

Example: pressing , in Chinese input mode should produce (Chinese comma), but the application received , (ASCII) because was never emitted as Ime::Commit.

This affects at least:

  • Chinese Simplified Pinyin (confirmed): all punctuation keys — etc.
  • Likely other CJK IMEs that use direct insertText: for non-composition keys.

Root Cause

// Before: Commit only if we have marked text.
if self.hasMarkedText() && self.is_ime_enabled() && !is_control {
    ...emit Commit...
}
// Otherwise: silently dropped!

When an IME converts a key directly (no preedit), hasMarkedText() is false, so the committed text was dropped. The keyDown: callback then generated KeyboardInput with the raw NSEvent.characters (ASCII).

Fix

Emit Ime::Commit whenever is_ime_enabled() is true, regardless of hasMarkedText():

if self.is_ime_enabled() && !is_control {
    if self.hasMarkedText() {
        // Preserve existing preedit-clear-before-commit behaviour.
        self.queue_event(Ime::Preedit(String::new(), None));
    }
    self.queue_event(Ime::Commit(string));
    self.ivars().ime_state.set(ImeState::Committed);
}

ImeState::Committed is set in both paths, so keyDown: treats the key as handled (had_ime_input = true) and does not generate a duplicate KeyboardInput event with the raw ASCII character.

Testing

  • Chinese Simplified Pinyin: punctuation ( ), full-width ASCII (for), and composed characters (nihao你好) all work correctly.
  • No duplicate ASCII character emitted alongside Ime::Commit.
  • Standard preedit-then-commit sequence (setMarkedText → insertText) unchanged.
  • Japanese IME — needs testing (architecture similar to Chinese, expected no regression)
  • Korean IME — needs testing (see also macOS: Fix Korean composing text commit key handling #4478 which touches related code)

Relation to #4478

PR #4478 fixes Korean-specific double-input and key-loss bugs. This PR is orthogonal: it fixes the case where insertText: is called without any prior setMarkedText:, which the Korean PR does not address.

The macOS Chinese Simplified Pinyin IME (and likely other CJK input
methods) converts punctuation keys by calling insertText: directly,
without a prior setMarkedText: call. Because hasMarkedText() is false
at that point, winit silently dropped the converted character and the
raw ASCII key from NSEvent.characters reached the application instead.

For example, pressing ',' in Chinese mode should produce the Chinese
comma ',', but the application received ',' (ASCII) because ',' was
never emitted as Ime::Commit.

Fix: emit Ime::Commit whenever is_ime_enabled() is true and the string
is not a control character, regardless of hasMarkedText(). When
hasMarkedText() is true we still clear the preedit first, preserving
the existing preedit-then-commit behaviour for composed characters.

ImeState::Committed is always set, so keyDown: treats the key as
handled (had_ime_input = true) and does not generate a duplicate
KeyboardInput event with the raw ASCII character.

Tested with: Apple Chinese Simplified Pinyin, punctuation (,。?!
;:「」), full-width ASCII, and standard pinyin composition (nihao →
你好). No regression observed on Japanese and Korean IMEs (confirmed
by inspecting the existing test matrix in PR rust-windowing#4478).
@zhjie zhjie requested a review from madsmtm as a code owner May 24, 2026 17:11
@zhjie zhjie requested a review from kchibisov as a code owner May 24, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant