Skip to content

Much improved IME#7967

Merged
emilk merged 18 commits intoemilk:mainfrom
umajho:fix-undesired-keyboard-input-events-during-ime-composition
Mar 24, 2026
Merged

Much improved IME#7967
emilk merged 18 commits intoemilk:mainfrom
umajho:fix-undesired-keyboard-input-events-during-ime-composition

Conversation

@umajho
Copy link
Copy Markdown
Contributor

@umajho umajho commented Mar 11, 2026

This approach is better than #7898 (#7914) because it correctly handles all three major IME types (Chinese, Japanese, and Korean) without requiring a predefined “IME mode”.

Environments I haved tested this PR in

macOS 15.7.3 (AArch64, Host of other virtual machines)

Run command: cargo run -p egui_demo_app --release

Tested IMEs:

  • builtin Chinese IME (Shuangpin - Simplified)
  • builtin Japanese IME (Romaji)
  • builtin Korean IME (2-Set)
Windows 11 25H2 (AArch64, Virtual Machine)

Build command: cargo build --release -p egui_demo_app --target=x86_64-pc-windows-gnu --features=glow --no-default-features

(I cannot use wgpu due to this bug, which prevents debugging inside the VM. Anyways, the rendering backend should be irrelevant here.)

Tested IMEs:

  • builtin Chinese IME (Shuangpin)
  • Sogou IME (Chinese Shuangpin)
  • WeType IME (Chinese Shuangpin)
  • builtin Japanese IME (Hiragana)
  • builtin Korean IME (2 Beolsik)
Linux [Wayland + IBus] (AArch64, Virtual Machine)

Fedora KDE Plasma Desktop 43 [Wayland + IBus 1.5.33-rc2]

