From 18326e3ad57e131883c2387fd6ed919e2d49ce0f Mon Sep 17 00:00:00 2001 From: "xi.nie" Date: Wed, 14 Sep 2022 15:55:03 +0800 Subject: [PATCH 1/5] try add ime support to native --- crates/eframe/src/native/run.rs | 4 ++++ crates/egui-winit/src/lib.rs | 22 +++++++++++++++++++- crates/egui/src/widgets/text_edit/builder.rs | 18 +++++++++------- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index f97269704a1..edc463763b8 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -361,6 +361,8 @@ mod glow_integration { let theme = system_theme.unwrap_or(self.native_options.default_theme); integration.egui_ctx.set_visuals(theme.egui_visuals()); + gl_window.window().set_ime_allowed(true); + { let event_loop_proxy = self.repaint_proxy.clone(); integration.egui_ctx.set_request_repaint_callback(move || { @@ -729,6 +731,8 @@ mod wgpu_integration { let theme = system_theme.unwrap_or(self.native_options.default_theme); integration.egui_ctx.set_visuals(theme.egui_visuals()); + window.set_ime_allowed(true); + { let event_loop_proxy = self.repaint_proxy.clone(); integration.egui_ctx.set_request_repaint_callback(move || { diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index d0b8323f6e5..bd4e59f5ac8 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -251,6 +251,27 @@ impl State { consumed, } } + WindowEvent::Ime(ime) => { + match ime { + winit::event::Ime::Enabled => { + self.egui_input.events.push(egui::Event::CompositionStart) + } + winit::event::Ime::Disabled => (), + winit::event::Ime::Commit(text) => self + .egui_input + .events + .push(egui::Event::CompositionEnd(text.clone())), + winit::event::Ime::Preedit(text, ..) => self + .egui_input + .events + .push(egui::Event::CompositionUpdate(text.clone())), + } + + EventResponse { + repaint: true, + consumed: egui_ctx.wants_keyboard_input(), + } + } WindowEvent::KeyboardInput { input, .. } => { self.on_keyboard_input(input); let consumed = egui_ctx.wants_keyboard_input() @@ -317,7 +338,6 @@ impl State { | WindowEvent::CloseRequested | WindowEvent::CursorEntered { .. } | WindowEvent::Destroyed - | WindowEvent::Ime(_) | WindowEvent::Occluded(_) | WindowEvent::Resized(_) | WindowEvent::ThemeChanged(_) diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 26a81c13ca4..0bd611f2272 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -618,7 +618,11 @@ impl<'t> TextEdit<'t> { if interactive { // eframe web uses `text_cursor_pos` when showing IME, // so only set it when text is editable and visible! - ui.ctx().output().text_cursor_pos = Some(cursor_pos.left_top()); + if cfg!(target_arch = "wasm32") { + ui.ctx().output().text_cursor_pos = Some(cursor_pos.left_top()); + } else { + ui.ctx().output().text_cursor_pos = Some(cursor_pos.left_bottom()); + } } } } @@ -828,14 +832,14 @@ fn events( } Event::CompositionEnd(prediction) => { - if !prediction.is_empty() - && prediction != "\n" - && prediction != "\r" - && state.has_ime - { + // prediction == "" means winit::event::Ime::Disabled, and we need to clear + // old text input. + if prediction != "\n" && prediction != "\r" && state.has_ime { state.has_ime = false; let mut ccursor = delete_selected(text, &cursor_range); - insert_text(&mut ccursor, text, prediction); + if !prediction.is_empty() { + insert_text(&mut ccursor, text, prediction); + } Some(CCursorRange::one(ccursor)) } else { None From 7e580b897627f5de3625c95b9f9c9213a85411b2 Mon Sep 17 00:00:00 2001 From: "xi.nie" Date: Thu, 15 Sep 2022 19:50:54 +0800 Subject: [PATCH 2/5] support ime in MacOS --- crates/egui-winit/src/lib.rs | 42 ++++++++++++++------ crates/egui/src/widgets/text_edit/builder.rs | 12 ++++-- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index bd4e59f5ac8..fe644457b60 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -83,6 +83,9 @@ pub struct State { /// /// Only one touch will be interpreted as pointer at any time. pointer_touch_id: Option, + + /// track ime state + input_method_editor_started: bool, } impl State { @@ -109,6 +112,8 @@ impl State { simulate_touch_screen: false, pointer_touch_id: None, + + input_method_editor_started: false, } } @@ -252,20 +257,33 @@ impl State { } } WindowEvent::Ime(ime) => { + // on Mac even Cmd-C is preessed during ime, a `c` is pushed to Preddit. + // So no need to check is_mac_cmd. + // + // How winit produce `Ime::Enabled` and `Ime::Disabled` differs in MacOS + // and Windows. + // + // - On Windows, before and after each Commit will produce an Enable/Disabled + // event. + // - On MacOS, only when user explicit enable/disable ime. No Disabled + // after Commit. + // + // We use input_method_editor_started to mannualy insert CompositionStart + // between Commits. match ime { - winit::event::Ime::Enabled => { - self.egui_input.events.push(egui::Event::CompositionStart) - } + winit::event::Ime::Enabled => (), winit::event::Ime::Disabled => (), - winit::event::Ime::Commit(text) => self - .egui_input - .events - .push(egui::Event::CompositionEnd(text.clone())), - winit::event::Ime::Preedit(text, ..) => self - .egui_input - .events - .push(egui::Event::CompositionUpdate(text.clone())), - } + winit::event::Ime::Commit(text) => { + self.input_method_editor_started = false; + self.egui_input.events.push(egui::Event::CompositionEnd(text.clone())) + }, + winit::event::Ime::Preedit(text, ..) => { + if !self.input_method_editor_started { + self.egui_input.events.push(egui::Event::CompositionStart); + } + self.egui_input.events.push(egui::Event::CompositionUpdate(text.clone())) + } + }; EventResponse { repaint: true, diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 0bd611f2272..1a7862250c6 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -618,6 +618,8 @@ impl<'t> TextEdit<'t> { if interactive { // eframe web uses `text_cursor_pos` when showing IME, // so only set it when text is editable and visible! + // But `winit` and `egui_web` differs in how to set the + // position of IME. if cfg!(target_arch = "wasm32") { ui.ctx().output().text_cursor_pos = Some(cursor_pos.left_top()); } else { @@ -820,11 +822,15 @@ fn events( } Event::CompositionUpdate(text_mark) => { - if !text_mark.is_empty() && text_mark != "\n" && text_mark != "\r" && state.has_ime + // empty prediction can be produced when user press backspace + // or escape during ime. We should clear current text. + if text_mark != "\n" && text_mark != "\r" && state.has_ime { let mut ccursor = delete_selected(text, &cursor_range); let start_cursor = ccursor; - insert_text(&mut ccursor, text, text_mark); + if !text_mark.is_empty() { + insert_text(&mut ccursor, text, text_mark); + } Some(CCursorRange::two(start_cursor, ccursor)) } else { None @@ -832,8 +838,6 @@ fn events( } Event::CompositionEnd(prediction) => { - // prediction == "" means winit::event::Ime::Disabled, and we need to clear - // old text input. if prediction != "\n" && prediction != "\r" && state.has_ime { state.has_ime = false; let mut ccursor = delete_selected(text, &cursor_range); From a44071e357da93c986a522b294ff77717e5249ee Mon Sep 17 00:00:00 2001 From: "xi.nie" Date: Thu, 15 Sep 2022 20:04:05 +0800 Subject: [PATCH 3/5] cargo fmt and clippy --- crates/egui-winit/src/lib.rs | 22 ++++++++++++-------- crates/egui/src/widgets/text_edit/builder.rs | 7 +++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index fe644457b60..4ac514c3e5a 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -257,17 +257,17 @@ impl State { } } WindowEvent::Ime(ime) => { - // on Mac even Cmd-C is preessed during ime, a `c` is pushed to Preddit. + // on Mac even Cmd-C is preessed during ime, a `c` is pushed to Preddit. // So no need to check is_mac_cmd. // - // How winit produce `Ime::Enabled` and `Ime::Disabled` differs in MacOS - // and Windows. + // How winit produce `Ime::Enabled` and `Ime::Disabled` differs in MacOS + // and Windows. // // - On Windows, before and after each Commit will produce an Enable/Disabled // event. - // - On MacOS, only when user explicit enable/disable ime. No Disabled + // - On MacOS, only when user explicit enable/disable ime. No Disabled // after Commit. - // + // // We use input_method_editor_started to mannualy insert CompositionStart // between Commits. match ime { @@ -275,13 +275,17 @@ impl State { winit::event::Ime::Disabled => (), winit::event::Ime::Commit(text) => { self.input_method_editor_started = false; - self.egui_input.events.push(egui::Event::CompositionEnd(text.clone())) - }, + self.egui_input + .events + .push(egui::Event::CompositionEnd(text.clone())) + } winit::event::Ime::Preedit(text, ..) => { - if !self.input_method_editor_started { + if !self.input_method_editor_started { self.egui_input.events.push(egui::Event::CompositionStart); } - self.egui_input.events.push(egui::Event::CompositionUpdate(text.clone())) + self.egui_input + .events + .push(egui::Event::CompositionUpdate(text.clone())) } }; diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 1a7862250c6..6d83389c577 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -618,7 +618,7 @@ impl<'t> TextEdit<'t> { if interactive { // eframe web uses `text_cursor_pos` when showing IME, // so only set it when text is editable and visible! - // But `winit` and `egui_web` differs in how to set the + // But `winit` and `egui_web` differs in how to set the // position of IME. if cfg!(target_arch = "wasm32") { ui.ctx().output().text_cursor_pos = Some(cursor_pos.left_top()); @@ -822,10 +822,9 @@ fn events( } Event::CompositionUpdate(text_mark) => { - // empty prediction can be produced when user press backspace + // empty prediction can be produced when user press backspace // or escape during ime. We should clear current text. - if text_mark != "\n" && text_mark != "\r" && state.has_ime - { + if text_mark != "\n" && text_mark != "\r" && state.has_ime { let mut ccursor = delete_selected(text, &cursor_range); let start_cursor = ccursor; if !text_mark.is_empty() { From 25bcc1e58cc1b01555464124c3d0494e98a08e5c Mon Sep 17 00:00:00 2001 From: "xi.nie" Date: Thu, 15 Sep 2022 21:42:40 +0800 Subject: [PATCH 4/5] set 'input_method_editor_started' after Preedit --- crates/egui-winit/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 4ac514c3e5a..cbcfa81885b 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -257,7 +257,7 @@ impl State { } } WindowEvent::Ime(ime) => { - // on Mac even Cmd-C is preessed during ime, a `c` is pushed to Preddit. + // on Mac even Cmd-C is preessed during ime, a `c` is pushed to Preedit. // So no need to check is_mac_cmd. // // How winit produce `Ime::Enabled` and `Ime::Disabled` differs in MacOS @@ -281,6 +281,7 @@ impl State { } winit::event::Ime::Preedit(text, ..) => { if !self.input_method_editor_started { + self.input_method_editor_started = true; self.egui_input.events.push(egui::Event::CompositionStart); } self.egui_input From 4a476706434e6ac5b9641c0cb4e97bf2a7794ce9 Mon Sep 17 00:00:00 2001 From: "xi.nie" Date: Thu, 15 Sep 2022 22:27:12 +0800 Subject: [PATCH 5/5] fix cargo cranky --- crates/egui-winit/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index cbcfa81885b..e9e997831a7 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -271,13 +271,12 @@ impl State { // We use input_method_editor_started to mannualy insert CompositionStart // between Commits. match ime { - winit::event::Ime::Enabled => (), - winit::event::Ime::Disabled => (), + winit::event::Ime::Enabled | winit::event::Ime::Disabled => (), winit::event::Ime::Commit(text) => { self.input_method_editor_started = false; self.egui_input .events - .push(egui::Event::CompositionEnd(text.clone())) + .push(egui::Event::CompositionEnd(text.clone())); } winit::event::Ime::Preedit(text, ..) => { if !self.input_method_editor_started { @@ -286,7 +285,7 @@ impl State { } self.egui_input .events - .push(egui::Event::CompositionUpdate(text.clone())) + .push(egui::Event::CompositionUpdate(text.clone())); } };