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}