summaryrefslogtreecommitdiffstats
path: root/winit
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winit/src/conversion.rs24
-rw-r--r--winit/src/program.rs42
-rw-r--r--winit/src/program/window_manager.rs229
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,
+ );
+ }
+ });
+ }
}