iced_core/
input_method.rs

1//! Listen to input method events.
2use crate::{Pixels, Point};
3
4use std::ops::Range;
5
6/// The input method strategy of a widget.
7#[derive(Debug, Clone, PartialEq)]
8pub enum InputMethod<T = String> {
9    /// Input method is disabled.
10    Disabled,
11    /// Input method is enabled.
12    Enabled {
13        /// The position at which the input method dialog should be placed.
14        position: Point,
15        /// The [`Purpose`] of the input method.
16        purpose: Purpose,
17        /// The preedit to overlay on top of the input method dialog, if needed.
18        ///
19        /// Ideally, your widget will show pre-edits on-the-spot; but, since that can
20        /// be tricky, you can instead provide the current pre-edit here and the
21        /// runtime will display it as an overlay (i.e. "Over-the-spot IME").
22        preedit: Option<Preedit<T>>,
23    },
24}
25
26/// The pre-edit of an [`InputMethod`].
27#[derive(Debug, Clone, PartialEq, Default)]
28pub struct Preedit<T = String> {
29    /// The current content.
30    pub content: T,
31    /// The selected range of the content.
32    pub selection: Option<Range<usize>>,
33    /// The text size of the content.
34    pub text_size: Option<Pixels>,
35}
36
37impl<T> Preedit<T> {
38    /// Creates a new empty [`Preedit`].
39    pub fn new() -> Self
40    where
41        T: Default,
42    {
43        Self::default()
44    }
45
46    /// Turns a [`Preedit`] into its owned version.
47    pub fn to_owned(&self) -> Preedit
48    where
49        T: AsRef<str>,
50    {
51        Preedit {
52            content: self.content.as_ref().to_owned(),
53            selection: self.selection.clone(),
54            text_size: self.text_size,
55        }
56    }
57}
58
59impl Preedit {
60    /// Borrows the contents of a [`Preedit`].
61    pub fn as_ref(&self) -> Preedit<&str> {
62        Preedit {
63            content: &self.content,
64            selection: self.selection.clone(),
65            text_size: self.text_size,
66        }
67    }
68}
69
70/// The purpose of an [`InputMethod`].
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
72pub enum Purpose {
73    /// No special hints for the IME (default).
74    #[default]
75    Normal,
76    /// The IME is used for secure input (e.g. passwords).
77    Secure,
78    /// The IME is used to input into a terminal.
79    ///
80    /// For example, that could alter OSK on Wayland to show extra buttons.
81    Terminal,
82}
83
84impl InputMethod {
85    /// Merges two [`InputMethod`] strategies, prioritizing the first one when both open:
86    /// ```
87    /// # use iced_core::input_method::{InputMethod, Purpose, Preedit};
88    /// # use iced_core::Point;
89    ///
90    /// let open = InputMethod::Enabled {
91    ///     position: Point::ORIGIN,
92    ///     purpose: Purpose::Normal,
93    ///     preedit: Some(Preedit { content: "1".to_owned(), selection: None, text_size: None }),
94    /// };
95    ///
96    /// let open_2 = InputMethod::Enabled {
97    ///     position: Point::ORIGIN,
98    ///     purpose: Purpose::Secure,
99    ///     preedit: Some(Preedit { content: "2".to_owned(), selection: None, text_size: None }),
100    /// };
101    ///
102    /// let mut ime = InputMethod::Disabled;
103    ///
104    /// ime.merge(&open);
105    /// assert_eq!(ime, open);
106    ///
107    /// ime.merge(&open_2);
108    /// assert_eq!(ime, open);
109    /// ```
110    pub fn merge<T: AsRef<str>>(&mut self, other: &InputMethod<T>) {
111        if let InputMethod::Enabled { .. } = self {
112            return;
113        }
114
115        *self = other.to_owned();
116    }
117
118    /// Returns true if the [`InputMethod`] is open.
119    pub fn is_enabled(&self) -> bool {
120        matches!(self, Self::Enabled { .. })
121    }
122}
123
124impl<T> InputMethod<T> {
125    /// Turns an [`InputMethod`] into its owned version.
126    pub fn to_owned(&self) -> InputMethod
127    where
128        T: AsRef<str>,
129    {
130        match self {
131            Self::Disabled => InputMethod::Disabled,
132            Self::Enabled {
133                position,
134                purpose,
135                preedit,
136            } => InputMethod::Enabled {
137                position: *position,
138                purpose: *purpose,
139                preedit: preedit.as_ref().map(Preedit::to_owned),
140            },
141        }
142    }
143}
144
145/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events.
146///
147/// This is also called a "composition event".
148///
149/// Most keypresses using a latin-like keyboard layout simply generate a
150/// [`keyboard::Event::KeyPressed`](crate::keyboard::Event::KeyPressed).
151/// However, one couldn't possibly have a key for every single
152/// unicode character that the user might want to type. The solution operating systems employ is
153/// to allow the user to type these using _a sequence of keypresses_ instead.
154///
155/// A prominent example of this is accents—many keyboard layouts allow you to first click the
156/// "accent key", and then the character you want to apply the accent to. In this case, some
157/// platforms will generate the following event sequence:
158///
159/// ```ignore
160/// // Press "`" key
161/// Ime::Preedit("`", Some((0, 0)))
162/// // Press "E" key
163/// Ime::Preedit("", None) // Synthetic event generated to clear preedit.
164/// Ime::Commit("é")
165/// ```
166///
167/// Additionally, certain input devices are configured to display a candidate box that allow the
168/// user to select the desired character interactively. (To properly position this box, you must use
169/// [`Shell::request_input_method`](crate::Shell::request_input_method).)
170///
171/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the
172/// following event sequence could be obtained:
173///
174/// ```ignore
175/// // Press "A" key
176/// Ime::Preedit("a", Some((1, 1)))
177/// // Press "B" key
178/// Ime::Preedit("a b", Some((3, 3)))
179/// // Press left arrow key
180/// Ime::Preedit("a b", Some((1, 1)))
181/// // Press space key
182/// Ime::Preedit("啊b", Some((3, 3)))
183/// // Press space key
184/// Ime::Preedit("", None) // Synthetic event generated to clear preedit.
185/// Ime::Commit("啊不")
186/// ```
187#[derive(Debug, Clone, PartialEq, Eq, Hash)]
188pub enum Event {
189    /// Notifies when the IME was opened.
190    ///
191    /// After getting this event you could receive [`Preedit`][Self::Preedit] and
192    /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
193    /// like [`Shell::request_input_method`].
194    ///
195    /// [`Shell::request_input_method`]: crate::Shell::request_input_method
196    Opened,
197
198    /// Notifies when a new composing text should be set at the cursor position.
199    ///
200    /// The value represents a pair of the preedit string and the cursor begin position and end
201    /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
202    /// this indicates that preedit was cleared.
203    ///
204    /// The cursor range is byte-wise indexed.
205    Preedit(String, Option<Range<usize>>),
206
207    /// Notifies when text should be inserted into the editor widget.
208    ///
209    /// Right before this event, an empty [`Self::Preedit`] event will be issued.
210    Commit(String),
211
212    /// Notifies when the IME was disabled.
213    ///
214    /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
215    /// [`Commit`][Self::Commit] events until the next [`Opened`][Self::Opened] event. You should
216    /// also stop issuing IME related requests like [`Shell::request_input_method`] and clear
217    /// pending preedit text.
218    ///
219    /// [`Shell::request_input_method`]: crate::Shell::request_input_method
220    Closed,
221}