(Not working at the moment because of another issue that will be fixed by #7983. It is a complicated story. )

[!NOTE]

IBus is partially broken in this system. The Input Method Selector refuses to select IBus. As a workaround, I have to open System Settings -> Virtual Keyboard and select “IBus Wayland” to start an IBus instance that works in egui.

The funny thing is: the Chinese Intelligent Pinyin IME is broken in native Apps like System Settings and KWrite, but works correctly in egui!

Screencast: What

2026-03-13 3 10 11 AM

Build command: cross build --release -p egui_demo_app --target=aarch64-unknown-linux-gnu --features=wayland,wgpu --no-default-features

(The Linux toolchain on my mac is somehow broken, so I used cross instead.)

Tested IMEs:

  • Chinese Intelligent Pinyin IME (Shuangpin)
  • Japanese Anthy IME (Hiragana)
  • Korean Hangul IME
Linux [X11 + Fcitx5] (AArch64, Virtual Machine)

Debian 13 [Cinnamon 6.4.10 + X11 + Fcitx5 5.1.2]

Build command: cross build --release -p egui_demo_app --target=aarch64-unknown-linux-gnu --features=x11,wgpu --no-default-features

Tested IMEs:

  • Chinese Shuangpin IME
  • Chinese Rime IME with luna-pinyin
  • Japanese Mozc IME (Hiragana)
  • Korean Hangul IME

Unlike macOS and Linux + Wayland, key-release events for keys processed by the IME are still forwarded to egui. These appear to be harmless in practice.
Unlike on Windows, however, they cannot be filtered reliably because there are no corresponding key-press events marked as “processed by IME”.


There are too many possible combinations to test (Operating Systems × Desktop Environments × Windowing Systems × IMFs × IMEs × …), and I only have access to a limited subset. For example, Google Japanese Input refused to install on my Windows VM, and some paid Japanese IMEs are not accessible to me. Therefore, I would appreciate feedback from people other than me using all kinds of environments.

Details

There are two possible approaches to removing keyboard events that have already been processed by an IME:

  • Approach 1: Filter out events inside egui that appear to have been received during IME composition.
  • Approach 2: Filter out such events in the platform backend (terminology borrowed from imgui, e.g. the egui-winit crate or the code under web/ in the eframe crate.).

Both approaches already exist in egui:

Compared to the first approach, the second has a clear advantage: when events are passed from the platform backends into egui, they are simplified and lose information. In contrast, events in the platform backends are the original events, which allows them to be handled more flexibly. This is also why #7898 (#7914), which attempts to address the issue from within the egui crate, struggles to make all IMEs work correctly at the same time and requires manually selecting an “IME mode”: the events received by egui have already been reduced and therefore lack necessary information.

A more appropriate solution is to consistently follow the second approach, explicitly requiring platform backends not to forward events that have already been processed by the IME to egui. This is the method used in this PR. Specifically, this PR works within the egui-winit crate, where the original KeyboardInput events can be accessed. At least for key press events, these can be used directly to determine whether the event has already been processed by the IME on Windows (by checking whether logical_key equals winit::keyboard::NamedKey::Process). This makes it straightforward to ensure that all IMEs work correctly at the same time.

This PR also reverts #4794, which took the first approach. It filters out some events that merely look like they were received during IME composition but actually are not. It also messes up the order of those events along the way.
As a result, it caused several IME-related issues. One of the sections in the Demonstrations below will illustrate these problems.

Demonstrations

Changes not included in this PR for displaying Unicode characters in demonstrations

Download unifont-17.0.03.otf from https://unifoundry.com/pub/unifont/unifont-17.0.03/font-builds/, and place it at crates/egui_demo_app/src/unifont-17.0.03.otf.

In crates/egui_demo_app/src/wrap_app.rs, add these lines at the beginning of impl WrapApp's pub fn new:

        {
            const MAIN_FONT: &'static [u8] = include_bytes!("./unifont-17.0.03.otf");

            let mut fonts = egui::FontDefinitions::default();

            fonts.font_data.insert(
                "main-font".to_owned(),
                std::sync::Arc::new(egui::FontData::from_static(MAIN_FONT)),
            );

            let proportional = fonts
                .families
                .entry(egui::FontFamily::Proportional)
                .or_default();
            proportional.insert(0, "main-font".to_owned());

            cc.egui_ctx.set_fonts(fonts);
        }

(I took this from somewhere, but I forgot where it is. Sorry…)

GNU Unifont is licensed under OFL-1.1.

This PR Fixes: Focus on a single-line TextEdit is lost after completing candidate selection with Japanese IME on Windows (#7809)

Screencast: ✅ Japanese IME now behaves correctly while Korean IME behaves as before

7809

This PR Fixes: Committing Japanese IME text with Enter inserts an unintended newline in multiline TextEdit on Windows (#7876)

Screencast: ✅ Japanese IME now behaves correctly while Korean IME behaves as before

7876

This PR Fixes: Backspacing deletes characters during composition in certain Chinese IMEs (e.g., Sogou) on Windows (#7908)

Screencast: ✅ Sogou IME now behaves correctly

7908

This PR Obsoletes #4794, because egui receives only IME events during composition from now on

On Windows, “incompatible” events are filtered in egui-winit, aligning the behavior with other systems.

Screencasts

Some Chinese IMEs on Windows:
2026-03-13 12 25 37 AM

The default Japanese IMEs on Windows:
2026-03-13 12 28 33 AM

The 2-set Korean IMEs handle arrow keys differently. It will be discussed in the next section.

This PR Reverts #4794, because it introduced several bugs

Some of its bugs have already been worked around in the past, but those workarounds might also be problematic. For example, #4912 is a workaround for a bug (#4908) introduced by #4794, and that workaround is in fact the root cause of the macOS backspacing bug I have worked around with #7810. (The reversion of #4912 is out of the scope of this PR, I will do that in #7983.)

It Caused: Arrow keys are incorrectly blocked during typical Korean IME composition

When composing Korean text using 2-Set IMEs, users should still be able to move the cursor with arrow keys regardless if the composition is committed.

Correct behavior
Screencasts

macOS TextEdit:
2026-03-12 8 04 15 PM

Windows Notepad:
2026-03-12 8 05 08 PM

With #4794 reverted, egui also behaves correctly (tested on Linux + Wayland, macOS, and Windows):
2026-03-12 8 03 51 PM

Incorrect behavior caused by #4794

remove_ime_incompatible_events removed arrow-key events in such cases. As a result, the first arrow key press only commits the composition, and users need to press the arrow key again to move the cursor:

Screencast

2026-03-12 8 06 40 PM

This is essentially the same issue described here: #7877 (comment)

It Caused: Backspacing leaves the last character in Korean IME pre-edit text not removed on macOS

Screencasts

Before this PR:
2026-03-17 10 48 12 PM

After this PR:
2026-03-17 10 47 23 PM

Korean IMEs also use Enter to confirm Hanja selections, and will not work properly in the Korean “IME mode” proposed by #7898 (#7914)

Screencast: Korean IME using Enter and Space for confirmation (IBus Korean Hangul IME)

The screencast below demonstrates that some Korean IMEs handle Hanja selection in a way similar to Japanese IMEs: the Up/Down arrow keys are used to navigate candidates, and Enter confirms the selected candidate.

2026-03-13 6 39 17 AM

Screencasts: Another example

Using the built-in Korean IME on Windows, I type two lines: the first line in Hangul, and the second line as the same word converted to Hanja.

Correct behavior in Notepad (reference):
7914-ref

Behavior after applying this PR, which matches the Notepad behavior:
7914-7967

Behavior after applying #7914 with the “IME mode” set to Korean (which is also the behavior before this PR being applied):
7914-7914
On the second line, each time a Hanja character is confirmed, an unintended newline is inserted. This mirrors the Japanese IME issues that are supposed to be fixed by setting the “IME mode” to Japanese.
(These Japanese IME issues are fixed in this PR as mentioned before.)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 11, 2026

Preview available at https://egui-pr-preview.github.io/pr/7967-fix-undesired-keyboard-input-events-during-ime-composition
Note that it might take a couple seconds for the update to show up after the preview_build workflow has completed.

View snapshot changes at kitdiff

@umajho
Copy link
Copy Markdown
Contributor Author

umajho commented Mar 11, 2026

Related winit issue: rust-windowing/winit#4508

@umajho umajho changed the title Fix: winit::event::KeyboardInput fiddling with IME composition on Windows Fix winit::event::KeyboardInput fiddling with IME composition on Windows properly Mar 12, 2026
@umajho umajho marked this pull request as ready for review March 13, 2026 22:24
@umajho
Copy link
Copy Markdown
Contributor Author

umajho commented Mar 13, 2026

Hello.
Since you were involved in the issues/PRs related to this PR, I'd appreciate it if you could take a look at this PR.
Sorry if the pinging annoyed you.

@choyunjin (Issue #7809)
@ClozedJP (Issue #7876)
@cats2333 (Issue #7908)
@sevenc-nanashi (PR #7877)
@rustbasic (PR #7898, PR #7914, and PR #4794)

@sevenc-nanashi
Copy link
Copy Markdown

sevenc-nanashi commented Mar 17, 2026

Tested on Windows 11 with Google Japanese IME, it's working very well, like other applications that supports IME. Nice work!

Speaking of implementation, as the behavior which winit sends keyboard events that is consumed by IME is confirmed as a bug, it would be nice to have a comment that says the implementation is workaround and will be fixed by winit.

@umajho umajho changed the title Fix winit::event::KeyboardInput fiddling with IME composition on Windows properly Workaround winit::event::KeyboardInput fiddling with IME composition on Windows properly Mar 17, 2026
@umajho umajho changed the title Workaround winit::event::KeyboardInput fiddling with IME composition on Windows properly Work around winit::event::KeyboardInput fiddling with IME composition on Windows properly Mar 19, 2026
@lucasmerlin lucasmerlin added bug Something is broken IME egui-winit porblems related to winit labels Mar 19, 2026
Copy link
Copy Markdown
Collaborator

@lucasmerlin lucasmerlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! Thanks for the thorough testing as well!

@umajho
Copy link
Copy Markdown
Contributor Author

umajho commented Mar 19, 2026

I noticed that @rustbasic reacted with an 👀 to the comment where I pinged people, so they may still be reviewing this PR. I think we should wait for their feedback before considering merging it.

@rustbasic
Copy link
Copy Markdown
Contributor

Great job, @umajho.
This Pull Request #7967 is a solid replacement for #4794.
It is useful for handling special key inputs during IME composition.

On a side note, I believe #7914 should still be pursued for separate reasons.
It(#7914) specifically addresses and resolves issues related to the 'Cheonjiin' Korean keyboard layout.
Furthermore, the start and end indices provided by Preedit are still necessary in certain contexts, so that PR remains relevant.

While not directly related to this (#7914 and #7967), there are still some unresolved issues regarding IME handling.
For instance, if the focus is moved to another TextEdit via a mouse click or the TAB key
while an IME composition is active, the character currently being typed is incorrectly transferred to the newly selected TextEdit.
( Currently, this is being partially prevented by checking the cursor_range. Related #4137 )

@umajho
Copy link
Copy Markdown
Contributor Author

umajho commented Mar 20, 2026

@rustbasic thanks for the feedback!

Could you elaborate more on the Cheonjiin issue Specifically:

  • how to reproduce it,
  • what is the expected behavior,
  • what is the actual behavior,
  • and by any chance, this PR inadvertently fixed it?

Regarding #4137

The ime_cursor_range it introduced led to #7485. Rather than adding more complexity by working around ime_cursor_range, I proposed in #7983 to replace #4137 with a simpler approach using mem.had_focus_last_frame.

I have tested #7983 on Windows and confirmed that the partial fix for the focus-related bug still works there. (And on macOS, the behavior remains incorrect as before.)

I guess the ultimate solution may involve explicitly disabling and re-enabling the IME when a TextEdit gains focus, though this is just a speculation.

@umajho
Copy link
Copy Markdown
Contributor Author

umajho commented Mar 20, 2026

Regarding the Preedit cursor range:

I'm also planning to introduce this in a follow-up PR that implements visuals for “real IME cursors & cursor ranges.” My intention is to work on it after this PR and #7983 are resolved.

By “real IME cursors & cursor ranges,” I mean something like the following:
Screenshot 2026-03-20 at 9 54 00 PM
Screenshot 2026-03-20 at 9 54 56 PM


Since this discussion is already outside the scope of this PR, I suggest we continue it in #7983 and merge this PR first.

(Since I've gathered feedback from both a Japanese IME user and a Korean IME user that this PR works, and I'm confident it also works for Chinese IMEs (at least for those Pinyin-like), I believe this PR is ready to be merged.)

@umajho umajho requested a review from lucasmerlin March 20, 2026 13:51
@rustbasic
Copy link
Copy Markdown
Contributor

rustbasic commented Mar 20, 2026

@rustbasic thanks for the feedback!

Could you elaborate more on the Cheonjiin issue Specifically:

  • how to reproduce it,
  • what is the expected behavior,
  • what is the actual behavior,
  • and by any chance, this PR inadvertently fixed it?

Regarding #4137

The ime_cursor_range it introduced led to #7485. Rather than adding more complexity by working around ime_cursor_range, I proposed in #7983 to replace #4137 with a simpler approach using mem.had_focus_last_frame.

I have tested #7983 on Windows and confirmed that the partial fix for the focus-related bug still works there. (And on macOS, the behavior remains incorrect as before.)

I guess the ultimate solution may involve explicitly disabling and re-enabling the IME when a TextEdit gains focus, though this is just a speculation.

Regarding the Cheonjiin Korean Keyboard issue:
The Cheonjiin layout issue was not resolved in #7967 and is not directly related to it.
For certain Korean characters with double final consonants (batchim), such as "않", the IME occasionally needs to revert and delete characters even after a Commit has been sent.
This is why the start and end indices from Preedit are essential.

Example sequence:
"안" -> 안"ㅎ" -> "않" -> 않"ㅇ" -> 않"아"
"안" -> 안"ㅎ" -> 안"해"

Regarding #7983:
While #7983 is effective when moving focus between different TextEdit elements on Windows, it also shows some effectiveness on WASM as well.
I believe #4137 should be maintained. If #4137 is removed, clicking a different position within the same TextEdit during composition causes the character to be incorrectly copied to that new position. Essentially, #4137 and #7983 address different problems: one handles issues within the same TextEdit, while the other handles transitions between different TextEdit elements.

Regarding disabling and re-enabling IME on focus:
The main challenge here is the non-deterministic timing of the Commit signal.
Disabling it for just a single frame might not be sufficient; it might require a delay of about 0.1 seconds. It could be a viable temporary workaround, though.

@rustbasic
Copy link
Copy Markdown
Contributor

rustbasic commented Mar 20, 2026

@umajho
In my opinion, it might be better not to modify #4137 in #7983. Instead, could you try setting wants_ime_events to had_focus_last_frame and resubmitting the PR? I’ll do some further testing in the meantime.

Update from further testing:
I have confirmed that the character copying issue is resolved on both Windows and WASM by combining #4137 and #7983.

@umajho
Copy link
Copy Markdown
Contributor Author

umajho commented Mar 20, 2026

Note

I moved the original content of this comment to #7983 (comment)

I think we should continue our discussion in #7983, since we are now talking about what comes after this PR, which is what I originally planned for #7983.

@cats2333
Copy link
Copy Markdown

Finally, a smooth typing experience in egui on Windows! Thank you for the extensive testing across different environments. You've solved a long-standing pain point for us : )

@emilk emilk added the eframe Relates to epi and eframe label Mar 24, 2026
@emilk emilk changed the title Work around winit::event::KeyboardInput fiddling with IME composition on Windows properly Much improved IME Mar 24, 2026
@emilk emilk merged commit b4f9cd7 into emilk:main Mar 24, 2026
26 checks passed
@emilk
Copy link
Copy Markdown
Owner

emilk commented Mar 24, 2026

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something is broken eframe Relates to epi and eframe egui-winit porblems related to winit IME

Projects

None yet

6 participants