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 --- winit/src/conversion.rs | 11 +++ winit/src/program.rs | 162 ++++++++++++++++++++++++++++++++++++++++----- winit/src/program/state.rs | 14 +++- 3 files changed, 171 insertions(+), 16 deletions(-) (limited to 'winit/src') diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 462be65b..a289f060 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -2,6 +2,7 @@ //! //! [`winit`]: https://github.com/rust-windowing/winit //! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.13/runtime +use crate::core::input_method; use crate::core::keyboard; use crate::core::mouse; use crate::core::touch; @@ -283,6 +284,16 @@ pub fn window_event( self::modifiers(new_modifiers.state()), ))) } + WindowEvent::Ime(ime) => { + use winit::event::Ime; + println!("ime event: {:?}", ime); + Some(Event::InputMethod(match ime { + Ime::Enabled => input_method::Event::Enabled, + Ime::Preedit(s, size) => input_method::Event::Preedit(s, size), + Ime::Commit(s) => input_method::Event::Commit(s), + Ime::Disabled => input_method::Event::Disabled, + })) + } WindowEvent::Focused(focused) => Some(Event::Window(if focused { window::Event::Focused } else { diff --git a/winit/src/program.rs b/winit/src/program.rs index d8436212..688d6731 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -3,6 +3,8 @@ mod state; mod window_manager; pub use state::State; +use winit::dpi::LogicalPosition; +use winit::dpi::LogicalSize; use crate::conversion; use crate::core; @@ -579,6 +581,8 @@ async fn run_instance( let mut clipboard = Clipboard::unconnected(); let mut compositor_receiver: Option> = None; + let mut preedit = Preedit::

