Skip to content

Commit

Permalink
follow web spec
Browse files Browse the repository at this point in the history
After the discussion on the PR, I decided to follow the Web
specification for the composition events. I added a few states to filter
out unnecessary events.
  • Loading branch information
garasubo committed Feb 14, 2020
1 parent ba8e004 commit 29f0d7e
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 43 deletions.
4 changes: 2 additions & 2 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
49 changes: 40 additions & 9 deletions src/platform_impl/linux/x11/event_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -34,6 +35,8 @@ pub(super) struct EventProcessor<T: 'static> {
// Number of touch events currently in progress
pub(super) num_touch: u32,
pub(super) first_touch: Option<u64>,
pub(super) is_composing: bool,
pub(super) composed_text: Option<String>,
}

impl<T: 'static> EventProcessor<T> {
Expand Down Expand Up @@ -605,6 +608,10 @@ impl<T: 'static> EventProcessor<T> {
};
callback(event);
}
if self.is_composing && !written.is_empty() {
self.composed_text = Some(written);
self.is_composing = false;
}
}
}

Expand Down Expand Up @@ -1191,13 +1198,37 @@ impl<T: 'static> EventProcessor<T> {
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(_) => (),
}
}
Expand Down
38 changes: 9 additions & 29 deletions src/platform_impl/linux/x11/ime/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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
}

Expand All @@ -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(
Expand Down Expand Up @@ -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");
}
Expand All @@ -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");
}
Expand Down Expand Up @@ -218,7 +200,6 @@ pub struct ImeContextClientData {
pub event_sender: ImeEventSender,
pub text: Vec<char>,
pub cursor_pos: usize,
pub is_composing: bool,
}

// WARNING: this struct doesn't destroy its XIC resource when dropped.
Expand All @@ -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);
Expand Down
12 changes: 9 additions & 3 deletions src/platform_impl/linux/x11/ime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ impl<T: 'static> EventLoop<T> {
device_mod_state: Default::default(),
num_touch: 0,
first_touch: None,
is_composing: false,
composed_text: None,
};

// Register for device hotplug events
Expand Down

0 comments on commit 29f0d7e

Please sign in to comment.