//! 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 { /// 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>, }, } /// The pre-edit of an [`InputMethod`]. #[derive(Debug, Clone, PartialEq, Default)] pub struct Preedit { /// The current content. pub content: T, /// The selected range of the content. pub selection: Option>, /// The text size of the content. pub text_size: Option, } impl Preedit { /// 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, { 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>(&mut self, other: &InputMethod) { 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 InputMethod { /// Turns an [`InputMethod`] into its owned version. pub fn to_owned(&self) -> InputMethod where T: AsRef, { 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>), /// 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, }