From f1470d1ab785ea81bee6d249728165a21fb94a58 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sun, 11 Sep 2022 00:48:24 +0300 Subject: [PATCH] Send empty `Ime::Preedit` before the `Ime::Commit` This should help downstream to automatically clear it. --- CHANGELOG.md | 1 + src/event.rs | 19 +++++++----- .../linux/wayland/seat/text_input/handlers.rs | 31 ++++++++++--------- .../linux/x11/event_processor.rs | 6 ++++ src/platform_impl/macos/view.rs | 4 +++ src/platform_impl/windows/event_loop.rs | 8 +++++ 6 files changed, 47 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 738d5b3a0f..c4a44d8343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Windows, fixed ALT+Space shortcut to open window menu. - On Wayland, fixed `Ime::Preedit` not being sent on IME reset. - Fixed unbound version specified for `raw-window-handle` leading to compilation failures. +- Empty `Ime::Preedit` event will be sent before `Ime::Commit` to help clearing preedit. # 0.27.2 (2022-8-12) diff --git a/src/event.rs b/src/event.rs index c148bbf477..362029b4e6 100644 --- a/src/event.rs +++ b/src/event.rs @@ -777,8 +777,9 @@ pub struct KeyboardInput { /// the character you want to apply the accent to. This will generate the following event sequence: /// ```ignore /// // Press "`" key -/// Ime::Preedit("`", Some(0), Some(0)) +/// Ime::Preedit("`", Some((0, 0))) /// // Press "E" key +/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. /// Ime::Commit("é") /// ``` /// @@ -789,14 +790,15 @@ pub struct KeyboardInput { /// sequence could be obtained: /// ```ignore /// // Press "A" key -/// Ime::Preedit("a", Some(1), Some(1)) +/// Ime::Preedit("a", Some((1, 1))) /// // Press "B" key -/// Ime::Preedit("a b", Some(3), Some(3)) +/// Ime::Preedit("a b", Some((3, 3))) /// // Press left arrow key -/// Ime::Preedit("a b", Some(1), Some(1)) +/// Ime::Preedit("a b", Some((1, 1))) /// // Press space key -/// Ime::Preedit("啊b", Some(3), Some(3)) +/// Ime::Preedit("啊b", Some((3, 3))) /// // Press space key +/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. /// Ime::Commit("啊不") /// ``` #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -812,20 +814,21 @@ pub enum Ime { /// Notifies when a new composing text should be set at the cursor position. /// /// The value represents a pair of the preedit string and the cursor begin position and end - /// position. When it's `None`, the cursor should be hidden. + /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string + /// this indicates that preedit was cleared. /// /// The cursor position is byte-wise indexed. Preedit(String, Option<(usize, usize)>), /// Notifies when text should be inserted into the editor widget. /// - /// Any pending [`Preedit`](Self::Preedit) must be cleared. + /// Right before this event winit will send empty [`Self::Preedit`] event. Commit(String), /// Notifies when the IME was disabled. /// /// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or - /// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You can + /// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should /// also stop issuing IME related requests like [`Window::set_ime_position`] and clear pending /// preedit text. Disabled, diff --git a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs index f4f131d09a..8d12b8f4e7 100644 --- a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs @@ -88,25 +88,28 @@ pub(super) fn handle_text_input( _ => return, }; + // Clear preedit at the start of `Done`. + event_sink.push_window_event( + WindowEvent::Ime(Ime::Preedit(String::new(), None)), + window_id, + ); + + // Send `Commit`. if let Some(text) = inner.pending_commit.take() { event_sink.push_window_event(WindowEvent::Ime(Ime::Commit(text)), window_id); } - // Always send preedit on `Done` events. - let (text, range) = inner - .pending_preedit - .take() - .map(|preedit| { - let cursor_range = preedit - .cursor_begin - .map(|b| (b, preedit.cursor_end.unwrap_or(b))); - - (preedit.text, cursor_range) - }) - .unwrap_or_default(); + // Send preedit. + if let Some(preedit) = inner.pending_preedit.take() { + let cursor_range = preedit + .cursor_begin + .map(|b| (b, preedit.cursor_end.unwrap_or(b))); - let event = Ime::Preedit(text, range); - event_sink.push_window_event(WindowEvent::Ime(event), window_id); + event_sink.push_window_event( + WindowEvent::Ime(Ime::Preedit(preedit.text, cursor_range)), + window_id, + ); + } } _ => (), } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 54cdc59b03..bae5be0204 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -614,6 +614,12 @@ impl EventProcessor { // If we're composing right now, send the string we've got from X11 via // Ime::Commit. if self.is_composing && keycode == 0 && !written.is_empty() { + let event = Event::WindowEvent { + window_id, + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), + }; + callback(event); + let event = Event::WindowEvent { window_id, event: WindowEvent::Ime(Ime::Commit(written)), diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 1b40d76d9c..c776688e33 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -641,6 +641,10 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran //let event: id = msg_send![NSApp(), currentEvent]; if state.is_ime_enabled() && !is_control { + AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: WindowId(get_window_id(state.ns_window)), + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), + })); AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::Ime(Ime::Commit(string)), diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3c48f93360..30401a7a96 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1261,6 +1261,10 @@ unsafe fn public_window_callback_inner( if let Some(text) = ime_context.get_composed_text() { userdata.window_state.lock().ime_state = ImeState::Enabled; + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), + }); userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::Ime(Ime::Commit(text)), @@ -1297,6 +1301,10 @@ unsafe fn public_window_callback_inner( // trying receiving composing result and commit if exists. let ime_context = ImeContext::current(window); if let Some(text) = ime_context.get_composed_text() { + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), + }); userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::Ime(Ime::Commit(text)),