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