mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Improve IME, and restrict mac-specific workaround (#7973)
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md)
before opening a Pull Request!
* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.
Please be patient! I will review your PR, but my time is limited!
-->
* Closes N/A
* [x] I have followed the instructions in the PR template
My PR that fixes the macOS backspacing issue (#7810) unfortunately
breaks text selection on Wayland (Fedora KDE Plasma Desktop 43 [Wayland,
with or without IBus]). I had actually tested on a Wayland setup but
failed to notice that :(
Windows and Linux+X11 (Debian 13 [Cinnamon 6.4.10 + X11 + fcitx5 5.1.2])
are not affected.
This PR fixes the issue by restricting the macOS fix to macOS-only.
<details><summary>Here is the correct behavior on Wayland after this PR
(and before #7810 is applied)</summary>

</details>
<details><summary>Here is the buggy behavior on Wayland before this
PR</summary>

</details>
## Cause of the Wayland issue
On Wayland, `winit` constantly emits `winit::event::Ime::Preedit("",
None)` events.
PR #7810 added these lines for handling `winit::event::Ime::Preedit(_,
None)` in `egui-winit` without considering the `target_os`:
14afefa252/crates/egui-winit/src/lib.rs (L619-L621)
As a result, while text is being selected, `egui-winit` receives these
`winit::event::Ime::Preedit("", None)` events from `winit` and forwards
them to `egui` as `egui::ImeEvent::Preedit("")`. `egui` then clears the
current text selection, because it currently does not distinguish
between IME pre-edit text and selected text.
---------
Co-authored-by: lucasmerlin <hi@lucasmerlin.me>
This commit is contained in:
@@ -548,23 +548,23 @@ impl State {
|
|||||||
///
|
///
|
||||||
/// | Setup | Events in Order |
|
/// | Setup | Events in Order |
|
||||||
/// | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
/// | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
/// | a-macos15-apple_shuangpin | `Predict("", None)` -> `Commit("测试")` |
|
/// | a-macos15-apple_shuangpin | `Preedit("", None)` -> `Commit("测试")` |
|
||||||
/// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Predict("", None)` -> `Commit("测试")` -> `Predict("", Some(0, 0))` -> `Predict("", None)` (duplicate until `TextEdit` blurred) |
|
/// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit("", None)` -> `Commit("测试")` -> `Preedit("", Some(0, 0))` -> `Preedit("", None)` (duplicate until `TextEdit` blurred) |
|
||||||
/// | c-windows11-ms_pinyin | `Predict("测试", Some(…))` -> `Predict("", None)` -> `Commit("测试")` -> `Disabled` |
|
/// | c-windows11-ms_pinyin | `Preedit("测试", Some(…))` -> `Preedit("", None)` -> `Commit("测试")` -> `Disabled` |
|
||||||
///
|
///
|
||||||
/// #### Situation: pressed backspace to delete the last character in the prediction
|
/// #### Situation: pressed backspace to delete the last character in the composition
|
||||||
///
|
///
|
||||||
/// | Setup | Events in Order |
|
/// | Setup | Events in Order |
|
||||||
/// | a-macos15-apple_shuangpin | `Predict("", None)` |
|
/// | a-macos15-apple_shuangpin | `Preedit("", None)` |
|
||||||
/// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Predict("", Some(0, 0))` -> `Predict("", None)` (duplicate until `TextEdit` blurred) |
|
/// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit("", Some(0, 0))` -> `Preedit("", None)` (duplicate until `TextEdit` blurred) |
|
||||||
/// | c-windows11-ms_pinyin | `Predict("", Some(0, 0))` -> `Predict("", None)` -> `Commit("")` -> `Disabled` |
|
/// | c-windows11-ms_pinyin | `Preedit("", Some(0, 0))` -> `Preedit("", None)` -> `Commit("")` -> `Disabled` |
|
||||||
///
|
///
|
||||||
/// #### Situation: clicked somewhere else while there is an active composition with the prediction "ce"
|
/// #### Situation: clicked somewhere else while there is an active composition with the pre-edit text "ce"
|
||||||
///
|
///
|
||||||
/// | Setup | Events in Order |
|
/// | Setup | Events in Order |
|
||||||
/// | ------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
/// | ------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
/// | a-macos15-apple_shuangpin | nothing emitted |
|
/// | a-macos15-apple_shuangpin | nothing emitted |
|
||||||
/// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Predict("", Some(0, 0))` (duplicate) -> `Predict("", None)` (duplicate until `TextEdit` blurred) |
|
/// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit("", Some(0, 0))` (duplicate) -> `Preedit("", None)` (duplicate until `TextEdit` blurred) |
|
||||||
/// | c-windows11-ms_pinyin | nothing emitted |
|
/// | c-windows11-ms_pinyin | nothing emitted |
|
||||||
fn on_ime(&mut self, ime: &winit::event::Ime) {
|
fn on_ime(&mut self, ime: &winit::event::Ime) {
|
||||||
// // code for inspecting ime events emitted by winit:
|
// // code for inspecting ime events emitted by winit:
|
||||||
@@ -610,15 +610,26 @@ impl State {
|
|||||||
self.ime_event_disable();
|
self.ime_event_disable();
|
||||||
}
|
}
|
||||||
winit::event::Ime::Preedit(_, None) => {
|
winit::event::Ime::Preedit(_, None) => {
|
||||||
// we need to emit this on macOS, since winit doesn't emit
|
if cfg!(target_os = "macos") {
|
||||||
// `Predict("", Some(0, 0))` before this event on macOS when the
|
// On macOS, when the user presses backspace to delete the
|
||||||
// user deletes the last character in the prediction with the
|
// last character in an IME composition, `winit` only emits
|
||||||
// backspace key. Without this, only `egui::ImeEvent::Disabled`
|
// `winit::event::Ime::Preedit("", None)` without a
|
||||||
// is emitted here, leading to the last character being left in
|
// preceding `winit::event::Ime::Preedit("", Some(0, 0))`.
|
||||||
// TextEdit in such situation.
|
//
|
||||||
self.egui_input
|
// The current implementation of `egui::TextEdit` relies on
|
||||||
.events
|
// receiving an `egui::ImeEvent::Preedit("")` to remove the
|
||||||
.push(egui::Event::Ime(egui::ImeEvent::Preedit(String::new())));
|
// last character in the composition in this case, so we
|
||||||
|
// emit it here.
|
||||||
|
//
|
||||||
|
// This is guarded to macOS-only, as applying it on other
|
||||||
|
// platforms is unnecessary and can cause undesired
|
||||||
|
// behavior.
|
||||||
|
// See: https://github.com/emilk/egui/pull/7973
|
||||||
|
self.egui_input
|
||||||
|
.events
|
||||||
|
.push(egui::Event::Ime(egui::ImeEvent::Preedit(String::new())));
|
||||||
|
}
|
||||||
|
|
||||||
self.ime_event_disable();
|
self.ime_event_disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1066,26 +1066,36 @@ fn events(
|
|||||||
} => check_for_mutating_key_press(os, &cursor_range, text, galley, modifiers, *key),
|
} => check_for_mutating_key_press(os, &cursor_range, text, galley, modifiers, *key),
|
||||||
|
|
||||||
Event::Ime(ime_event) => {
|
Event::Ime(ime_event) => {
|
||||||
/// Empty prediction can be produced with [`ImeEvent::Preedit`]
|
/// Both `ImeEvent::Preedit("")` and `ImeEvent::Commit("")`
|
||||||
/// or [`ImeEvent::Commit`] when user press backspace or escape
|
/// might be emitted from different integrations to signify that
|
||||||
/// during IME, so this function should be called in both cases
|
/// the current IME composition should be cleared.
|
||||||
/// to clear current text.
|
|
||||||
///
|
///
|
||||||
/// Example platforms where only `ImeEvent::Preedit("")` of
|
/// Example integrations where only `ImeEvent::Preedit("")` of
|
||||||
/// those two events is emitted when the last character in the
|
/// those two events is emitted when the last character is
|
||||||
/// prediction is deleted:
|
/// deleted with a backspace:
|
||||||
/// - macOS 15.7.3.
|
/// - `egui-winit` on macOS 15.7.3.
|
||||||
/// - Debian13 with gnome48 and wayland.
|
/// - `egui-winit` on Debian13 with gnome48 and wayland.
|
||||||
///
|
///
|
||||||
/// An example platform where only `ImeEvent::Commit("")` of
|
/// An example integration where only `ImeEvent::Commit("")` of
|
||||||
/// those two events is emitted when the last character in the
|
/// those two events is emitted when the last character is
|
||||||
/// prediction is deleted:
|
/// deleted with a backspace:
|
||||||
/// - Safari 26.2 (on macOS 15.7.3).
|
/// - `eframe`'s web integration on Safari 26.2 (on macOS
|
||||||
fn clear_prediction(
|
/// 15.7.3).
|
||||||
|
///
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// The term “pre-edit string” is used by X11 and Wayland, and
|
||||||
|
/// we use “pre-edit text” and “pre-edit range” here in the
|
||||||
|
/// same manner.
|
||||||
|
/// See: <https://wayland.app/protocols/input-method-unstable-v2>
|
||||||
|
///
|
||||||
|
/// We previously referred to “pre-edit text” as “prediction”,
|
||||||
|
/// which is not standard and can mean different things.
|
||||||
|
fn clear_preedit_text(
|
||||||
text: &mut dyn TextBuffer,
|
text: &mut dyn TextBuffer,
|
||||||
cursor_range: &CCursorRange,
|
preedit_range: &CCursorRange,
|
||||||
) -> CCursor {
|
) -> CCursor {
|
||||||
text.delete_selected(cursor_range)
|
text.delete_selected(preedit_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
match ime_event {
|
match ime_event {
|
||||||
@@ -1094,33 +1104,33 @@ fn events(
|
|||||||
state.ime_cursor_range = cursor_range;
|
state.ime_cursor_range = cursor_range;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ImeEvent::Preedit(text_mark) => {
|
ImeEvent::Preedit(preedit_text) => {
|
||||||
if text_mark == "\n" || text_mark == "\r" {
|
if preedit_text == "\n" || preedit_text == "\r" {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let mut ccursor = clear_prediction(text, &cursor_range);
|
let mut ccursor = clear_preedit_text(text, &cursor_range);
|
||||||
|
|
||||||
let start_cursor = ccursor;
|
let start_cursor = ccursor;
|
||||||
if !text_mark.is_empty() {
|
if !preedit_text.is_empty() {
|
||||||
text.insert_text_at(&mut ccursor, text_mark, char_limit);
|
text.insert_text_at(&mut ccursor, preedit_text, char_limit);
|
||||||
}
|
}
|
||||||
state.ime_cursor_range = cursor_range;
|
state.ime_cursor_range = cursor_range;
|
||||||
Some(CCursorRange::two(start_cursor, ccursor))
|
Some(CCursorRange::two(start_cursor, ccursor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImeEvent::Commit(prediction) => {
|
ImeEvent::Commit(commit_text) => {
|
||||||
if prediction == "\n" || prediction == "\r" {
|
if commit_text == "\n" || commit_text == "\r" {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
state.ime_enabled = false;
|
state.ime_enabled = false;
|
||||||
|
|
||||||
let mut ccursor = clear_prediction(text, &cursor_range);
|
let mut ccursor = clear_preedit_text(text, &cursor_range);
|
||||||
|
|
||||||
if !prediction.is_empty()
|
if !commit_text.is_empty()
|
||||||
&& cursor_range.secondary.index
|
&& cursor_range.secondary.index
|
||||||
== state.ime_cursor_range.secondary.index
|
== state.ime_cursor_range.secondary.index
|
||||||
{
|
{
|
||||||
text.insert_text_at(&mut ccursor, prediction, char_limit);
|
text.insert_text_at(&mut ccursor, commit_text, char_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(CCursorRange::one(ccursor))
|
Some(CCursorRange::one(ccursor))
|
||||||
|
|||||||
Reference in New Issue
Block a user