Add new Ime event for desktop platforms

This commit brings new Ime event to account for preedit state of input
method, also adding `Window::set_ime_allowed` to toggle IME input on
the particular window.

This commit implements API as designed in #1497 for desktop platforms.

Co-authored-by: Artur Kovacs <kovacs.artur.barnabas@gmail.com>
Co-authored-by: Markus Siglreithmaier <m.siglreith@gmail.com>
Co-authored-by: Murarth <murarth@gmail.com>
Co-authored-by: Yusuke Kominami <yukke.konan@gmail.com>
Co-authored-by: moko256 <koutaro.mo@gmail.com>
This commit is contained in:
Kirill Chibisov 2022-05-07 05:29:25 +03:00 committed by GitHub
parent b4175c1454
commit f04fa5d54f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1346 additions and 311 deletions

View file

@ -5,11 +5,11 @@ use sctk::reexports::protocols::unstable::text_input::v3::client::zwp_text_input
Event as TextInputEvent, ZwpTextInputV3,
};
use crate::event::WindowEvent;
use crate::event::{Ime, WindowEvent};
use crate::platform_impl::wayland;
use crate::platform_impl::wayland::event_loop::WinitState;
use super::{TextInputHandler, TextInputInner};
use super::{Preedit, TextInputHandler, TextInputInner};
#[inline]
pub(super) fn handle_text_input(
@ -30,8 +30,11 @@ pub(super) fn handle_text_input(
inner.target_window_id = Some(window_id);
// Enable text input on that surface.
text_input.enable();
text_input.commit();
if window_handle.ime_allowed.get() {
text_input.enable();
text_input.commit();
event_sink.push_window_event(WindowEvent::Ime(Ime::Enabled), window_id);
}
// Notify a window we're currently over about text input handler.
let text_input_handler = TextInputHandler {
@ -58,19 +61,45 @@ pub(super) fn handle_text_input(
text_input: text_input.detach(),
};
window_handle.text_input_left(text_input_handler);
event_sink.push_window_event(WindowEvent::Ime(Ime::Disabled), window_id);
}
TextInputEvent::PreeditString {
text,
cursor_begin,
cursor_end,
} => {
let cursor_begin = usize::try_from(cursor_begin).ok();
let cursor_end = usize::try_from(cursor_end).ok();
let text = text.unwrap_or_default();
inner.pending_preedit = Some(Preedit {
text,
cursor_begin,
cursor_end,
});
}
TextInputEvent::CommitString { text } => {
// Update currenly commited string.
inner.commit_string = text;
// Update currenly commited string and reset previous preedit.
inner.pending_preedit = None;
inner.pending_commit = Some(text.unwrap_or_default());
}
TextInputEvent::Done { .. } => {
let (window_id, text) = match (inner.target_window_id, inner.commit_string.take()) {
(Some(window_id), Some(text)) => (window_id, text),
let window_id = match inner.target_window_id {
Some(window_id) => window_id,
_ => return,
};
for ch in text.chars() {
event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id);
if let Some(text) = inner.pending_commit.take() {
event_sink.push_window_event(WindowEvent::Ime(Ime::Commit(text)), window_id);
}
// Push preedit string we've got after latest commit.
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(preedit.text, cursor_range);
event_sink.push_window_event(WindowEvent::Ime(event), window_id);
}
}
_ => (),

View file

@ -20,6 +20,17 @@ impl TextInputHandler {
self.text_input.set_cursor_rectangle(x, y, 0, 0);
self.text_input.commit();
}
#[inline]
pub fn set_input_allowed(&self, allowed: bool) {
if allowed {
self.text_input.enable();
} else {
self.text_input.disable();
}
self.text_input.commit();
}
}
/// A wrapper around text input to automatically destroy the object on `Drop`.
@ -52,15 +63,25 @@ struct TextInputInner {
/// Currently focused surface.
target_window_id: Option<WindowId>,
/// Pending string to commit.
commit_string: Option<String>,
/// Pending commit event which will be dispatched on `text_input_v3::Done`.
pending_commit: Option<String>,
/// Pending preedit event which will be dispatched on `text_input_v3::Done`.
pending_preedit: Option<Preedit>,
}
struct Preedit {
text: String,
cursor_begin: Option<usize>,
cursor_end: Option<usize>,
}
impl TextInputInner {
fn new() -> Self {
Self {
target_window_id: None,
commit_string: None,
pending_commit: None,
pending_preedit: None,
}
}
}