Improve robustness of TextAgent in eframe/web#8045
Conversation
|
Preview available at https://egui-pr-preview.github.io/pr/8045-fix-cheonjiin View snapshot changes at kitdiff |
|
@rustbasic Hello. I'd appreciate it if you could test whether this solution works with the Cheonjiin IME you are using. It would also be very helpful if you could try this in other web setups to check for any regressions. Thanks in advance. |
| /// | ||
| /// `before_chars` and `after_chars` are the number of characters (not | ||
| /// bytes) to delete before and after the cursor, respectively. | ||
| DeleteSurrounding { |
There was a problem hiding this comment.
This is actually not an ad hoc variant introduced just for this PR. A variant with the same name was added to winit in 0.31 (though in this implementation, the fields are defined as *_chars rather than *_bytes).
https://docs.rs/winit/0.31.0-beta.2/winit/event/enum.Ime.html#variant.DeleteSurrounding
|
I can confirm this PR fixes the 2 issues mentioned in #8046, at least on my device. |
|
@umajho rustbasic-2026-03-31.mp4 |
|
Wow, that's bad. If you go Could you also check https://stackblitz.com/edit/egui-text-agent-experiment and record what it looks like when you input text? For reference, here is how GBoard behaves on the demo: |
|
Are you asking for the keyboard settings screen first? Here it is. 삼성키보드 = SAMSUNG Keyboard image blured. |
rustbasic-2026-03-31-21-16-53.mp4 |
|
With the changes mentioned in the comment, I get the best result when I modify However, there is still an issue: when ImeEvent::Commit(commit_text) => {
if commit_text == "\n" || commit_text == "\r" {
None
} else {
let mut ccursor = cursor_range.primary;
state.ime_enabled = false;
if !commit_text.is_empty()
&& cursor_range.secondary.index
== state.ime_cursor_range.secondary.index
{
ccursor = clear_preedit_text(text, &cursor_range);
text.insert_text_at(&mut ccursor, commit_text, char_limit);
}
Some(CCursorRange::one(ccursor))
}
}rustbasic-2026-03-31-21-48-00.mp4 |
Yes, thanks for the information. Sorry, my instructions were not clear. You need to go to the “preview” tab.
Thanks for the suggested changes. I will check them tomorrow. |
rustbasic-2026-03-31-23-32-37.mp4 |
|
@rustbasic I have made some changes. does this improve things? The stackblitz page has also been updated to reflect the changes. |
|
It appears to be fixable, since the issue does not occur when "않았다" is entered consecutively without any problems.
rustbasic-2026-04-01-18-46-25.mp4
rustbasic-2026-04-01-18-49-07.mp4 |
|
It appears to be fixable, since it works correctly when "않았다" is entered continuously. However, if you enter "않", wait briefly for a commit to occur, and then enter the next character, "않" gets duplicated. Result 1-1) "않않았다" rustbasic-2026-04-01-19-01-15.mp4If you enter "안", wait briefly for a commit to occur, and then enter the next character, "안" gets duplicated. Result 2-1) "안않았다" rustbasic-2026-04-01-19-11-08.mp4 |
|
Is this auto-committing behavior expected? I will try to figure out how to fix the problem tomorrow. |
|
Sorry for the delayed response. Update: |
# Conflicts: # crates/eframe/src/web/text_agent.rs # crates/egui/src/widgets/text_edit/builder.rs
|
FIXME: 5ce55ad causes Gboard 14.7.09 to require an extra initial delete keystroke before ASCII character deletion behaves normally.
|
… keyboard flickering on web (emilk#8078) * Closes N/A * Partially replaces emilk#7983 * Related: emilk#8045 * [x] I have followed the instructions in the PR template ## Details In emilk#7983, I modified `Memory::request_focus` to interrupt any ongoing IME composition. This fixed a bug where clicking inside an already focused `TextEdit` failed to cancel the active composition, resulting in duplicated text: emilk#8045 (comment) To avoid introducing API changes in that PR, I ensured the IME state was reset by forcing `PlatformOutput::ime` to `None` for at least one frame. While this works well on desktop platforms, it causes virtual keyboard flickering on the web: emilk#8045 (comment) In this PR, I delegate the responsibility for handling IME composition interruptions to integrations, allowing each integration to decide how to interrupt compositions in a flexible manner. ### The new field `should_interrupt_composition` on `IMEOutput`. Instead of introducing a new `OutputCommand` variant, this PR adds a new field `should_interrupt_composition` to `IMEOutput`. Interrupting an active composition is only meaningful when IME remains allowed. If IME should be disabled altogether, `PlatformOutput::ime` can simply be set to `None`. Given this, IMO, it is more appropriate to attach the interrupt signal to `IMEOutput` (i.e., the type of `PlatformOutput::ime`).
(cherry picked from commit 288224764bc979b3fb889135ce15823731184540)
… to work (cherry picked from commit 0273f25b51b88b20ebba1e8a51952656383d6c71)
… keyboard flickering on web (emilk#8078) * Closes N/A * Partially replaces emilk#7983 * Related: emilk#8045 * [x] I have followed the instructions in the PR template ## Details In emilk#7983, I modified `Memory::request_focus` to interrupt any ongoing IME composition. This fixed a bug where clicking inside an already focused `TextEdit` failed to cancel the active composition, resulting in duplicated text: emilk#8045 (comment) To avoid introducing API changes in that PR, I ensured the IME state was reset by forcing `PlatformOutput::ime` to `None` for at least one frame. While this works well on desktop platforms, it causes virtual keyboard flickering on the web: emilk#8045 (comment) In this PR, I delegate the responsibility for handling IME composition interruptions to integrations, allowing each integration to decide how to interrupt compositions in a flexible manner. ### The new field `should_interrupt_composition` on `IMEOutput`. Instead of introducing a new `OutputCommand` variant, this PR adds a new field `should_interrupt_composition` to `IMEOutput`. Interrupting an active composition is only meaningful when IME remains allowed. If IME should be disabled altogether, `PlatformOutput::ime` can simply be set to `None`. Given this, IMO, it is more appropriate to attach the interrupt signal to `IMEOutput` (i.e., the type of `PlatformOutput::ime`).
# Conflicts: # crates/egui/src/data/input.rs
# Conflicts: # crates/eframe/src/web/text_agent.rs I let `GLM-5.2 (max)` resolve the conflicts.
|
@rustbasic Sorry to bother you. When you have a chance, could you verify that the fix still works now that this PR has been rebased onto the current |
|
I have briefly tested #8045 on Windows 10, WASM (Chrome), and Android (Korean Cheonjiin keyboard).
Since these three symptoms occur even when #8045 is reverted (removed), they are bugs introduced in #8083. I will continue using it for a few more days and let you know if I find any other anomalies. |
|
Ouch, @lucasmerlin. I'm so embarrassed of myself. The reason this PR fixed it is that, during the merge with #8083, GLM added a defensive check to ensure 15fd72a#diff-180cddab275a9f2a9ba697c0c68fbf19182359aae05c360583d2484187c79815R98 (L98~L100) if selection_start > text_utf16.len() || selection_end > text_utf16.len() {
return None;
}
This PR still is not ready. Gboard 14.7.09 was working perfectly before, but this PR introduces some issues with backspace handling for it. Since IIRC it works in #8068, I want to figure out how to preserve Gboard 14.7.09's behavior while fixing the Cheonjiin issue at the same time. So, I think opening a separate PR is the better approach. On why this PR hasn't seen much progress: Unfortunately, I'm going to the dentist for some tooth issues, and there's a good chance that I will be recovering from a procedure afterward. Because of this, I probably wont' be available for at least the next 24 hours. If someone opens the fix PR, I'd really appreciate it. |
|
I think Symptom 2 is likely a bug introduced in this PR, because at the moment it doesn't seem reproducible for #8083 due to Symptom 1. Maybe the app freezing was mistaken for the cursor not showing by you? I can't reproduce Symptom 3 on egui.rs. Or perhaps I misunderstood your reproduction steps? Could you upload a screencast? |
|
Even after applying #8271 and testing, Symptom 2 still occurs. Therefore, Symptom 2 is indeed an issue introduced by #8083 (and is not a simple misunderstanding caused by the app freezing). To reproduce Symptom 3, you first need to input some characters so that a new text cursor appears, and then select (highlight) a block of text. |
|
screencast DevTools.-.rustbasic.github.io_ezchat_.2026-06-28.15-22-26.mp4 |
Ohh, I see. You mean selecting text while the IME composition is still active. That also explains why I didn't encounter any of the three symptoms you reported. (Including the first one: although I made it sound obvious after your initial report, I don't think I wound have discovered it on my own. If you hadn't specifically mentioned “If I try to type characters in this state”, it would never have occurred to me to click elsewhere while composing.) |
|
@rustbasic Just like the The root cause is: On android Chrome, due to some complicated interaction in The root cause can actually be demonstrated using a commit from before #8083 was merged: If you type something, commit it, and click back into the TextEdit, you get a normal cursor: However, if you type something, click somewhere else without committing the composition, then click back into the TextEdit, you get what appears to be a selection (in reality, it is an active composition, but before #8083 there was no way to distinguish the two). If you type a number, the number is appended after the highlighted text instead of replacing it, which kinda indicates that the highlighted region is not actually a typical selection: |
|
I haven't looked into the internal details closely, so I am not familiar with the technical aspects. Please note that symptoms 2 and 3 above do not cause any issues if you set |
The second screencast in my reply above demonstrates a buggy behavior that is caused by the same underlying bug of the symptoms you mentioned. I can reproduce it both on the commit prior to #8083 and on #8271 with the legacy visuals enabled. What is happening is:
This can give the impression that everything is working correctly under the legacy visuals, when in fact the underlying state is still incorrect. |
|
@rustbasic could you verify whether the new commit fixes the symptoms you found? Thanks. |
|
Great news! I'll keep using it and let you know if I find any new issues. |





TextEdit#8068owns_ime_eventsonMemory#7983This PR primarily aims to address an issue with the Samsung Keyboard Korean Cheonjiin layout found by @rustbasic.
Since I don't have access to the same environment, I'll need to rely on their feedback for validation.
Additionally, I need to gather feedback on whether these changes introduce regressions in other setups, so this PR is not ready.
See also: https://stackblitz.com/edit/egui-text-agent-experiment?file=src%2FApp.tsx