1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
//! Listen to input method events.
use crate::{Pixels, Point};
use std::ops::Range;
/// The input method strategy of a widget.
#[derive(Debug, Clone, PartialEq)]
pub enum InputMethod<T = String> {
/// Input method is disabled.
Disabled,
/// Input method is enabled.
Enabled {
/// 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<Preedit<T>>,
},
}
/// The pre-edit of an [`InputMethod`].
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Preedit<T = String> {
/// The current content.
pub content: T,
/// The selected range of the content.
pub selection: Option<Range<usize>>,
/// The text size of the content.
pub text_size: Option<Pixels>,
}
impl<T> Preedit<T> {
/// Creates a new empty [`Preedit`].
pub fn new() -> Self
where
T: Default,
{
Self::default()
}
/// Turns a [`Preedit`] into its owned version.
pub fn to_owned(&self) -> Preedit
where
T: AsRef<str>,
{
Preedit {
content: self.content.as_ref().to_owned(),
selection: self.selection.clone(),
text_size: self.text_size,
}
}
}
impl Preedit {
/// Borrows the contents of a [`Preedit`].
pub fn as_ref(&self) -> Preedit<&str> {
Preedit {
content: &self.content,
selection: self.selection.clone(),
text_size: self.text_size,
}
}
}
/// 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 first one when both open:
/// ```
/// # use iced_core::input_method::{InputMethod, Purpose, Preedit};
/// # use iced_core::Point;
///
/// let open = InputMethod::Enabled {
/// position: Point::ORIGIN,
/// purpose: Purpose::Normal,
/// preedit: Some(Preedit { content: "1".to_owned(), selection: None, text_size: None }),
/// };
///
/// let open_2 = InputMethod::Enabled {
/// position: Point::ORIGIN,
/// purpose: Purpose::Secure,
/// preedit: Some(Preedit { content: "2".to_owned(), selection: None, text_size: None }),
/// };
///
/// let mut ime = InputMethod::Disabled;
///
/// ime.merge(&open);
/// assert_eq!(ime, open);
///
/// ime.merge(&open_2);
/// assert_eq!(ime, open);
/// ```
pub fn merge<T: AsRef<str>>(&mut self, other: &InputMethod<T>) {
if let InputMethod::Enabled { .. } = self {
return;
}
*self = other.to_owned();
}
/// Returns true if the [`InputMethod`] is open.
pub fn is_enabled(&self) -> bool {
matches!(self, Self::Enabled { .. })
}
}
impl<T> InputMethod<T> {
/// Turns an [`InputMethod`] into its owned version.
pub fn to_owned(&self) -> InputMethod
where
T: AsRef<str>,
{
match self {
Self::Disabled => InputMethod::Disabled,
Self::Enabled {
position,
purpose,
preedit,
} => InputMethod::Enabled {
position: *position,
purpose: *purpose,
preedit: preedit.as_ref().map(Preedit::to_owned),
},
}
}
}
/// 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
/// [`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
/// "accent key", and then the character you want to apply the accent to. In this case, some
/// platforms will generate the following event sequence:
///
/// ```ignore
/// // Press "`" key
/// Ime::Preedit("`", Some((0, 0)))
/// // Press "E" key
/// 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
/// [`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:
///
/// ```ignore
/// // Press "A" key
/// Ime::Preedit("a", Some((1, 1)))
/// // Press "B" key
/// Ime::Preedit("a b", Some((3, 3)))
/// // Press left arrow key
/// Ime::Preedit("a b", Some((1, 1)))
/// // Press space key
/// Ime::Preedit("啊b", Some((3, 3)))
/// // Press space key
/// 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 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 [`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.
///
/// The value represents a pair of the preedit string and the cursor begin position and end
/// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
/// this indicates that preedit was cleared.
///
/// The cursor range is byte-wise indexed.
Preedit(String, Option<Range<usize>>),
/// Notifies when text should be inserted into the editor widget.
///
/// 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 [`Opened`][Self::Opened] event. You should
/// also stop issuing IME related requests like [`Shell::request_input_method`] and clear
/// pending preedit text.
///
/// [`Shell::request_input_method`]: crate::Shell::request_input_method
Closed,
}
|