summaryrefslogtreecommitdiffstats
path: root/winit
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2025-02-02 20:45:29 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2025-02-02 20:45:29 +0100
commitae10adda74320e8098bfeb401f12a278e1e7b3e2 (patch)
tree1827aabad023b06a6cb9dd6ec50093af969ecf0c /winit
parentd5ee9c27955e6dfeb645e2641f3d24b006685484 (diff)
downloadiced-ae10adda74320e8098bfeb401f12a278e1e7b3e2.tar.gz
iced-ae10adda74320e8098bfeb401f12a278e1e7b3e2.tar.bz2
iced-ae10adda74320e8098bfeb401f12a278e1e7b3e2.zip
Refactor and simplify `input_method` API
Diffstat (limited to 'winit')
-rw-r--r--winit/src/conversion.rs36
-rw-r--r--winit/src/program.rs156
-rw-r--r--winit/src/program/state.rs12
-rw-r--r--winit/src/program/window_manager.rs145
4 files changed, 174 insertions, 175 deletions
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<Event> {
+ 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<winit::window::Icon> {
@@ -1183,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 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<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 {
@@ -878,29 +874,15 @@ async fn run_instance<P, C>(
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<P, C>(
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<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 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<P: Program> Debug for State<P>
@@ -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<Instant>,
+ preedit: Option<Preedit<P::Renderer>>,
}
impl<P, C> Window<P, C>
@@ -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<Renderer>
+where
+ Renderer: text::Renderer,
+{
+ position: Point,
+ content: text::paragraph::Plain<Renderer::Paragraph>,
+}
+
+impl<Renderer> Preedit<Renderer>
+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,
+ );
+ });
+ }
}