Skip to main content

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    /// Numeric input (integers).
85    Number,
86    /// Decimal number input (integers and fractions).
87    Decimal,
88    /// Telephone number input.
89    Phone,
90    /// Email address input.
91    Email,
92    /// URL input.
93    Url,
94    /// Search query input.
95    Search,
96}
97
98impl InputMethod {
99    /// Merges two [`InputMethod`] strategies, prioritizing the first one when both open:
100    /// ```
101    /// # use iced_core::input_method::{InputMethod, Purpose, Preedit};
102    /// # use iced_core::{Point, Rectangle, Size};
103    ///
104    /// let open = InputMethod::Enabled {
105    ///     cursor: Rectangle::new(Point::ORIGIN, Size::UNIT),
106    ///     purpose: Purpose::Normal,
107    ///     preedit: Some(Preedit { content: "1".to_owned(), selection: None, text_size: None }),
108    /// };
109    ///
110    /// let open_2 = InputMethod::Enabled {
111    ///     cursor: Rectangle::new(Point::ORIGIN, Size::UNIT),
112    ///     purpose: Purpose::Secure,
113    ///     preedit: Some(Preedit { content: "2".to_owned(), selection: None, text_size: None }),
114    /// };
115    ///
116    /// let mut ime = InputMethod::Disabled;
117    ///
118    /// ime.merge(&open);
119    /// assert_eq!(ime, open);
120    ///
121    /// ime.merge(&open_2);
122    /// assert_eq!(ime, open);
123    /// ```
124    pub fn merge<T: AsRef<str>>(&mut self, other: &InputMethod<T>) {
125        if let InputMethod::Enabled { .. } = self {
126            return;
127        }
128
129        *self = other.to_owned();
130    }
131
132    /// Returns true if the [`InputMethod`] is open.
133    pub fn is_enabled(&self) -> bool {
134        matches!(self, Self::Enabled { .. })
135    }
136}
137
138impl<T> InputMethod<T> {
139    /// Turns an [`InputMethod`] into its owned version.
140    pub fn to_owned(&self) -> InputMethod
141    where
142        T: AsRef<str>,
143    {
144        match self {
145            Self::Disabled => InputMethod::Disabled,
146            Self::Enabled {
147                cursor,
148                purpose,
149                preedit,
150            } => InputMethod::Enabled {
151                cursor: *cursor,
152                purpose: *purpose,
153                preedit: preedit.as_ref().map(Preedit::to_owned),
154            },
155        }
156    }
157}
158
159/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events.
160///
161/// This is also called a "composition event".
162///
163/// Most keypresses using a latin-like keyboard layout simply generate a
164/// [`keyboard::Event::KeyPressed`](crate::keyboard::Event::KeyPressed).
165/// However, one couldn't possibly have a key for every single
166/// unicode character that the user might want to type. The solution operating systems employ is
167/// to allow the user to type these using _a sequence of keypresses_ instead.
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
169pub enum Event {
170    /// Notifies when the IME was opened.
171    ///
172    /// After getting this event you could receive [`Preedit`][Self::Preedit] and
173    /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
174    /// like [`Shell::request_input_method`].
175    ///
176    /// [`Shell::request_input_method`]: crate::Shell::request_input_method
177    Opened,
178
179    /// Notifies when a new composing text should be set at the cursor position.
180    ///
181    /// The value represents a pair of the preedit string and the cursor begin position and end
182    /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
183    /// this indicates that preedit was cleared.
184    ///
185    /// The cursor range is byte-wise indexed.
186    Preedit(String, Option<Range<usize>>),
187
188    /// Notifies when text should be inserted into the editor widget.
189    ///
190    /// Right before this event, an empty [`Self::Preedit`] event will be issued.
191    Commit(String),
192
193    /// Notifies when the IME was disabled.
194    ///
195    /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
196    /// [`Commit`][Self::Commit] events until the next [`Opened`][Self::Opened] event. You should
197    /// also stop issuing IME related requests like [`Shell::request_input_method`] and clear
198    /// pending preedit text.
199    ///
200    /// [`Shell::request_input_method`]: crate::Shell::request_input_method
201    Closed,
202}