::new(); + debug.startup_finished(); loop { @@ -873,17 +877,27 @@ async fn run_instance( }); if let user_interface::State::Updated { - redraw_request: Some(redraw_request), + redraw_request, + caret_info, } = ui_state { match redraw_request { - window::RedrawRequest::NextFrame => { + Some(window::RedrawRequest::NextFrame) => { window.raw.request_redraw(); window.redraw_at = None; } - window::RedrawRequest::At(at) => { + Some(window::RedrawRequest::At(at)) => { window.redraw_at = Some(at); } + None => {} + } + + if let Some(caret_info) = caret_info { + update_input_method( + window, + &mut preedit, + &caret_info, + ); } } @@ -1032,24 +1046,37 @@ async fn run_instance( window.raw.request_redraw(); match ui_state { - #[cfg(not( - feature = "unconditional-rendering" - ))] user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => { - window.raw.request_redraw(); - window.redraw_at = None; + redraw_request: _redraw_request, + caret_info, + } => { + #[cfg(not( + feature = "unconditional-rendering" + ))] + match _redraw_request { + Some( + window::RedrawRequest::NextFrame, + ) => { + window.raw.request_redraw(); + window.redraw_at = None; + } + Some(window::RedrawRequest::At(at)) => { + window.redraw_at = Some(at); + } + None => {} } - window::RedrawRequest::At(at) => { - window.redraw_at = Some(at); + + if let Some(caret_info) = caret_info { + update_input_method( + window, + &mut preedit, + &caret_info, + ); } - }, + } user_interface::State::Outdated => { uis_stale = true; } - user_interface::State::Updated { .. } => {} } for (event, status) in window_events @@ -1138,6 +1165,111 @@ async fn run_instance( let _ = ManuallyDrop::into_inner(user_interfaces); } +fn update_input_method( + window: &mut crate::program::window_manager::Window, + preedit: &mut Preedit

, + caret_info: &crate::core::CaretInfo, +) where + P: Program, + C: Compositor + 'static, +{ + window.raw.set_ime_allowed(caret_info.input_method_allowed); + window.raw.set_ime_cursor_area( + LogicalPosition::new(caret_info.position.x, caret_info.position.y), + LogicalSize::new(10, 10), + ); + + let text = window.state.preedit(); + if !text.is_empty() { + preedit.update(text.as_str(), &window.renderer); + preedit.fill( + &mut window.renderer, + window.state.text_color(), + window.state.background_color(), + caret_info.position, + ); + } +} + +struct Preedit { + content: Option<::Paragraph>, +} + +impl Preedit

{ + fn new() -> Self { + Self { content: None } + } + + fn update(&mut self, text: &str, renderer: &P::Renderer) { + use core::text::Paragraph as _; + use core::text::Renderer as _; + + self.content = Some( + ::Paragraph::with_text( + core::Text::<&str, ::Font> { + content: text, + bounds: Size::INFINITY, + size: renderer.default_size(), + line_height: core::text::LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: core::alignment::Horizontal::Left, + vertical_alignment: core::alignment::Vertical::Top, //Bottom, + shaping: core::text::Shaping::Advanced, + wrapping: core::text::Wrapping::None, + }, + ), + ); + } + + fn fill( + &self, + renderer: &mut P::Renderer, + fore_color: core::Color, + bg_color: core::Color, + caret_position: Point, + ) { + use core::text::Paragraph as _; + use core::text::Renderer as _; + use core::Renderer as _; + + let Some(ref content) = self.content else { + return; + }; + if content.min_width() < 1.0 { + return; + } + + let top_left = Point::new( + caret_position.x, + caret_position.y - content.min_height(), + ); + let bounds = core::Rectangle::new(top_left, content.min_bounds()); + renderer.with_layer(bounds, |renderer| { + renderer.fill_quad( + core::renderer::Quad { + bounds, + ..Default::default() + }, + core::Background::Color(bg_color), + ); + + let underline = 2.; + renderer.fill_quad( + core::renderer::Quad { + bounds: bounds.shrink(core::Padding { + top: bounds.height - underline, + ..Default::default() + }), + ..Default::default() + }, + core::Background::Color(fore_color), + ); + + renderer.fill_paragraph(content, top_left, fore_color, bounds); + }); + } +} + /// Builds a window's [`UserInterface`] for the [`Program`]. fn build_user_interface<'a, P: Program>( program: &'a P, diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs index e883d04a..2b5710ac 100644 --- a/winit/src/program/state.rs +++ b/winit/src/program/state.rs @@ -4,7 +4,7 @@ use crate::core::{Color, Size}; use crate::graphics::Viewport; use crate::program::Program; -use winit::event::{Touch, WindowEvent}; +use winit::event::{Ime, Touch, WindowEvent}; use winit::window::Window; use std::fmt::{Debug, Formatter}; @@ -22,6 +22,7 @@ where modifiers: winit::keyboard::ModifiersState, theme: P::Theme, style: theme::Style, + preedit: String, } impl Debug for State

@@ -73,6 +74,7 @@ where modifiers: winit::keyboard::ModifiersState::default(), theme, style, + preedit: String::default(), } } @@ -136,6 +138,11 @@ where self.style.text_color } + /// TODO + pub fn preedit(&self) -> String { + self.preedit.clone() + } + /// Processes the provided window event and updates the [`State`] accordingly. pub fn update( &mut self, @@ -179,6 +186,11 @@ where WindowEvent::ModifiersChanged(new_modifiers) => { self.modifiers = new_modifiers.state(); } + WindowEvent::Ime(ime) => { + if let Ime::Preedit(text, _) = ime { + self.preedit = text.clone(); + } + } #[cfg(feature = "debug")] WindowEvent::KeyboardInput { event: -- cgit From 0c6d4eb23f07e0ab424dc22dd198924b8540192a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 2 Feb 2025 17:50:12 +0100 Subject: Run `cargo fmt` and fix lints --- winit/src/program/state.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs index 2b5710ac..6361d1ba 100644 --- a/winit/src/program/state.rs +++ b/winit/src/program/state.rs @@ -186,10 +186,8 @@ where WindowEvent::ModifiersChanged(new_modifiers) => { self.modifiers = new_modifiers.state(); } - WindowEvent::Ime(ime) => { - if let Ime::Preedit(text, _) = ime { - self.preedit = text.clone(); - } + WindowEvent::Ime(Ime::Preedit(text, _)) => { + self.preedit = text.clone(); } #[cfg(feature = "debug")] WindowEvent::KeyboardInput { -- 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` --- winit/src/conversion.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'winit/src') diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index a289f060..c7f9aaaf 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -289,7 +289,10 @@ pub fn window_event( println!("ime event: {:?}", ime); Some(Event::InputMethod(match ime { Ime::Enabled => input_method::Event::Enabled, - Ime::Preedit(s, size) => input_method::Event::Preedit(s, size), + Ime::Preedit(s, size) => input_method::Event::Preedit( + s, + size.map(|(start, end)| (start..end)), + ), Ime::Commit(s) => input_method::Event::Commit(s), Ime::Disabled => input_method::Event::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 --- winit/src/conversion.rs | 36 +++++---- winit/src/program.rs | 156 ++---------------------------------- winit/src/program/state.rs | 12 +-- winit/src/program/window_manager.rs | 145 ++++++++++++++++++++++++++++++++- 4 files changed, 174 insertions(+), 175 deletions(-) (limited to 'winit/src') diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index c7f9aaaf..ab84afff 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -141,6 +141,7 @@ pub fn window_event( scale_factor: f64, modifiers: winit::keyboard::ModifiersState, ) -> Option { + use winit::event::Ime; use winit::event::WindowEvent; match event { @@ -284,19 +285,15 @@ pub fn window_event( self::modifiers(new_modifiers.state()), ))) } - WindowEvent::Ime(ime) => { - use winit::event::Ime; - println!("ime event: {:?}", ime); - Some(Event::InputMethod(match ime { - Ime::Enabled => input_method::Event::Enabled, - Ime::Preedit(s, size) => input_method::Event::Preedit( - s, - size.map(|(start, end)| (start..end)), - ), - Ime::Commit(s) => input_method::Event::Commit(s), - Ime::Disabled => input_method::Event::Disabled, - })) - } + WindowEvent::Ime(event) => Some(Event::InputMethod(match event { + Ime::Enabled => input_method::Event::Opened, + Ime::Preedit(content, size) => input_method::Event::Preedit( + content, + size.map(|(start, end)| (start..end)), + ), + Ime::Commit(content) => input_method::Event::Commit(content), + Ime::Disabled => input_method::Event::Closed, + })), WindowEvent::Focused(focused) => Some(Event::Window(if focused { window::Event::Focused } else { @@ -1174,7 +1171,7 @@ pub fn resize_direction( } } -/// Converts some [`window::Icon`] into it's `winit` counterpart. +/// Converts some [`window::Icon`] into its `winit` counterpart. /// /// Returns `None` if there is an error during the conversion. pub fn icon(icon: window::Icon) -> Option { @@ -1183,6 +1180,17 @@ pub fn icon(icon: window::Icon) -> Option { winit::window::Icon::from_rgba(pixels, size.width, size.height).ok() } +/// Convertions some [`input_method::Purpose`] to its `winit` counterpart. +pub fn ime_purpose( + purpose: input_method::Purpose, +) -> winit::window::ImePurpose { + match purpose { + input_method::Purpose::Normal => winit::window::ImePurpose::Normal, + input_method::Purpose::Secure => winit::window::ImePurpose::Password, + input_method::Purpose::Terminal => winit::window::ImePurpose::Terminal, + } +} + // See: https://en.wikipedia.org/wiki/Private_Use_Areas fn is_private_use(c: char) -> bool { ('\u{E000}'..='\u{F8FF}').contains(&c) diff --git a/winit/src/program.rs b/winit/src/program.rs index 688d6731..302bc6c3 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -3,8 +3,6 @@ mod state; mod window_manager; pub use state::State; -use winit::dpi::LogicalPosition; -use winit::dpi::LogicalSize; use crate::conversion; use crate::core; @@ -581,8 +579,6 @@ async fn run_instance( let mut clipboard = Clipboard::unconnected(); let mut compositor_receiver: Option> = None; - let mut preedit = Preedit::

::new(); - debug.startup_finished(); loop { @@ -878,29 +874,15 @@ async fn run_instance( if let user_interface::State::Updated { redraw_request, - caret_info, + input_method, } = ui_state { - match redraw_request { - Some(window::RedrawRequest::NextFrame) => { - window.raw.request_redraw(); - window.redraw_at = None; - } - Some(window::RedrawRequest::At(at)) => { - window.redraw_at = Some(at); - } - None => {} - } - - if let Some(caret_info) = caret_info { - update_input_method( - window, - &mut preedit, - &caret_info, - ); - } + window.request_redraw(redraw_request); + window.request_input_method(input_method); } + window.draw_preedit(); + debug.render_started(); match compositor.present( &mut window.renderer, @@ -1048,31 +1030,14 @@ async fn run_instance( match ui_state { user_interface::State::Updated { redraw_request: _redraw_request, - caret_info, + input_method, } => { #[cfg(not( feature = "unconditional-rendering" ))] - match _redraw_request { - Some( - window::RedrawRequest::NextFrame, - ) => { - window.raw.request_redraw(); - window.redraw_at = None; - } - Some(window::RedrawRequest::At(at)) => { - window.redraw_at = Some(at); - } - None => {} - } + window.request_redraw(_redraw_request); - if let Some(caret_info) = caret_info { - update_input_method( - window, - &mut preedit, - &caret_info, - ); - } + window.request_input_method(input_method); } user_interface::State::Outdated => { uis_stale = true; @@ -1165,111 +1130,6 @@ async fn run_instance( let _ = ManuallyDrop::into_inner(user_interfaces); } -fn update_input_method( - window: &mut crate::program::window_manager::Window, - preedit: &mut Preedit

, - caret_info: &crate::core::CaretInfo, -) where - P: Program, - C: Compositor + 'static, -{ - window.raw.set_ime_allowed(caret_info.input_method_allowed); - window.raw.set_ime_cursor_area( - LogicalPosition::new(caret_info.position.x, caret_info.position.y), - LogicalSize::new(10, 10), - ); - - let text = window.state.preedit(); - if !text.is_empty() { - preedit.update(text.as_str(), &window.renderer); - preedit.fill( - &mut window.renderer, - window.state.text_color(), - window.state.background_color(), - caret_info.position, - ); - } -} - -struct Preedit { - content: Option<::Paragraph>, -} - -impl Preedit

{ - fn new() -> Self { - Self { content: None } - } - - fn update(&mut self, text: &str, renderer: &P::Renderer) { - use core::text::Paragraph as _; - use core::text::Renderer as _; - - self.content = Some( - ::Paragraph::with_text( - core::Text::<&str, ::Font> { - content: text, - bounds: Size::INFINITY, - size: renderer.default_size(), - line_height: core::text::LineHeight::default(), - font: renderer.default_font(), - horizontal_alignment: core::alignment::Horizontal::Left, - vertical_alignment: core::alignment::Vertical::Top, //Bottom, - shaping: core::text::Shaping::Advanced, - wrapping: core::text::Wrapping::None, - }, - ), - ); - } - - fn fill( - &self, - renderer: &mut P::Renderer, - fore_color: core::Color, - bg_color: core::Color, - caret_position: Point, - ) { - use core::text::Paragraph as _; - use core::text::Renderer as _; - use core::Renderer as _; - - let Some(ref content) = self.content else { - return; - }; - if content.min_width() < 1.0 { - return; - } - - let top_left = Point::new( - caret_position.x, - caret_position.y - content.min_height(), - ); - let bounds = core::Rectangle::new(top_left, content.min_bounds()); - renderer.with_layer(bounds, |renderer| { - renderer.fill_quad( - core::renderer::Quad { - bounds, - ..Default::default() - }, - core::Background::Color(bg_color), - ); - - let underline = 2.; - renderer.fill_quad( - core::renderer::Quad { - bounds: bounds.shrink(core::Padding { - top: bounds.height - underline, - ..Default::default() - }), - ..Default::default() - }, - core::Background::Color(fore_color), - ); - - renderer.fill_paragraph(content, top_left, fore_color, bounds); - }); - } -} - /// Builds a window's [`UserInterface`] for the [`Program`]. fn build_user_interface<'a, P: Program>( program: &'a P, diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs index 6361d1ba..e883d04a 100644 --- a/winit/src/program/state.rs +++ b/winit/src/program/state.rs @@ -4,7 +4,7 @@ use crate::core::{Color, Size}; use crate::graphics::Viewport; use crate::program::Program; -use winit::event::{Ime, Touch, WindowEvent}; +use winit::event::{Touch, WindowEvent}; use winit::window::Window; use std::fmt::{Debug, Formatter}; @@ -22,7 +22,6 @@ where modifiers: winit::keyboard::ModifiersState, theme: P::Theme, style: theme::Style, - preedit: String, } impl Debug for State

@@ -74,7 +73,6 @@ where modifiers: winit::keyboard::ModifiersState::default(), theme, style, - preedit: String::default(), } } @@ -138,11 +136,6 @@ where self.style.text_color } - /// TODO - pub fn preedit(&self) -> String { - self.preedit.clone() - } - /// Processes the provided window event and updates the [`State`] accordingly. pub fn update( &mut self, @@ -186,9 +179,6 @@ where WindowEvent::ModifiersChanged(new_modifiers) => { self.modifiers = new_modifiers.state(); } - WindowEvent::Ime(Ime::Preedit(text, _)) => { - self.preedit = text.clone(); - } #[cfg(feature = "debug")] WindowEvent::KeyboardInput { event: diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index a3c991df..cd49a8b4 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -1,13 +1,20 @@ +use crate::conversion; +use crate::core::alignment; use crate::core::mouse; +use crate::core::renderer; +use crate::core::text; use crate::core::theme; use crate::core::time::Instant; -use crate::core::window::Id; -use crate::core::{Point, Size}; +use crate::core::window::{Id, RedrawRequest}; +use crate::core::{ + Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector, +}; use crate::graphics::Compositor; use crate::program::{Program, State}; use std::collections::BTreeMap; use std::sync::Arc; +use winit::dpi::{LogicalPosition, LogicalSize}; use winit::monitor::MonitorHandle; #[allow(missing_debug_implementations)] @@ -65,6 +72,7 @@ where renderer, mouse_interaction: mouse::Interaction::None, redraw_at: None, + preedit: None, }, ); @@ -155,6 +163,7 @@ where pub surface: C::Surface, pub renderer: P::Renderer, pub redraw_at: Option, + preedit: Option>, } impl Window @@ -179,4 +188,136 @@ where Size::new(size.width, size.height) } + + pub fn request_redraw(&mut self, redraw_request: RedrawRequest) { + match redraw_request { + RedrawRequest::NextFrame => { + self.raw.request_redraw(); + self.redraw_at = None; + } + RedrawRequest::At(at) => { + self.redraw_at = Some(at); + } + RedrawRequest::Wait => {} + } + } + + pub fn request_input_method(&mut self, input_method: InputMethod) { + self.raw.set_ime_allowed(match input_method { + InputMethod::Disabled => false, + InputMethod::Allowed | InputMethod::Open { .. } => true, + }); + + if let InputMethod::Open { + position, + purpose, + preedit, + } = input_method + { + self.raw.set_ime_cursor_area( + LogicalPosition::new(position.x, position.y), + LogicalSize::new(10, 10), + ); + + self.raw.set_ime_purpose(conversion::ime_purpose(purpose)); + + if let Some(content) = preedit { + if let Some(preedit) = &mut self.preedit { + preedit.update(&content, &self.renderer); + } else { + let mut preedit = Preedit::new(); + preedit.update(&content, &self.renderer); + + self.preedit = Some(preedit); + } + } + } else { + self.preedit = None; + } + } + + pub fn draw_preedit(&mut self) { + if let Some(preedit) = &self.preedit { + preedit.draw( + &mut self.renderer, + self.state.text_color(), + self.state.background_color(), + ); + } + } +} + +struct Preedit +where + Renderer: text::Renderer, +{ + position: Point, + content: text::paragraph::Plain, +} + +impl Preedit +where + Renderer: text::Renderer, +{ + fn new() -> Self { + Self { + position: Point::ORIGIN, + content: text::paragraph::Plain::default(), + } + } + + fn update(&mut self, text: &str, renderer: &Renderer) { + self.content.update(Text { + content: text, + bounds: Size::INFINITY, + size: renderer.default_size(), + line_height: text::LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, //Bottom, + shaping: text::Shaping::Advanced, + wrapping: text::Wrapping::None, + }); + } + + fn draw(&self, renderer: &mut Renderer, color: Color, background: Color) { + if self.content.min_width() < 1.0 { + return; + } + + let top_left = + self.position - Vector::new(0.0, self.content.min_height()); + + let bounds = Rectangle::new(top_left, self.content.min_bounds()); + + renderer.with_layer(bounds, |renderer| { + renderer.fill_quad( + renderer::Quad { + bounds, + ..Default::default() + }, + background, + ); + + renderer.fill_paragraph( + self.content.raw(), + top_left, + color, + bounds, + ); + + const UNDERLINE: f32 = 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: bounds.shrink(Padding { + top: bounds.height - UNDERLINE, + ..Default::default() + }), + ..Default::default() + }, + color, + ); + }); + } } -- 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` --- winit/src/program/window_manager.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index cd49a8b4..32faa059 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -203,10 +203,13 @@ where } pub fn request_input_method(&mut self, input_method: InputMethod) { - self.raw.set_ime_allowed(match input_method { - InputMethod::Disabled => false, - InputMethod::Allowed | InputMethod::Open { .. } => true, - }); + match input_method { + InputMethod::None => {} + InputMethod::Disabled => self.raw.set_ime_allowed(false), + InputMethod::Allowed | InputMethod::Open { .. } => { + self.raw.set_ime_allowed(true) + } + } if let InputMethod::Open { position, -- cgit From d28af5739bfaafa141dc8071a0c910e8693f3b3c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 00:51:57 +0100 Subject: Track pre-edits separately from focus in text inputs --- winit/src/program/window_manager.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 32faa059..08d19d16 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -205,9 +205,11 @@ where pub fn request_input_method(&mut self, input_method: InputMethod) { match input_method { InputMethod::None => {} - InputMethod::Disabled => self.raw.set_ime_allowed(false), + InputMethod::Disabled => { + self.raw.set_ime_allowed(false); + } InputMethod::Allowed | InputMethod::Open { .. } => { - self.raw.set_ime_allowed(true) + self.raw.set_ime_allowed(true); } } -- cgit From 3a35fd6249eeb324379d3a14b020ccc48ec16fb4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 3 Feb 2025 01:30:41 +0100 Subject: Clamp pre-edit inside viewport bounds --- winit/src/program/window_manager.rs | 46 ++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 11 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 08d19d16..35a8d7dc 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -221,17 +221,19 @@ where { self.raw.set_ime_cursor_area( LogicalPosition::new(position.x, position.y), - LogicalSize::new(10, 10), + LogicalSize::new(10, 10), // TODO? ); self.raw.set_ime_purpose(conversion::ime_purpose(purpose)); if let Some(content) = preedit { - if let Some(preedit) = &mut self.preedit { - preedit.update(&content, &self.renderer); + if content.is_empty() { + self.preedit = None; + } else if let Some(preedit) = &mut self.preedit { + preedit.update(position, &content, &self.renderer); } else { let mut preedit = Preedit::new(); - preedit.update(&content, &self.renderer); + preedit.update(position, &content, &self.renderer); self.preedit = Some(preedit); } @@ -247,6 +249,10 @@ where &mut self.renderer, self.state.text_color(), self.state.background_color(), + &Rectangle::new( + Point::ORIGIN, + self.state.viewport().logical_size(), + ), ); } } @@ -271,7 +277,9 @@ where } } - fn update(&mut self, text: &str, renderer: &Renderer) { + fn update(&mut self, position: Point, text: &str, renderer: &Renderer) { + self.position = position; + self.content.update(Text { content: text, bounds: Size::INFINITY, @@ -279,21 +287,37 @@ where line_height: text::LineHeight::default(), font: renderer.default_font(), horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, //Bottom, + vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, wrapping: text::Wrapping::None, }); } - fn draw(&self, renderer: &mut Renderer, color: Color, background: Color) { + fn draw( + &self, + renderer: &mut Renderer, + color: Color, + background: Color, + viewport: &Rectangle, + ) { if self.content.min_width() < 1.0 { return; } - let top_left = - self.position - Vector::new(0.0, self.content.min_height()); + let mut bounds = Rectangle::new( + self.position - Vector::new(0.0, self.content.min_height()), + self.content.min_bounds(), + ); + + bounds.x = bounds + .x + .max(viewport.x) + .min(viewport.x + viewport.width - bounds.width); - let bounds = Rectangle::new(top_left, self.content.min_bounds()); + bounds.y = bounds + .y + .max(viewport.y) + .min(viewport.y + viewport.height - bounds.height); renderer.with_layer(bounds, |renderer| { renderer.fill_quad( @@ -306,7 +330,7 @@ where renderer.fill_paragraph( self.content.raw(), - top_left, + bounds.position(), color, bounds, ); -- 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 --- winit/src/program/window_manager.rs | 120 ++++++++++++++++++++++++++++-------- 1 file changed, 95 insertions(+), 25 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 35a8d7dc..86cee973 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -1,5 +1,6 @@ use crate::conversion; use crate::core::alignment; +use crate::core::input_method; use crate::core::mouse; use crate::core::renderer; use crate::core::text; @@ -12,11 +13,13 @@ use crate::core::{ use crate::graphics::Compositor; use crate::program::{Program, State}; -use std::collections::BTreeMap; -use std::sync::Arc; use winit::dpi::{LogicalPosition, LogicalSize}; use winit::monitor::MonitorHandle; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::sync::Arc; + #[allow(missing_debug_implementations)] pub struct WindowManager where @@ -226,16 +229,26 @@ where self.raw.set_ime_purpose(conversion::ime_purpose(purpose)); - if let Some(content) = preedit { - if content.is_empty() { + if let Some(preedit) = preedit { + if preedit.content.is_empty() { self.preedit = None; - } else if let Some(preedit) = &mut self.preedit { - preedit.update(position, &content, &self.renderer); + } else if let Some(overlay) = &mut self.preedit { + overlay.update( + position, + &preedit, + self.state.background_color(), + &self.renderer, + ); } else { - let mut preedit = Preedit::new(); - preedit.update(position, &content, &self.renderer); - - self.preedit = Some(preedit); + let mut overlay = Preedit::new(); + overlay.update( + position, + &preedit, + self.state.background_color(), + &self.renderer, + ); + + self.preedit = Some(overlay); } } } else { @@ -263,7 +276,8 @@ where Renderer: text::Renderer, { position: Point, - content: text::paragraph::Plain, + content: Renderer::Paragraph, + spans: Vec>, } impl Preedit @@ -273,24 +287,67 @@ where fn new() -> Self { Self { position: Point::ORIGIN, - content: text::paragraph::Plain::default(), + spans: Vec::new(), + content: Renderer::Paragraph::default(), } } - fn update(&mut self, position: Point, text: &str, renderer: &Renderer) { + fn update( + &mut self, + position: Point, + preedit: &input_method::Preedit, + background: Color, + renderer: &Renderer, + ) { self.position = position; - self.content.update(Text { - content: text, - bounds: Size::INFINITY, - size: renderer.default_size(), - line_height: text::LineHeight::default(), - font: renderer.default_font(), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - shaping: text::Shaping::Advanced, - wrapping: text::Wrapping::None, - }); + let spans = match &preedit.selection { + Some(selection) => { + vec![ + text::Span { + text: Cow::Borrowed( + &preedit.content[..selection.start], + ), + ..text::Span::default() + }, + text::Span { + text: Cow::Borrowed( + if selection.start == selection.end { + "\u{200A}" + } else { + &preedit.content[selection.start..selection.end] + }, + ), + color: Some(background), + ..text::Span::default() + }, + text::Span { + text: Cow::Borrowed(&preedit.content[selection.end..]), + ..text::Span::default() + }, + ] + } + _ => vec![text::Span { + text: Cow::Borrowed(&preedit.content), + ..text::Span::default() + }], + }; + + if spans != self.spans.as_slice() { + use text::Paragraph as _; + + self.content = Renderer::Paragraph::with_spans(Text { + content: &spans, + bounds: Size::INFINITY, + size: renderer.default_size(), + line_height: text::LineHeight::default(), + font: renderer.default_font(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: text::Shaping::Advanced, + wrapping: text::Wrapping::None, + }); + } } fn draw( @@ -300,6 +357,8 @@ where background: Color, viewport: &Rectangle, ) { + use text::Paragraph as _; + if self.content.min_width() < 1.0 { return; } @@ -329,7 +388,7 @@ where ); renderer.fill_paragraph( - self.content.raw(), + &self.content, bounds.position(), color, bounds, @@ -347,6 +406,17 @@ where }, color, ); + + for span_bounds in self.content.span_bounds(1) { + renderer.fill_quad( + renderer::Quad { + bounds: span_bounds + + (bounds.position() - Point::ORIGIN), + ..Default::default() + }, + color, + ); + } }); } } -- 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` --- winit/src/program/window_manager.rs | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 86cee973..ae214e7c 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -16,7 +16,6 @@ use crate::program::{Program, State}; use winit::dpi::{LogicalPosition, LogicalSize}; use winit::monitor::MonitorHandle; -use std::borrow::Cow; use std::collections::BTreeMap; use std::sync::Arc; @@ -304,33 +303,17 @@ where let spans = match &preedit.selection { Some(selection) => { vec![ - text::Span { - text: Cow::Borrowed( - &preedit.content[..selection.start], - ), - ..text::Span::default() - }, - text::Span { - text: Cow::Borrowed( - if selection.start == selection.end { - "\u{200A}" - } else { - &preedit.content[selection.start..selection.end] - }, - ), - color: Some(background), - ..text::Span::default() - }, - text::Span { - text: Cow::Borrowed(&preedit.content[selection.end..]), - ..text::Span::default() - }, + text::Span::new(&preedit.content[..selection.start]), + text::Span::new(if selection.start == selection.end { + "\u{200A}" + } else { + &preedit.content[selection.start..selection.end] + }) + .color(background), + text::Span::new(&preedit.content[selection.end..]), ] } - _ => vec![text::Span { - text: Cow::Borrowed(&preedit.content), - ..text::Span::default() - }], + _ => vec![text::Span::new(&preedit.content)], }; if spans != self.spans.as_slice() { -- 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` --- winit/src/program.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'winit/src') diff --git a/winit/src/program.rs b/winit/src/program.rs index 302bc6c3..7ead4c3b 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -1025,19 +1025,19 @@ async fn run_instance( ); #[cfg(feature = "unconditional-rendering")] - window.raw.request_redraw(); + window.request_redraw( + window::RedrawRequest::NextFrame, + ); match ui_state { user_interface::State::Updated { redraw_request: _redraw_request, - input_method, + .. } => { #[cfg(not( feature = "unconditional-rendering" ))] window.request_redraw(_redraw_request); - - window.request_input_method(input_method); } user_interface::State::Outdated => { uis_stale = true; -- cgit