From 7db5256b720c3ecbe7c1cce7a1b47fd03151e03a Mon Sep 17 00:00:00 2001 From: KENZ Date: Fri, 10 Jan 2025 07:12:31 +0900 Subject: Draft `input_method` support --- core/src/event.rs | 4 ++++ core/src/input_method.rs | 24 ++++++++++++++++++++++++ core/src/lib.rs | 2 ++ core/src/shell.rs | 25 ++++++++++++++++++++++++- 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 core/src/input_method.rs (limited to 'core') diff --git a/core/src/event.rs b/core/src/event.rs index b6cf321e..323c5ffd 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -1,4 +1,5 @@ //! Handle events of a user interface. +use crate::input_method; use crate::keyboard; use crate::mouse; use crate::touch; @@ -23,6 +24,9 @@ pub enum Event { /// A touch event Touch(touch::Event), + + /// A input method event + InputMethod(input_method::Event), } /// The status of an [`Event`] after being processed. diff --git a/core/src/input_method.rs b/core/src/input_method.rs new file mode 100644 index 00000000..ceda39c3 --- /dev/null +++ b/core/src/input_method.rs @@ -0,0 +1,24 @@ +//! Listen to input method events. + +/// A input method event. +/// +/// _**Note:** This type is largely incomplete! If you need to track +/// additional events, feel free to [open an issue] and share your use case!_ +/// +/// [open an issue]: https://github.com/iced-rs/iced/issues +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Event { + // These events correspond to underlying winit ime events. + // https://docs.rs/winit/latest/winit/event/enum.Ime.html + /// the IME was enabled. + Enabled, + + /// new composing text should be set at the cursor position. + Preedit(String, Option<(usize, usize)>), + + /// text should be inserted into the editor widget. + Commit(String), + + /// the IME was disabled. + Disabled, +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 16b3aa0f..c7c38044 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -17,6 +17,7 @@ pub mod event; pub mod font; pub mod gradient; pub mod image; +pub mod input_method; pub mod keyboard; pub mod layout; pub mod mouse; @@ -72,6 +73,7 @@ pub use renderer::Renderer; pub use rotation::Rotation; pub use settings::Settings; pub use shadow::Shadow; +pub use shell::CaretInfo; pub use shell::Shell; pub use size::Size; pub use svg::Svg; diff --git a/core/src/shell.rs b/core/src/shell.rs index 12ebbaa8..d2c1b9ec 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -1,6 +1,15 @@ -use crate::event; use crate::time::Instant; use crate::window; +use crate::{event, Point}; + +/// TODO +#[derive(Clone, Copy, Debug)] +pub struct CaretInfo { + /// TODO + pub position: Point, + /// TODO + pub input_method_allowed: bool, +} /// A connection to the state of a shell. /// @@ -15,6 +24,7 @@ pub struct Shell<'a, Message> { redraw_request: Option, is_layout_invalid: bool, are_widgets_invalid: bool, + caret_info: Option, } impl<'a, Message> Shell<'a, Message> { @@ -26,6 +36,7 @@ impl<'a, Message> Shell<'a, Message> { redraw_request: None, is_layout_invalid: false, are_widgets_invalid: false, + caret_info: None, } } @@ -80,6 +91,16 @@ impl<'a, Message> Shell<'a, Message> { self.redraw_request } + /// TODO + pub fn update_caret_info(&mut self, caret_info: Option) { + self.caret_info = caret_info.or(self.caret_info); + } + + /// TODO + pub fn caret_info(&self) -> Option { + self.caret_info + } + /// Returns whether the current layout is invalid or not. pub fn is_layout_invalid(&self) -> bool { self.is_layout_invalid @@ -130,6 +151,8 @@ impl<'a, Message> Shell<'a, Message> { ); } + self.update_caret_info(other.caret_info()); + self.is_layout_invalid = self.is_layout_invalid || other.is_layout_invalid; -- cgit From d5ee9c27955e6dfeb645e2641f3d24b006685484 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 2 Feb 2025 17:55:16 +0100 Subject: Copy `winit` docs for `input_method::Event` --- core/src/input_method.rs | 76 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 12 deletions(-) (limited to 'core') diff --git a/core/src/input_method.rs b/core/src/input_method.rs index ceda39c3..d282d404 100644 --- a/core/src/input_method.rs +++ b/core/src/input_method.rs @@ -1,24 +1,76 @@ //! Listen to input method events. +use std::ops::Range; -/// A input method event. +/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events. /// -/// _**Note:** This type is largely incomplete! If you need to track -/// additional events, feel free to [open an issue] and share your use case!_ +/// This is also called a "composition event". /// -/// [open an issue]: https://github.com/iced-rs/iced/issues -#[derive(Debug, Clone, PartialEq, Eq)] +/// Most keypresses using a latin-like keyboard layout simply generate a +/// [`WindowEvent::KeyboardInput`]. However, one couldn't possibly have a key for every single +/// unicode character that the user might want to type +/// - so 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 by winit 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 +/// [`Window::set_ime_cursor_area`].) +/// +/// 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 by winit to clear preedit. +/// Ime::Commit("啊不") +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Event { - // These events correspond to underlying winit ime events. - // https://docs.rs/winit/latest/winit/event/enum.Ime.html - /// the IME was enabled. + /// Notifies when the IME was enabled. + /// + /// After getting this event you could receive [`Preedit`][Self::Preedit] and + /// [`Commit`][Self::Commit] events. You should also start performing IME related requests + /// like [`Window::set_ime_cursor_area`]. Enabled, - /// new composing text should be set at the cursor position. - Preedit(String, Option<(usize, usize)>), + /// 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>), - /// text should be inserted into the editor widget. + /// Notifies when text should be inserted into the editor widget. + /// + /// Right before this event winit will send empty [`Self::Preedit`] event. Commit(String), - /// the IME was disabled. + /// 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 [`Enabled`][Self::Enabled] event. You should + /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear + /// pending preedit text. Disabled, } -- cgit From ae10adda74320e8098bfeb401f12a278e1e7b3e2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 2 Feb 2025 20:45:29 +0100 Subject: Refactor and simplify `input_method` API --- core/src/input_method.rs | 129 +++++++++++++++++++++++++++++++++----- core/src/lib.rs | 2 +- core/src/shell.rs | 78 ++++++++++------------- core/src/text/paragraph.rs | 6 ++ core/src/window/redraw_request.rs | 12 ++++ 5 files changed, 165 insertions(+), 62 deletions(-) (limited to 'core') diff --git a/core/src/input_method.rs b/core/src/input_method.rs index d282d404..c293582d 100644 --- a/core/src/input_method.rs +++ b/core/src/input_method.rs @@ -1,17 +1,112 @@ //! Listen to input method events. +use crate::Point; + use std::ops::Range; +/// The input method strategy of a widget. +#[derive(Debug, Clone, PartialEq)] +pub enum InputMethod { + /// No input method is allowed. + Disabled, + /// Input methods are allowed, but not open yet. + Allowed, + /// Input method is open. + Open { + /// 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 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 second one when both ready: + /// ``` + /// # use iced_core::input_method::{InputMethod, Purpose}; + /// # use iced_core::Point; + /// + /// let open = InputMethod::Open { + /// position: Point::ORIGIN, + /// purpose: Purpose::Normal, + /// preedit: None, + /// }; + /// + /// let open_2 = InputMethod::Open { + /// position: Point::ORIGIN, + /// purpose: Purpose::Secure, + /// preedit: None, + /// }; + /// + /// let mut ime = InputMethod::Disabled; + /// + /// ime.merge(&InputMethod::::Allowed); + /// assert_eq!(ime, InputMethod::Allowed); + /// + /// ime.merge(&InputMethod::::Disabled); + /// assert_eq!(ime, InputMethod::Allowed); + /// + /// ime.merge(&open); + /// assert_eq!(ime, open); + /// + /// ime.merge(&open_2); + /// assert_eq!(ime, open_2); + /// ``` + pub fn merge>(&mut self, other: &InputMethod) { + match other { + InputMethod::Disabled => {} + InputMethod::Open { + position, + purpose, + preedit, + } => { + *self = Self::Open { + position: *position, + purpose: *purpose, + preedit: preedit + .as_ref() + .map(AsRef::as_ref) + .map(str::to_owned), + }; + } + InputMethod::Allowed if matches!(self, Self::Disabled) => { + *self = Self::Allowed; + } + InputMethod::Allowed => {} + } + } +} + /// 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 -/// [`WindowEvent::KeyboardInput`]. However, one couldn't possibly have a key for every single -/// unicode character that the user might want to type -/// - so the solution operating systems employ is to allow the user to type these using _a sequence -/// of keypresses_ instead. +/// [`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 +/// 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: /// @@ -19,13 +114,13 @@ use std::ops::Range; /// // Press "`" key /// Ime::Preedit("`", Some((0, 0))) /// // Press "E" key -/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. +/// 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 -/// [`Window::set_ime_cursor_area`].) +/// [`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: @@ -40,17 +135,19 @@ use std::ops::Range; /// // Press space key /// Ime::Preedit("啊b", Some((3, 3))) /// // Press space key -/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. +/// 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 enabled. + /// 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 [`Window::set_ime_cursor_area`]. - Enabled, + /// 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. /// @@ -63,14 +160,16 @@ pub enum Event { /// Notifies when text should be inserted into the editor widget. /// - /// Right before this event winit will send empty [`Self::Preedit`] event. + /// 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 [`Enabled`][Self::Enabled] event. You should - /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear + /// [`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. - Disabled, + /// + /// [`Shell::request_input_method`]: crate::Shell::request_input_method + Closed, } diff --git a/core/src/lib.rs b/core/src/lib.rs index c7c38044..c31a8da7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -62,6 +62,7 @@ pub use event::Event; pub use font::Font; pub use gradient::Gradient; pub use image::Image; +pub use input_method::InputMethod; pub use layout::Layout; pub use length::Length; pub use overlay::Overlay; @@ -73,7 +74,6 @@ pub use renderer::Renderer; pub use rotation::Rotation; pub use settings::Settings; pub use shadow::Shadow; -pub use shell::CaretInfo; pub use shell::Shell; pub use size::Size; pub use svg::Svg; diff --git a/core/src/shell.rs b/core/src/shell.rs index d2c1b9ec..e87d1696 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -1,15 +1,6 @@ -use crate::time::Instant; +use crate::event; use crate::window; -use crate::{event, Point}; - -/// TODO -#[derive(Clone, Copy, Debug)] -pub struct CaretInfo { - /// TODO - pub position: Point, - /// TODO - pub input_method_allowed: bool, -} +use crate::InputMethod; /// A connection to the state of a shell. /// @@ -21,10 +12,10 @@ pub struct CaretInfo { pub struct Shell<'a, Message> { messages: &'a mut Vec, event_status: event::Status, - redraw_request: Option, + redraw_request: window::RedrawRequest, + input_method: InputMethod, is_layout_invalid: bool, are_widgets_invalid: bool, - caret_info: Option, } impl<'a, Message> Shell<'a, Message> { @@ -33,10 +24,10 @@ impl<'a, Message> Shell<'a, Message> { Self { messages, event_status: event::Status::Ignored, - redraw_request: None, + redraw_request: window::RedrawRequest::Wait, is_layout_invalid: false, are_widgets_invalid: false, - caret_info: None, + input_method: InputMethod::Disabled, } } @@ -70,35 +61,38 @@ impl<'a, Message> Shell<'a, Message> { /// Requests a new frame to be drawn as soon as possible. pub fn request_redraw(&mut self) { - self.redraw_request = Some(window::RedrawRequest::NextFrame); - } - - /// Requests a new frame to be drawn at the given [`Instant`]. - pub fn request_redraw_at(&mut self, at: Instant) { - match self.redraw_request { - None => { - self.redraw_request = Some(window::RedrawRequest::At(at)); - } - Some(window::RedrawRequest::At(current)) if at < current => { - self.redraw_request = Some(window::RedrawRequest::At(at)); - } - _ => {} - } + self.redraw_request = window::RedrawRequest::NextFrame; + } + + /// Requests a new frame to be drawn at the given [`window::RedrawRequest`]. + pub fn request_redraw_at( + &mut self, + redraw_request: impl Into, + ) { + self.redraw_request = self.redraw_request.min(redraw_request.into()); } /// Returns the request a redraw should happen, if any. - pub fn redraw_request(&self) -> Option { + pub fn redraw_request(&self) -> window::RedrawRequest { self.redraw_request } - /// TODO - pub fn update_caret_info(&mut self, caret_info: Option) { - self.caret_info = caret_info.or(self.caret_info); + /// Requests the current [`InputMethod`] strategy. + pub fn request_input_method>( + &mut self, + ime: &InputMethod, + ) { + self.input_method.merge(ime); } - /// TODO - pub fn caret_info(&self) -> Option { - self.caret_info + /// Returns the current [`InputMethod`] strategy. + pub fn input_method(&self) -> &InputMethod { + &self.input_method + } + + /// Returns the current [`InputMethod`] strategy. + pub fn input_method_mut(&mut self) -> &mut InputMethod { + &mut self.input_method } /// Returns whether the current layout is invalid or not. @@ -143,22 +137,14 @@ impl<'a, Message> Shell<'a, Message> { pub fn merge(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) { self.messages.extend(other.messages.drain(..).map(f)); - if let Some(new) = other.redraw_request { - self.redraw_request = Some( - self.redraw_request - .map(|current| if current < new { current } else { new }) - .unwrap_or(new), - ); - } - - self.update_caret_info(other.caret_info()); - self.is_layout_invalid = self.is_layout_invalid || other.is_layout_invalid; self.are_widgets_invalid = self.are_widgets_invalid || other.are_widgets_invalid; + self.redraw_request = self.redraw_request.min(other.redraw_request); self.event_status = self.event_status.merge(other.event_status); + self.input_method.merge(&other.input_method); } } diff --git a/core/src/text/paragraph.rs b/core/src/text/paragraph.rs index 924276c3..700c2c75 100644 --- a/core/src/text/paragraph.rs +++ b/core/src/text/paragraph.rs @@ -129,6 +129,12 @@ impl Plain

