diff options
author | 2025-01-10 07:12:31 +0900 | |
---|---|---|
committer | 2025-02-02 17:44:13 +0100 | |
commit | 7db5256b720c3ecbe7c1cce7a1b47fd03151e03a (patch) | |
tree | ccf08e3f76e27d0871185b786ece83f970dddc77 /winit | |
parent | 599d8b560bec8036c5ddda62a7bf0a540bdec396 (diff) | |
download | iced-7db5256b720c3ecbe7c1cce7a1b47fd03151e03a.tar.gz iced-7db5256b720c3ecbe7c1cce7a1b47fd03151e03a.tar.bz2 iced-7db5256b720c3ecbe7c1cce7a1b47fd03151e03a.zip |
Draft `input_method` support
Diffstat (limited to 'winit')
-rw-r--r-- | winit/src/conversion.rs | 11 | ||||
-rw-r--r-- | winit/src/program.rs | 162 | ||||
-rw-r--r-- | winit/src/program/state.rs | 14 |
3 files changed, 171 insertions, 16 deletions
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<P, C>( let mut clipboard = Clipboard::unconnected(); let mut compositor_receiver: Option<oneshot::Receiver<_>> = None; + let mut preedit = Preedit::<P>::new(); + debug.startup_finished(); loop { @@ -873,17 +877,27 @@ async fn run_instance<P, C>( }); 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<P, C>( 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<P, C>( let _ = ManuallyDrop::into_inner(user_interfaces); } +fn update_input_method<P, C>( + window: &mut crate::program::window_manager::Window<P, C>, + preedit: &mut Preedit<P>, + caret_info: &crate::core::CaretInfo, +) where + P: Program, + C: Compositor<Renderer = P::Renderer> + '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<P: Program> { + content: Option<<P::Renderer as core::text::Renderer>::Paragraph>, +} + +impl<P: Program> Preedit<P> { + 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( + <P::Renderer as core::text::Renderer>::Paragraph::with_text( + core::Text::<&str, <P::Renderer as core::text::Renderer>::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<P: Program> Debug for State<P> @@ -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: |