diff options
Diffstat (limited to 'winit/src')
| -rw-r--r-- | winit/src/conversion.rs | 24 | ||||
| -rw-r--r-- | winit/src/program.rs | 42 | ||||
| -rw-r--r-- | winit/src/program/window_manager.rs | 229 | 
3 files changed, 266 insertions, 29 deletions
| diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 462be65b..ab84afff 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; @@ -140,6 +141,7 @@ pub fn window_event(      scale_factor: f64,      modifiers: winit::keyboard::ModifiersState,  ) -> Option<Event> { +    use winit::event::Ime;      use winit::event::WindowEvent;      match event { @@ -283,6 +285,15 @@ pub fn window_event(                  self::modifiers(new_modifiers.state()),              )))          } +        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 { @@ -1160,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<winit::window::Icon> { @@ -1169,6 +1180,17 @@ pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {      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 d8436212..7ead4c3b 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -873,20 +873,16 @@ async fn run_instance<P, C>(                          });                          if let user_interface::State::Updated { -                            redraw_request: Some(redraw_request), +                            redraw_request, +                            input_method,                          } = ui_state                          { -                            match redraw_request { -                                window::RedrawRequest::NextFrame => { -                                    window.raw.request_redraw(); -                                    window.redraw_at = None; -                                } -                                window::RedrawRequest::At(at) => { -                                    window.redraw_at = Some(at); -                                } -                            } +                            window.request_redraw(redraw_request); +                            window.request_input_method(input_method);                          } +                        window.draw_preedit(); +                          debug.render_started();                          match compositor.present(                              &mut window.renderer, @@ -1029,27 +1025,23 @@ async fn run_instance<P, C>(                                  );                              #[cfg(feature = "unconditional-rendering")] -                            window.raw.request_redraw(); +                            window.request_redraw( +                                window::RedrawRequest::NextFrame, +                            );                              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; -                                    } -                                    window::RedrawRequest::At(at) => { -                                        window.redraw_at = Some(at); -                                    } -                                }, +                                    redraw_request: _redraw_request, +                                    .. +                                } => { +                                    #[cfg(not( +                                        feature = "unconditional-rendering" +                                    ))] +                                    window.request_redraw(_redraw_request); +                                }                                  user_interface::State::Outdated => {                                      uis_stale = true;                                  } -                                user_interface::State::Updated { .. } => {}                              }                              for (event, status) in window_events diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index a3c991df..ae214e7c 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -1,14 +1,23 @@ +use crate::conversion; +use crate::core::alignment; +use crate::core::input_method;  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 winit::dpi::{LogicalPosition, LogicalSize}; +use winit::monitor::MonitorHandle; +  use std::collections::BTreeMap;  use std::sync::Arc; -use winit::monitor::MonitorHandle;  #[allow(missing_debug_implementations)]  pub struct WindowManager<P, C> @@ -65,6 +74,7 @@ where                  renderer,                  mouse_interaction: mouse::Interaction::None,                  redraw_at: None, +                preedit: None,              },          ); @@ -155,6 +165,7 @@ where      pub surface: C::Surface,      pub renderer: P::Renderer,      pub redraw_at: Option<Instant>, +    preedit: Option<Preedit<P::Renderer>>,  }  impl<P, C> Window<P, C> @@ -179,4 +190,216 @@ 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) { +        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, +            purpose, +            preedit, +        } = input_method +        { +            self.raw.set_ime_cursor_area( +                LogicalPosition::new(position.x, position.y), +                LogicalSize::new(10, 10), // TODO? +            ); + +            self.raw.set_ime_purpose(conversion::ime_purpose(purpose)); + +            if let Some(preedit) = preedit { +                if preedit.content.is_empty() { +                    self.preedit = None; +                } else if let Some(overlay) = &mut self.preedit { +                    overlay.update( +                        position, +                        &preedit, +                        self.state.background_color(), +                        &self.renderer, +                    ); +                } else { +                    let mut overlay = Preedit::new(); +                    overlay.update( +                        position, +                        &preedit, +                        self.state.background_color(), +                        &self.renderer, +                    ); + +                    self.preedit = Some(overlay); +                } +            } +        } 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(), +                &Rectangle::new( +                    Point::ORIGIN, +                    self.state.viewport().logical_size(), +                ), +            ); +        } +    } +} + +struct Preedit<Renderer> +where +    Renderer: text::Renderer, +{ +    position: Point, +    content: Renderer::Paragraph, +    spans: Vec<text::Span<'static, (), Renderer::Font>>, +} + +impl<Renderer> Preedit<Renderer> +where +    Renderer: text::Renderer, +{ +    fn new() -> Self { +        Self { +            position: Point::ORIGIN, +            spans: Vec::new(), +            content: Renderer::Paragraph::default(), +        } +    } + +    fn update( +        &mut self, +        position: Point, +        preedit: &input_method::Preedit, +        background: Color, +        renderer: &Renderer, +    ) { +        self.position = position; + +        let spans = match &preedit.selection { +            Some(selection) => { +                vec![ +                    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::new(&preedit.content)], +        }; + +        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( +        &self, +        renderer: &mut Renderer, +        color: Color, +        background: Color, +        viewport: &Rectangle, +    ) { +        use text::Paragraph as _; + +        if self.content.min_width() < 1.0 { +            return; +        } + +        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); + +        bounds.y = bounds +            .y +            .max(viewport.y) +            .min(viewport.y + viewport.height - bounds.height); + +        renderer.with_layer(bounds, |renderer| { +            renderer.fill_quad( +                renderer::Quad { +                    bounds, +                    ..Default::default() +                }, +                background, +            ); + +            renderer.fill_paragraph( +                &self.content, +                bounds.position(), +                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, +            ); + +            for span_bounds in self.content.span_bounds(1) { +                renderer.fill_quad( +                    renderer::Quad { +                        bounds: span_bounds +                            + (bounds.position() - Point::ORIGIN), +                        ..Default::default() +                    }, +                    color, +                ); +            } +        }); +    }  } | 
