diff --git a/src/event.rs b/src/event.rs index 9b0b54660f..7d1eb60aa9 100644 --- a/src/event.rs +++ b/src/event.rs @@ -513,9 +513,9 @@ pub struct KeyboardInput { #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum CompositionEvent { - CompositionStart, + CompositionStart(String), CompositionUpdate(String, usize), - CompositionEnd, + CompositionEnd(String), } /// Describes touch-screen input state. diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index d9f641edd9..6a96e01b0d 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -12,11 +12,12 @@ use super::{ use util::modifiers::{ModifierKeyState, ModifierKeymap}; -use crate::platform_impl::platform::x11::ime::ImeEventReceiver; +use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::{ - DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent, + CompositionEvent, DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, + TouchPhase, WindowEvent, }, event_loop::EventLoopWindowTarget as RootELW, }; @@ -34,6 +35,8 @@ pub(super) struct EventProcessor { // Number of touch events currently in progress pub(super) num_touch: u32, pub(super) first_touch: Option, + pub(super) is_composing: bool, + pub(super) composed_text: Option, } impl EventProcessor { @@ -605,6 +608,10 @@ impl EventProcessor { }; callback(event); } + if self.is_composing && !written.is_empty() { + self.composed_text = Some(written); + self.is_composing = false; + } } } @@ -1191,13 +1198,37 @@ impl EventProcessor { Err(_) => (), } match self.ime_event_receiver.try_recv() { - Ok((window, event)) => { - debug!("get composition event"); - callback(Event::WindowEvent { - window_id: mkwid(window), - event: WindowEvent::Composition(event), - }); - } + Ok((window, event)) => match event { + ImeEvent::Start => { + self.is_composing = true; + self.composed_text = None; + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Composition(CompositionEvent::CompositionStart( + "".to_owned(), + )), + }); + } + ImeEvent::Update(text, position) => { + if self.is_composing { + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Composition(CompositionEvent::CompositionUpdate( + text, position, + )), + }); + } + } + ImeEvent::End => { + self.is_composing = false; + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Composition(CompositionEvent::CompositionEnd( + self.composed_text.take().unwrap_or("".to_owned()), + )), + }); + } + }, Err(_) => (), } } diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs index d93f9bc9cf..ddf4f65432 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -6,10 +6,8 @@ use std::{ }; use super::{ffi, util, XConnection, XError}; -use crate::event::CompositionEvent::{CompositionEnd, CompositionStart, CompositionUpdate}; -use crate::platform_impl::platform::x11::ime::ImeEventSender; +use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender}; use std::ffi::CStr; -use std::iter::FromIterator; use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct}; #[derive(Debug)] @@ -27,12 +25,10 @@ extern "C" fn preedit_start_callback( client_data.text.clear(); client_data.cursor_pos = 0; - client_data.is_composing = true; client_data .event_sender - .send((client_data.window, CompositionStart)) + .send((client_data.window, ImeEvent::Start)) .expect("failed to send composition start event"); - debug!("preedit start callback"); -1 } @@ -43,22 +39,10 @@ extern "C" fn preedit_done_callback( ) { let client_data = unsafe { &mut *(client_data as *mut ImeContextClientData) }; - if !client_data.text.is_empty() { - client_data.text.clear(); - client_data.cursor_pos = 0; - client_data - .event_sender - .send(( - client_data.window, - CompositionUpdate(client_data.text.iter().collect(), client_data.cursor_pos), - )) - .expect("failed to send composition update event"); - } client_data .event_sender - .send((client_data.window, CompositionEnd)) + .send((client_data.window, ImeEvent::End)) .expect("failed to send composition end event"); - client_data.is_composing = false; } extern "C" fn preedit_draw_callback( @@ -100,15 +84,16 @@ extern "C" fn preedit_draw_callback( client_data.text.split_off(chg_range.start); client_data.text.append(&mut new_chars); client_data.text.append(&mut old_text_tail); + let mut cursor_byte_pos = 0; + for i in 0..client_data.cursor_pos { + cursor_byte_pos += client_data.text[i].len_utf8(); + } client_data .event_sender .send(( client_data.window, - CompositionUpdate( - String::from_iter(client_data.text.clone()), - client_data.cursor_pos, - ), + ImeEvent::Update(client_data.text.iter().collect(), cursor_byte_pos), )) .expect("failed to send composition update event"); } @@ -126,10 +111,7 @@ extern "C" fn preedit_caret_callback( .event_sender .send(( client_data.window, - CompositionUpdate( - String::from_iter(client_data.text.clone()), - client_data.cursor_pos, - ), + ImeEvent::Update(client_data.text.iter().collect(), client_data.cursor_pos), )) .expect("failed to send composition update event"); } @@ -218,7 +200,6 @@ pub struct ImeContextClientData { pub event_sender: ImeEventSender, pub text: Vec, pub cursor_pos: usize, - pub is_composing: bool, } // WARNING: this struct doesn't destroy its XIC resource when dropped. @@ -245,7 +226,6 @@ impl ImeContext { event_sender, text: Vec::new(), cursor_pos: 0, - is_composing: false, }); let client_data_ptr = Box::into_raw(client_data); let preedit_callbacks = PreeditCallbacks::new(client_data_ptr as ffi::XPointer); diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs index 1e29c230b9..04ed0f4857 100644 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ b/src/platform_impl/linux/x11/ime/mod.rs @@ -19,12 +19,18 @@ use self::{ inner::{close_im, ImeInner}, input_method::PotentialInputMethods, }; -use crate::event::CompositionEvent; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ImeEvent { + Start, + Update(String, usize), + End, +} pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>; pub type ImeSender = Sender<(ffi::Window, i16, i16)>; -pub type ImeEventReceiver = Receiver<(ffi::Window, CompositionEvent)>; -pub type ImeEventSender = Sender<(ffi::Window, CompositionEvent)>; +pub type ImeEventReceiver = Receiver<(ffi::Window, ImeEvent)>; +pub type ImeEventSender = Sender<(ffi::Window, ImeEvent)>; #[derive(Debug)] pub enum ImeCreationError { diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 43abec25dc..7c12cb7195 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -227,6 +227,8 @@ impl EventLoop { device_mod_state: Default::default(), num_touch: 0, first_touch: None, + is_composing: false, + composed_text: None, }; // Register for device hotplug events