{ self.raw.min_width() } + /// Returns the minimum height that can fit the contents of the + /// [`Paragraph`]. + pub fn min_height(&self) -> f32 { + self.raw.min_height() + } + /// Returns the cached [`Paragraph`]. pub fn raw(&self) -> &P { &self.raw diff --git a/core/src/window/redraw_request.rs b/core/src/window/redraw_request.rs index b0c000d6..0ae4face 100644 --- a/core/src/window/redraw_request.rs +++ b/core/src/window/redraw_request.rs @@ -8,6 +8,15 @@ pub enum RedrawRequest { /// Redraw at the given time. At(Instant), + + /// No redraw is needed. + Wait, +} + +impl From for RedrawRequest { + fn from(time: Instant) -> Self { + Self::At(time) + } } #[cfg(test)] @@ -34,5 +43,8 @@ mod tests { assert!(RedrawRequest::At(now) <= RedrawRequest::At(now)); assert!(RedrawRequest::At(now) <= RedrawRequest::At(later)); assert!(RedrawRequest::At(later) >= RedrawRequest::At(now)); + + assert!(RedrawRequest::Wait > RedrawRequest::NextFrame); + assert!(RedrawRequest::Wait > RedrawRequest::At(later)); } } -- cgit From db990b77e4aa8d001c774703301342c951d6caaa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 2 Feb 2025 21:06:50 +0100 Subject: Add neutral `None` variant to `InputMethod` --- core/src/input_method.rs | 13 ++++++++++--- core/src/shell.rs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'core') diff --git a/core/src/input_method.rs b/core/src/input_method.rs index c293582d..b25f29aa 100644 --- a/core/src/input_method.rs +++ b/core/src/input_method.rs @@ -6,6 +6,8 @@ use std::ops::Range; /// The input method strategy of a widget. #[derive(Debug, Clone, PartialEq)] pub enum InputMethod { + /// No input method strategy has been specified. + None, /// No input method is allowed. Disabled, /// Input methods are allowed, but not open yet. @@ -73,7 +75,7 @@ impl InputMethod { /// ``` pub fn merge>(&mut self, other: &InputMethod) { match other { - InputMethod::Disabled => {} + InputMethod::None => {} InputMethod::Open { position, purpose, @@ -88,10 +90,15 @@ impl InputMethod { .map(str::to_owned), }; } - InputMethod::Allowed if matches!(self, Self::Disabled) => { + InputMethod::Allowed + if matches!(self, Self::None | Self::Disabled) => + { *self = Self::Allowed; } - InputMethod::Allowed => {} + InputMethod::Disabled if matches!(self, Self::None) => { + *self = Self::Disabled; + } + _ => {} } } } diff --git a/core/src/shell.rs b/core/src/shell.rs index e87d1696..d01233c7 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -27,7 +27,7 @@ impl<'a, Message> Shell<'a, Message> { redraw_request: window::RedrawRequest::Wait, is_layout_invalid: false, are_widgets_invalid: false, - input_method: InputMethod::Disabled, + input_method: InputMethod::None, } } -- cgit From c83809adb907498ba2a573ec9fb50936601ac8fc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 02:33:40 +0100 Subject: Implement basic IME selection in `Preedit` overlay --- core/src/input_method.rs | 47 ++++++++++++++++++++++++++++++++++++++++++----- core/src/text.rs | 17 +++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/src/input_method.rs b/core/src/input_method.rs index b25f29aa..f10a1c3b 100644 --- a/core/src/input_method.rs +++ b/core/src/input_method.rs @@ -23,10 +23,50 @@ pub enum InputMethod { /// 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: 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>, +} + +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(), + } + } +} + +impl Preedit { + /// Borrows the contents of a [`Preedit`]. + pub fn as_ref(&self) -> Preedit<&str> { + Preedit { + content: &self.content, + selection: self.selection.clone(), + } + } +} + /// The purpose of an [`InputMethod`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Purpose { @@ -84,10 +124,7 @@ impl InputMethod { *self = Self::Open { position: *position, purpose: *purpose, - preedit: preedit - .as_ref() - .map(AsRef::as_ref) - .map(str::to_owned), + preedit: preedit.as_ref().map(Preedit::to_owned), }; } InputMethod::Allowed diff --git a/core/src/text.rs b/core/src/text.rs index c144fd24..8dde9e21 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -270,6 +270,23 @@ pub struct Span<'a, Link = (), Font = crate::Font> { pub strikethrough: bool, } +impl Default for Span<'_, Link, Font> { + fn default() -> Self { + Self { + text: Cow::default(), + size: None, + line_height: None, + font: None, + color: None, + link: None, + highlight: None, + padding: Padding::default(), + underline: false, + strikethrough: false, + } + } +} + /// A text highlight. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Highlight { -- cgit From c9abe25d3167bb12d935e3a095160a897dd98176 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 02:38:20 +0100 Subject: Use `text::Span::new` in `window_manager` --- core/src/text.rs | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) (limited to 'core') diff --git a/core/src/text.rs b/core/src/text.rs index 8dde9e21..a7e1f281 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -270,23 +270,6 @@ pub struct Span<'a, Link = (), Font = crate::Font> { pub strikethrough: bool, } -impl Default for Span<'_, Link, Font> { - fn default() -> Self { - Self { - text: Cow::default(), - size: None, - line_height: None, - font: None, - color: None, - link: None, - highlight: None, - padding: Padding::default(), - underline: false, - strikethrough: false, - } - } -} - /// A text highlight. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Highlight { @@ -301,15 +284,7 @@ impl<'a, Link, Font> Span<'a, Link, Font> { pub fn new(fragment: impl IntoFragment<'a>) -> Self { Self { text: fragment.into_fragment(), - size: None, - line_height: None, - font: None, - color: None, - highlight: None, - link: None, - padding: Padding::ZERO, - underline: false, - strikethrough: false, + ..Self::default() } } @@ -457,6 +432,23 @@ impl<'a, Link, Font> Span<'a, Link, Font> { } } +impl Default for Span<'_, Link, Font> { + fn default() -> Self { + Self { + text: Cow::default(), + size: None, + line_height: None, + font: None, + color: None, + link: None, + highlight: None, + padding: Padding::default(), + underline: false, + strikethrough: false, + } + } +} + impl<'a, Link, Font> From<&'a str> for Span<'a, Link, Font> { fn from(value: &'a str) -> Self { Span::new(value) -- cgit From ba755c69d648cace61f23537266f2e556ee70c15 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 03:34:41 +0100 Subject: Fulfill `InputMethod` requests only during `RedrawRequested` --- core/src/shell.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'core') diff --git a/core/src/shell.rs b/core/src/shell.rs index d01233c7..a13492d5 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -78,6 +78,9 @@ impl<'a, Message> Shell<'a, Message> { } /// Requests the current [`InputMethod`] strategy. + /// + /// __Important__: This request will only be honored by the + /// [`Shell`] only during a [`window::Event::RedrawRequested`]. pub fn request_input_method>( &mut self, ime: &InputMethod, -- cgit From 76c25d2fb2a6d83c6dbb71a92c184209c1d36acc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 03:36:18 +0100 Subject: Fix typo in `core::Event` documentation --- core/src/event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/event.rs b/core/src/event.rs index 323c5ffd..7f0ab914 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -25,7 +25,7 @@ pub enum Event { /// A touch event Touch(touch::Event), - /// A input method event + /// An input method event InputMethod(input_method::Event), } -- cgit From 141290c7402a4e087ce18d60b210f4feeafcebee Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 17:12:08 +0100 Subject: Fix `InputMethod` conflicts with multiple scrollables --- core/src/input_method.rs | 64 ++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 24 deletions(-) (limited to 'core') diff --git a/core/src/input_method.rs b/core/src/input_method.rs index f10a1c3b..4e8c383b 100644 --- a/core/src/input_method.rs +++ b/core/src/input_method.rs @@ -82,21 +82,21 @@ pub enum Purpose { } impl InputMethod { - /// Merges two [`InputMethod`] strategies, prioritizing the second one when both ready: + /// Merges two [`InputMethod`] strategies, prioritizing the first one when both open: /// ``` - /// # use iced_core::input_method::{InputMethod, Purpose}; + /// # use iced_core::input_method::{InputMethod, Purpose, Preedit}; /// # use iced_core::Point; /// /// let open = InputMethod::Open { /// position: Point::ORIGIN, /// purpose: Purpose::Normal, - /// preedit: None, + /// preedit: Some(Preedit { content: "1".to_owned(), selection: None }), /// }; /// /// let open_2 = InputMethod::Open { /// position: Point::ORIGIN, /// purpose: Purpose::Secure, - /// preedit: None, + /// preedit: Some(Preedit { content: "2".to_owned(), selection: None }), /// }; /// /// let mut ime = InputMethod::Disabled; @@ -111,31 +111,47 @@ impl InputMethod { /// assert_eq!(ime, open); /// /// ime.merge(&open_2); - /// assert_eq!(ime, open_2); + /// assert_eq!(ime, open); /// ``` pub fn merge>(&mut self, other: &InputMethod) { - match other { - InputMethod::None => {} - InputMethod::Open { + match (&self, other) { + (InputMethod::Open { .. }, _) + | ( + InputMethod::Allowed, + InputMethod::None | InputMethod::Disabled, + ) + | (InputMethod::Disabled, InputMethod::None) => {} + _ => { + *self = other.to_owned(); + } + } + } + + /// Returns true if the [`InputMethod`] is open. + pub fn is_open(&self) -> bool { + matches!(self, Self::Open { .. }) + } +} + +impl InputMethod { + /// Turns an [`InputMethod`] into its owned version. + pub fn to_owned(&self) -> InputMethod + where + T: AsRef, + { + match self { + Self::None => InputMethod::None, + Self::Disabled => InputMethod::Disabled, + Self::Allowed => InputMethod::Allowed, + Self::Open { position, purpose, preedit, - } => { - *self = Self::Open { - position: *position, - purpose: *purpose, - preedit: preedit.as_ref().map(Preedit::to_owned), - }; - } - InputMethod::Allowed - if matches!(self, Self::None | Self::Disabled) => - { - *self = Self::Allowed; - } - InputMethod::Disabled if matches!(self, Self::None) => { - *self = Self::Disabled; - } - _ => {} + } => InputMethod::Open { + position: *position, + purpose: *purpose, + preedit: preedit.as_ref().map(Preedit::to_owned), + }, } } } -- cgit