Refactor and simplify input_method API

This commit is contained in:
Héctor Ramón Jiménez 2025-02-02 20:45:29 +01:00
parent d5ee9c2795
commit ae10adda74
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
19 changed files with 540 additions and 472 deletions

View file

@ -1,17 +1,112 @@
//! Listen to input method events.
use crate::Point;
use std::ops::Range;
/// The input method strategy of a widget.
#[derive(Debug, Clone, PartialEq)]
pub enum InputMethod<T = String> {
/// No input method is allowed.
Disabled,
/// Input methods are allowed, but not open yet.
Allowed,
/// Input method is open.
Open {
/// The position at which the input method dialog should be placed.
position: Point,
/// The [`Purpose`] of the input method.
purpose: Purpose,
/// The preedit to overlay on top of the input method dialog, if needed.
///
/// Ideally, your widget will show pre-edits on-the-spot; but, since that can
/// be tricky, you can instead provide the current pre-edit here and the
/// runtime will display it as an overlay (i.e. "Over-the-spot IME").
preedit: Option<T>,
},
}
/// The purpose of an [`InputMethod`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Purpose {
/// No special hints for the IME (default).
#[default]
Normal,
/// The IME is used for secure input (e.g. passwords).
Secure,
/// The IME is used to input into a terminal.
///
/// For example, that could alter OSK on Wayland to show extra buttons.
Terminal,
}
impl InputMethod {
/// Merges two [`InputMethod`] strategies, prioritizing the second one when both ready:
/// ```
/// # use iced_core::input_method::{InputMethod, Purpose};
/// # use iced_core::Point;
///
/// let open = InputMethod::Open {
/// position: Point::ORIGIN,
/// purpose: Purpose::Normal,
/// preedit: None,
/// };
///
/// let open_2 = InputMethod::Open {
/// position: Point::ORIGIN,
/// purpose: Purpose::Secure,
/// preedit: None,
/// };
///
/// let mut ime = InputMethod::Disabled;
///
/// ime.merge(&InputMethod::<String>::Allowed);
/// assert_eq!(ime, InputMethod::Allowed);
///
/// ime.merge(&InputMethod::<String>::Disabled);
/// assert_eq!(ime, InputMethod::Allowed);
///
/// ime.merge(&open);
/// assert_eq!(ime, open);
///
/// ime.merge(&open_2);
/// assert_eq!(ime, open_2);
/// ```
pub fn merge<T: AsRef<str>>(&mut self, other: &InputMethod<T>) {
match other {
InputMethod::Disabled => {}
InputMethod::Open {
position,
purpose,
preedit,
} => {
*self = Self::Open {
position: *position,
purpose: *purpose,
preedit: preedit
.as_ref()
.map(AsRef::as_ref)
.map(str::to_owned),
};
}
InputMethod::Allowed if matches!(self, Self::Disabled) => {
*self = Self::Allowed;
}
InputMethod::Allowed => {}
}
}
}
/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events.
///
/// This is also called a "composition event".
///
/// Most keypresses using a latin-like keyboard layout simply generate a
/// [`WindowEvent::KeyboardInput`]. However, one couldn't possibly have a key for every single
/// unicode character that the user might want to type
/// - so the solution operating systems employ is to allow the user to type these using _a sequence
/// of keypresses_ instead.
/// [`keyboard::Event::KeyPressed`](crate::keyboard::Event::KeyPressed).
/// However, one couldn't possibly have a key for every single
/// unicode character that the user might want to type. The solution operating systems employ is
/// to allow the user to type these using _a sequence of keypresses_ instead.
///
/// A prominent example of this is accents - many keyboard layouts allow you to first click the
/// A prominent example of this is accentsmany keyboard layouts allow you to first click the
/// "accent key", and then the character you want to apply the accent to. In this case, some
/// platforms will generate the following event sequence:
///
@ -19,13 +114,13 @@ use std::ops::Range;
/// // Press "`" key
/// Ime::Preedit("`", Some((0, 0)))
/// // Press "E" key
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Preedit("", None) // Synthetic event generated to clear preedit.
/// Ime::Commit("é")
/// ```
///
/// Additionally, certain input devices are configured to display a candidate box that allow the
/// user to select the desired character interactively. (To properly position this box, you must use
/// [`Window::set_ime_cursor_area`].)
/// [`Shell::request_input_method`](crate::Shell::request_input_method).)
///
/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the
/// following event sequence could be obtained:
@ -40,17 +135,19 @@ use std::ops::Range;
/// // Press space key
/// Ime::Preedit("啊b", Some((3, 3)))
/// // Press space key
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Preedit("", None) // Synthetic event generated to clear preedit.
/// Ime::Commit("啊不")
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Event {
/// Notifies when the IME was enabled.
/// Notifies when the IME was opened.
///
/// After getting this event you could receive [`Preedit`][Self::Preedit] and
/// [`Commit`][Self::Commit] events. You should also start performing IME related requests
/// like [`Window::set_ime_cursor_area`].
Enabled,
/// like [`Shell::request_input_method`].
///
/// [`Shell::request_input_method`]: crate::Shell::request_input_method
Opened,
/// Notifies when a new composing text should be set at the cursor position.
///
@ -63,14 +160,16 @@ pub enum Event {
/// Notifies when text should be inserted into the editor widget.
///
/// Right before this event winit will send empty [`Self::Preedit`] event.
/// Right before this event, an empty [`Self::Preedit`] event will be issued.
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 should
/// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear
/// [`Commit`][Self::Commit] events until the next [`Opened`][Self::Opened] event. You should
/// also stop issuing IME related requests like [`Shell::request_input_method`] and clear
/// pending preedit text.
Disabled,
///
/// [`Shell::request_input_method`]: crate::Shell::request_input_method
Closed,
}