Skip to content

Commit

Permalink
Send empty Ime::Preedit before the Ime::Commit
Browse files Browse the repository at this point in the history
This should help downstream to automatically clear it.
  • Loading branch information
kchibisov committed Sep 11, 2022
1 parent 79dc6bf commit f1470d1
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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)

Expand Down
19 changes: 11 additions & 8 deletions src/event.rs
Expand Up @@ -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("é")
/// ```
///
Expand All @@ -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)]
Expand All @@ -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,
Expand Down
31 changes: 17 additions & 14 deletions src/platform_impl/linux/wayland/seat/text_input/handlers.rs
Expand Up @@ -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,
);
}
}
_ => (),
}
Expand Down
6 changes: 6 additions & 0 deletions src/platform_impl/linux/x11/event_processor.rs
Expand Up @@ -614,6 +614,12 @@ impl<T: 'static> EventProcessor<T> {
// 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)),
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/macos/view.rs
Expand Up @@ -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)),
Expand Down
8 changes: 8 additions & 0 deletions src/platform_impl/windows/event_loop.rs
Expand Up @@ -1261,6 +1261,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
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)),
Expand Down Expand Up @@ -1297,6 +1301,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
// 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)),
Expand Down

0 comments on commit f1470d1

Please sign in to comment.