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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
155pub enum Event {
156    /// Notifies when the IME was opened.
157    ///
158    /// After getting this event you could receive [`Preedit`][Self::Preedit] and
159    /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
160    /// like [`Shell::request_input_method`].
161    ///
162    /// [`Shell::request_input_method`]: crate::Shell::request_input_method
163    Opened,
164
165    /// Notifies when a new composing text should be set at the cursor position.
166    ///
167    /// The value represents a pair of the preedit string and the cursor begin position and end
168    /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
169    /// this indicates that preedit was cleared.
170    ///
171    /// The cursor range is byte-wise indexed.
172    Preedit(String, Option<Range<usize>>),
173
174    /// Notifies when text should be inserted into the editor widget.
175    ///
176    /// Right before this event, an empty [`Self::Preedit`] event will be issued.
177    Commit(String),
178
179    /// Notifies when the IME was disabled.
180    ///
181    /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
182    /// [`Commit`][Self::Commit] events until the next [`Opened`][Self::Opened] event. You should
183    /// also stop issuing IME related requests like [`Shell::request_input_method`] and clear
184    /// pending preedit text.
185    ///
186    /// [`Shell::request_input_method`]: crate::Shell::request_input_method
187    Closed,
188}