diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs index f161c8a0e0..bf1c6d5915 100644 --- a/examples/integration_opengl/src/main.rs +++ b/examples/integration_opengl/src/main.rs @@ -2,6 +2,7 @@ mod controls; mod scene; use controls::Controls; +use iced_glutin::ime::IME; use scene::Scene; use glow::*; @@ -77,7 +78,7 @@ pub fn main() { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - + let ime = IME::connect(windowed_context.window()); match event { Event::WindowEvent { event, .. } => { match event { @@ -130,6 +131,7 @@ pub fn main() { text_color: Color::WHITE, }, &mut clipboard, + &ime, &mut debug, ); diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 69d46c3ec0..00be03abff 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -6,8 +6,8 @@ use scene::Scene; use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; use iced_winit::{ - conversion, futures, program, renderer, winit, Clipboard, Color, Debug, - Size, + conversion, futures, ime::IME, program, renderer, winit, Clipboard, Color, + Debug, Size, }; use winit::{ @@ -147,7 +147,8 @@ pub fn main() { event_loop.run(move |event, _, control_flow| { // You should change this if you want to render continuosly *control_flow = ControlFlow::Wait; - + let ime = IME::connect(&window); + ime.set_ime_allowed(true); match event { Event::WindowEvent { event, .. } => { match event { @@ -189,6 +190,7 @@ pub fn main() { &iced_wgpu::Theme::Dark, &renderer::Style { text_color: Color::WHITE }, &mut clipboard, + & ime, &mut debug, ); diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml index 2326ffc6e1..57597f41d0 100644 --- a/examples/todos/Cargo.toml +++ b/examples/todos/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] iced = { path = "../..", features = ["async-std", "debug"] } +iced_native = {path="../../native/"} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" lazy_static = "1.4" diff --git a/glutin/src/application.rs b/glutin/src/application.rs index d38fa220f3..da782de9fc 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -10,6 +10,7 @@ use iced_winit::application; use iced_winit::conversion; use iced_winit::futures; use iced_winit::futures::channel::mpsc; +use iced_winit::ime::IME; use iced_winit::renderer; use iced_winit::user_interface; use iced_winit::{Clipboard, Command, Debug, Proxy, Settings}; @@ -194,7 +195,7 @@ async fn run_instance( { use glutin::event; use iced_winit::futures::stream::StreamExt; - + let mut ime = IME::connect(context.window()); let mut clipboard = Clipboard::connect(context.window()); let mut cache = user_interface::Cache::default(); let mut state = application::State::new(&application, context.window()); @@ -208,6 +209,7 @@ async fn run_instance( init_command, &mut runtime, &mut clipboard, + &ime, &mut proxy, &mut debug, context.window(), @@ -244,6 +246,7 @@ async fn run_instance( state.cursor_position(), &mut renderer, &mut clipboard, + &ime, &mut messages, ); @@ -270,6 +273,7 @@ async fn run_instance( &mut renderer, &mut runtime, &mut clipboard, + &ime, &mut proxy, &mut debug, &mut messages, @@ -339,6 +343,7 @@ async fn run_instance( context = context .make_current() .expect("Make OpenGL context current"); + ime = IME::connect(context.window()); } } diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index b4afd99862..fff939fd63 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -30,9 +30,9 @@ pub use text::Text; use crate::{Backend, Primitive, Renderer}; use iced_native::layout::{self, Layout}; -use iced_native::mouse; use iced_native::renderer; use iced_native::widget::tree::{self, Tree}; +use iced_native::{mouse, IME}; use iced_native::{ Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, Widget, }; @@ -165,6 +165,7 @@ where cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { let bounds = layout.bounds(); diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 8987b9930f..83d98cfe9b 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -1,11 +1,11 @@ //! Build and reuse custom widgets using The Elm Architecture. -use iced_native::event; use iced_native::layout::{self, Layout}; use iced_native::mouse; use iced_native::overlay; use iced_native::renderer; use iced_native::widget; use iced_native::widget::tree::{self, Tree}; +use iced_native::{event, IME}; use iced_native::{ Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; @@ -178,6 +178,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { let mut local_messages = Vec::new(); @@ -191,6 +192,7 @@ where cursor_position, renderer, clipboard, + ime, &mut local_shell, ) }); @@ -455,6 +457,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> iced_native::event::Status { let mut local_messages = Vec::new(); @@ -468,6 +471,7 @@ where cursor_position, renderer, clipboard, + ime, &mut local_shell, ) }) diff --git a/lazy/src/responsive.rs b/lazy/src/responsive.rs index 0b7ae6de10..f34f179cc6 100644 --- a/lazy/src/responsive.rs +++ b/lazy/src/responsive.rs @@ -1,10 +1,10 @@ -use iced_native::event; use iced_native::layout::{self, Layout}; use iced_native::mouse; use iced_native::overlay; use iced_native::renderer; use iced_native::widget::horizontal_space; use iced_native::widget::tree::{self, Tree}; +use iced_native::{event, IME}; use iced_native::{ Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, }; @@ -150,6 +150,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { let state = tree.state.downcast_mut::(); @@ -168,6 +169,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) }, @@ -372,6 +374,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.with_overlay_mut_maybe(|overlay| { @@ -381,6 +384,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) }) diff --git a/native/src/command/action.rs b/native/src/command/action.rs index a6954f8f75..9b363c6454 100644 --- a/native/src/command/action.rs +++ b/native/src/command/action.rs @@ -1,4 +1,5 @@ use crate::clipboard; +use crate::ime; use crate::system; use crate::widget; use crate::window; @@ -19,6 +20,9 @@ pub enum Action { /// Run a clipboard action. Clipboard(clipboard::Action), + /// Run a IME releated action. + IME(ime::Action), + /// Run a window action. Window(window::Action), @@ -46,6 +50,7 @@ impl Action { match self { Self::Future(future) => Action::Future(Box::pin(future.map(f))), Self::Clipboard(action) => Action::Clipboard(action.map(f)), + Self::IME(action) => Action::IME(action), Self::Window(window) => Action::Window(window.map(f)), Self::System(system) => Action::System(system.map(f)), Self::Widget(widget) => Action::Widget(widget.map(f)), @@ -60,6 +65,9 @@ impl fmt::Debug for Action { Self::Clipboard(action) => { write!(f, "Action::Clipboard({:?})", action) } + Self::IME(action) => { + write!(f, "Action::IME({:?})", action) + } Self::Window(action) => write!(f, "Action::Window({:?})", action), Self::System(action) => write!(f, "Action::System({:?})", action), Self::Widget(_action) => write!(f, "Action::Widget"), diff --git a/native/src/element.rs b/native/src/element.rs index 074e422ef4..03d3a7bdca 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -1,10 +1,10 @@ use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget; use crate::widget::tree::{self, Tree}; +use crate::{layout, IME}; use crate::{ Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Widget, }; @@ -330,6 +330,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, B>, ) -> event::Status { let mut local_messages = Vec::new(); @@ -342,6 +343,7 @@ where cursor_position, renderer, clipboard, + ime, &mut local_shell, ); @@ -470,6 +472,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.element.widget.on_event( @@ -479,6 +482,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } diff --git a/native/src/ime.rs b/native/src/ime.rs new file mode 100644 index 0000000000..c88061e319 --- /dev/null +++ b/native/src/ime.rs @@ -0,0 +1,43 @@ +//! Access the IME. +use std::fmt; + +/// +pub trait IME { + /// + fn set_ime_allowed(&self, allowed: bool); + + /// + fn set_ime_position(&self, x: i32, y: i32); +} + +/// A null implementation of the [`IME`] trait. +#[derive(Debug, Clone, Copy)] +pub struct Null; + +impl IME for Null { + fn set_ime_allowed(&self, _allowed: bool) {} + + fn set_ime_position(&self, _x: i32, _y: i32) {} +} + +/// A IME action to be performed by some [`Command`]. +/// +/// [`Command`]: crate::Command +pub enum Action { + /// + Allow(bool), + + /// + Position(i32, i32), +} + +impl fmt::Debug for Action { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Action::Allow(_) => { + write!(f, "Action::Allow") + } + Action::Position(_, _) => write!(f, "Action::SetPosition"), + } + } +} diff --git a/native/src/lib.rs b/native/src/lib.rs index 02269265f9..4d148bcd06 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -48,6 +48,7 @@ pub mod clipboard; pub mod command; pub mod event; pub mod image; +pub mod ime; pub mod keyboard; pub mod layout; pub mod mouse; @@ -96,6 +97,7 @@ pub use debug::Debug; pub use element::Element; pub use event::Event; pub use hasher::Hasher; +pub use ime::IME; pub use layout::Layout; pub use overlay::Overlay; pub use program::Program; diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 905d3389d9..8021175bf4 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -7,11 +7,11 @@ pub use element::Element; pub use menu::Menu; use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::renderer; use crate::widget; use crate::widget::tree::{self, Tree}; +use crate::{layout, IME}; use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size}; /// An interactive component that can be displayed on top of other widgets. @@ -91,6 +91,7 @@ where _cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, _shell: &mut Shell<'_, Message>, ) -> event::Status { event::Status::Ignored diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index b919c221ee..5536d9c472 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -1,10 +1,10 @@ pub use crate::Overlay; use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::renderer; use crate::widget; +use crate::{layout, IME}; use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector}; /// A generic [`Overlay`]. @@ -63,6 +63,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.overlay.on_event( @@ -71,6 +72,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } @@ -148,6 +150,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, B>, ) -> event::Status { let mut local_messages = Vec::new(); @@ -159,6 +162,7 @@ where cursor_position, renderer, clipboard, + ime, &mut local_shell, ); diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index 0813587288..32538c579d 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -1,5 +1,4 @@ //! Build and show dropdown menus. -use crate::alignment; use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -10,6 +9,7 @@ use crate::touch; use crate::widget::container::{self, Container}; use crate::widget::scrollable::{self, Scrollable}; use crate::widget::tree::{self, Tree}; +use crate::{alignment, IME}; use crate::{ Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget, @@ -251,6 +251,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.container.on_event( @@ -260,6 +261,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } @@ -377,6 +379,7 @@ where cursor_position: Point, renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _: &dyn IME, _shell: &mut Shell<'_, Message>, ) -> event::Status { match event { diff --git a/native/src/program/state.rs b/native/src/program/state.rs index 8ae1cacbc8..80c538294a 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -1,8 +1,8 @@ -use crate::application; use crate::event::{self, Event}; use crate::mouse; use crate::renderer; use crate::user_interface::{self, UserInterface}; +use crate::{application, IME}; use crate::{Clipboard, Command, Debug, Point, Program, Size}; /// The execution state of a [`Program`]. It leverages caching, event @@ -94,6 +94,7 @@ where theme: &::Theme, style: &renderer::Style, clipboard: &mut dyn Clipboard, + ime: &dyn IME, debug: &mut Debug, ) -> (Vec, Option>) { let mut user_interface = build_user_interface( @@ -112,6 +113,7 @@ where cursor_position, renderer, clipboard, + ime, &mut messages, ); diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 344ba4d6a6..3bb7b20055 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -1,10 +1,10 @@ //! Implement your own event loop to drive a user interface. -use crate::application; use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; use crate::widget; +use crate::{application, IME}; use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; /// A set of interactive graphical elements with a specific [`Layout`]. @@ -121,7 +121,7 @@ where /// completing [the previous example](#example): /// /// ```no_run - /// use iced_native::{clipboard, Size, Point}; + /// use iced_native::{clipboard,ime, Size, Point}; /// use iced_native::user_interface::{self, UserInterface}; /// use iced_wgpu::Renderer; /// @@ -146,7 +146,7 @@ where /// let mut window_size = Size::new(1024.0, 768.0); /// let mut cursor_position = Point::default(); /// let mut clipboard = clipboard::Null; - /// + /// let ime = ime::Null; /// // Initialize our event storage /// let mut events = Vec::new(); /// let mut messages = Vec::new(); @@ -167,6 +167,7 @@ where /// cursor_position, /// &mut renderer, /// &mut clipboard, + /// &ime, /// &mut messages /// ); /// @@ -184,6 +185,7 @@ where cursor_position: Point, renderer: &mut Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, messages: &mut Vec, ) -> (State, Vec) { use std::mem::ManuallyDrop; @@ -212,6 +214,7 @@ where cursor_position, renderer, clipboard, + ime, &mut shell, ); @@ -282,6 +285,7 @@ where base_cursor, renderer, clipboard, + ime, &mut shell, ); @@ -318,6 +322,7 @@ where /// /// ```no_run /// use iced_native::clipboard; + /// use iced_native::ime; /// use iced_native::renderer; /// use iced_native::user_interface::{self, UserInterface}; /// use iced_native::{Size, Point, Theme}; @@ -344,6 +349,7 @@ where /// let mut window_size = Size::new(1024.0, 768.0); /// let mut cursor_position = Point::default(); /// let mut clipboard = clipboard::Null; + /// let ime = ime::Null; /// let mut events = Vec::new(); /// let mut messages = Vec::new(); /// @@ -363,6 +369,7 @@ where /// cursor_position, /// &mut renderer, /// &mut clipboard, + /// &ime, /// &mut messages /// ); /// diff --git a/native/src/widget.rs b/native/src/widget.rs index 8890b8e798..24f714de5f 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -89,7 +89,7 @@ use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; -use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell}; +use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell, IME}; /// A component that displays information and allows interaction. /// @@ -187,6 +187,7 @@ where _cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, _shell: &mut Shell<'_, Message>, ) -> event::Status { event::Status::Ignored diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 6c0b8f6e79..d7f3389140 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -2,13 +2,13 @@ //! //! A [`Button`] has some local [`State`]. use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::touch; use crate::widget::tree::{self, Tree}; use crate::widget::Operation; +use crate::{layout, IME}; use crate::{ Background, Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle, Shell, Vector, Widget, @@ -188,6 +188,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { if let event::Status::Captured = self.content.as_widget_mut().on_event( @@ -197,6 +198,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) { return event::Status::Captured; diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index dc3c0bd061..c52f998e40 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -1,5 +1,4 @@ //! Show toggle controls using checkboxes. -use crate::alignment; use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -7,6 +6,7 @@ use crate::renderer; use crate::text; use crate::touch; use crate::widget::{self, Row, Text, Tree}; +use crate::{alignment, IME}; use crate::{ Alignment, Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget, @@ -174,6 +174,7 @@ where cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { match event { diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index a8b0f18307..b3ba95e2d3 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -1,10 +1,10 @@ //! Distribute content vertically. use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget::{Operation, Tree}; +use crate::{layout, IME}; use crate::{ Alignment, Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget, @@ -168,6 +168,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.children @@ -182,6 +183,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) }) diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 2afad3f2d3..79fd3fece9 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -1,11 +1,11 @@ //! Decorate content and apply alignment. use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget::{Operation, Tree}; +use crate::{layout, IME}; use crate::{ Background, Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget, @@ -188,6 +188,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.content.as_widget_mut().on_event( @@ -197,6 +198,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs index b1fe596c17..6c185805a3 100644 --- a/native/src/widget/image/viewer.rs +++ b/native/src/widget/image/viewer.rs @@ -1,10 +1,10 @@ //! Zoom and pan on an image. use crate::event::{self, Event}; -use crate::image; use crate::layout; use crate::mouse; use crate::renderer; use crate::widget::tree::{self, Tree}; +use crate::{image, IME}; use crate::{ Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, @@ -147,6 +147,7 @@ where cursor_position: Point, renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, _shell: &mut Shell<'_, Message>, ) -> event::Status { let bounds = layout.bounds(); diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index d84fb7a0f9..154416930c 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -33,13 +33,13 @@ pub use title_bar::TitleBar; pub use iced_style::pane_grid::{Line, StyleSheet}; use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::touch; use crate::widget::container; use crate::widget::tree::{self, Tree}; +use crate::{layout, IME}; use crate::{ Clipboard, Color, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, @@ -272,6 +272,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { let action = tree.state.downcast_mut::(); @@ -306,6 +307,7 @@ where cursor_position, renderer, clipboard, + ime, shell, is_picked, ) diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 98ce2c4b96..0c665b4ae8 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -1,11 +1,11 @@ use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget::container; use crate::widget::pane_grid::{Draggable, TitleBar}; use crate::widget::Tree; +use crate::{layout, IME}; use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; /// The content of a [`Pane`]. @@ -191,6 +191,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, is_picked: bool, ) -> event::Status { @@ -206,6 +207,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ); @@ -224,6 +226,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) }; diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index eb85f9249d..d374f89e86 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -1,10 +1,10 @@ use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget::container; use crate::widget::Tree; +use crate::{layout, IME}; use crate::{ Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size, }; @@ -265,6 +265,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { let mut children = layout.children(); @@ -289,6 +290,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } else { @@ -303,6 +305,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } else { diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index c334804e38..826b208482 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -1,5 +1,4 @@ //! Display a dropdown list of selectable values. -use crate::alignment; use crate::event::{self, Event}; use crate::keyboard; use crate::layout; @@ -10,6 +9,7 @@ use crate::renderer; use crate::text::{self, Text}; use crate::touch; use crate::widget::tree::{self, Tree}; +use crate::{alignment, IME}; use crate::{ Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Size, Widget, @@ -157,6 +157,7 @@ where cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { update( diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index cb83f745df..6640b2bc26 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -1,5 +1,4 @@ //! Create choices using radio buttons. -use crate::alignment; use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -7,6 +6,7 @@ use crate::renderer; use crate::text; use crate::touch; use crate::widget::{self, Row, Text, Tree}; +use crate::{alignment, IME}; use crate::{ Alignment, Clipboard, Color, Element, Layout, Length, Point, Rectangle, Shell, Widget, @@ -182,6 +182,7 @@ where cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { match event { diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index eda7c2d355..89b3c08d4c 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -1,10 +1,10 @@ //! Distribute content horizontally. use crate::event::{self, Event}; use crate::layout::{self, Layout}; -use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget::{Operation, Tree}; +use crate::{mouse, IME}; use crate::{ Alignment, Clipboard, Element, Length, Padding, Point, Rectangle, Shell, Widget, @@ -155,6 +155,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.children @@ -169,6 +170,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) }) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 4ebb07a065..8ab2ba17e9 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -1,6 +1,5 @@ //! Navigate an endless amount of content with a scrollbar. use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; @@ -8,6 +7,7 @@ use crate::touch; use crate::widget; use crate::widget::operation::{self, Operation}; use crate::widget::tree::{self, Tree}; +use crate::{layout, IME}; use crate::{ Background, Clipboard, Color, Command, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, @@ -187,6 +187,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { update( @@ -208,6 +209,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) }, diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 585d9c35c9..a0d54ceae6 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -2,11 +2,11 @@ //! //! A [`Slider`] has some local [`State`]. use crate::event::{self, Event}; -use crate::layout; use crate::mouse; use crate::renderer; use crate::touch; use crate::widget::tree::{self, Tree}; +use crate::{layout, IME}; use crate::{ Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, Shell, Size, Widget, @@ -188,6 +188,7 @@ where cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { update( diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 4479095e77..a55c37a849 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -11,7 +11,6 @@ pub use value::Value; use editor::Editor; -use crate::alignment; use crate::event::{self, Event}; use crate::keyboard; use crate::layout; @@ -22,13 +21,12 @@ use crate::touch; use crate::widget; use crate::widget::operation::{self, Operation}; use crate::widget::tree::{self, Tree}; -use crate::window::Action; +use crate::{alignment, IME}; use crate::{ Clipboard, Color, Command, Element, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget, }; pub use iced_style::text_input::{Appearance, StyleSheet}; - /// A field that can be filled with text. /// /// # Example @@ -243,6 +241,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { update( @@ -251,6 +250,7 @@ where cursor_position, renderer, clipboard, + ime, shell, &mut self.value, self.size, @@ -369,6 +369,7 @@ pub fn update<'a, Message, Renderer>( cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, value: &mut Value, size: Option, @@ -739,28 +740,48 @@ where } Event::Keyboard(keyboard::Event::IMEPreedit(text)) => { let state = state(); + let cursor_offset = state.cursor.start(value); - let mut editor = Editor::new(value, &mut state.cursor); - if state.is_ime_editing { - editor.delete(); - } - state.is_ime_editing = true; + + // limit borrow life time. let mut chars_count = 0; + let message = { + let mut editor = Editor::new(value, &mut state.cursor); + if state.is_ime_editing { + editor.delete(); + } + state.is_ime_editing = true; - let action: Action<()> = Action::MoveIMECandidateWindow { - x: cursor_position.x as i32, - y: cursor_position.y as i32, + for ch in text.chars() { + editor.insert(ch); + chars_count += 1; + } + (on_change)(editor.contents()) }; - let action = crate::command::Action::Window(action); - let command = crate::command::Command::single(action); - for ch in text.chars() { - editor.insert(ch); - chars_count += 1; - } - let message = (on_change)(editor.contents()); state .cursor .select_range(cursor_offset, cursor_offset + chars_count); + // calcurate where we need to place candidate window. + let text_bounds = layout.children().next().unwrap().bounds(); + let size = size.unwrap_or_else(|| renderer.default_size()); + let position = match state.cursor.state(value) { + cursor::State::Index(position) => position, + cursor::State::Selection { start, end } => start.min(end), + }; + let position = measure_cursor_and_scroll_offset( + renderer, + text_bounds, + value, + size, + position, + font.clone(), + ); + let position = ( + (text_bounds.x + position.0) as i32, + (text_bounds.y) as i32 + size as i32, + ); + ime.set_ime_position(position.0, position.1); + shell.publish(message); return event::Status::Captured; } @@ -963,7 +984,8 @@ pub struct State { last_click: Option, cursor: Cursor, keyboard_modifiers: keyboard::Modifiers, - is_ime_editing: bool, // TODO: Add stateful horizontal scrolling offset + is_ime_editing: bool, + // TODO: Add stateful horizontal scrolling offset } impl State { diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 7893f78c79..987abd0cc0 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -6,6 +6,7 @@ use crate::mouse; use crate::renderer; use crate::text; use crate::widget::{self, Row, Text, Tree}; +use crate::IME; use crate::{ Alignment, Clipboard, Element, Event, Layout, Length, Point, Rectangle, Shell, Widget, @@ -186,6 +187,7 @@ where cursor_position: Point, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, + _ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { match event { diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 674f2ba687..0772feb893 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -8,6 +8,7 @@ use crate::widget; use crate::widget::container; use crate::widget::overlay; use crate::widget::{Text, Tree}; +use crate::IME; use crate::{ Clipboard, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget, @@ -138,6 +139,7 @@ where cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, + ime: &dyn IME, shell: &mut Shell<'_, Message>, ) -> event::Status { self.content.as_widget_mut().on_event( @@ -147,6 +149,7 @@ where cursor_position, renderer, clipboard, + ime, shell, ) } diff --git a/native/src/window/action.rs b/native/src/window/action.rs index ecdb0095fe..73338e2246 100644 --- a/native/src/window/action.rs +++ b/native/src/window/action.rs @@ -25,15 +25,6 @@ pub enum Action { SetMode(Mode), /// Fetch the current [`Mode`] of the window. FetchMode(Box T + 'static>), - /// Move IME candidate window - MoveIMECandidateWindow { - /// The new logical x location of the ime candidate window - x: i32, - /// The new logical x location of the ime candidate window - y: i32, - }, - /// Set IME allow - SetIMEAllow(bool), } impl Action { @@ -50,10 +41,6 @@ impl Action { Self::Move { x, y } => Action::Move { x, y }, Self::SetMode(mode) => Action::SetMode(mode), Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))), - Self::SetIMEAllow(allow) => Action::SetIMEAllow(allow), - Self::MoveIMECandidateWindow { x, y } => { - Action::MoveIMECandidateWindow { x, y } - } } } } @@ -71,16 +58,6 @@ impl fmt::Debug for Action { } Self::SetMode(mode) => write!(f, "Action::SetMode({:?})", mode), Self::FetchMode(_) => write!(f, "Action::FetchMode"), - Self::MoveIMECandidateWindow { x, y } => { - write!( - f, - "Action::MoveIMECandidateWindow {{ x: {}, y: {} }}", - x, y - ) - } - Self::SetIMEAllow(allow) => { - write!(f, "Action::SetIMEAllow {{ allow : {} }}", allow) - } } } } diff --git a/winit/src/application.rs b/winit/src/application.rs index be7f9bb855..aed17dd78b 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -5,6 +5,7 @@ pub use state::State; use crate::clipboard::{self, Clipboard}; use crate::conversion; +use crate::ime::IME; use crate::mouse; use crate::renderer; use crate::widget::operation; @@ -239,6 +240,7 @@ async fn run_instance( use winit::event; let mut clipboard = Clipboard::connect(&window); + let ime = IME::connect(&window); let mut cache = user_interface::Cache::default(); let mut surface = compositor.create_surface(&window); @@ -261,6 +263,7 @@ async fn run_instance( init_command, &mut runtime, &mut clipboard, + &ime, &mut proxy, &mut debug, &window, @@ -296,6 +299,7 @@ async fn run_instance( state.cursor_position(), &mut renderer, &mut clipboard, + &ime, &mut messages, ); @@ -322,6 +326,7 @@ async fn run_instance( &mut renderer, &mut runtime, &mut clipboard, + &ime, &mut proxy, &mut debug, &mut messages, @@ -534,13 +539,14 @@ where /// Updates an [`Application`] by feeding it the provided messages, spawning any /// resulting [`Command`], and tracking its [`Subscription`]. -pub fn update( +pub fn update<'a, A: Application, E: Executor>( application: &mut A, cache: &mut user_interface::Cache, state: &State, renderer: &mut A::Renderer, runtime: &mut Runtime, A::Message>, clipboard: &mut Clipboard, + ime: &IME<'a>, proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, messages: &mut Vec, @@ -564,6 +570,7 @@ pub fn update( command, runtime, clipboard, + ime, proxy, debug, window, @@ -576,7 +583,7 @@ pub fn update( } /// Runs the actions of a [`Command`]. -pub fn run_command( +pub fn run_command<'a, A, E>( application: &A, cache: &mut user_interface::Cache, state: &State, @@ -584,6 +591,7 @@ pub fn run_command( command: Command, runtime: &mut Runtime, A::Message>, clipboard: &mut Clipboard, + ime: &IME<'a>, proxy: &mut winit::event_loop::EventLoopProxy, debug: &mut Debug, window: &winit::window::Window, @@ -614,6 +622,14 @@ pub fn run_command( clipboard.write(contents); } }, + command::Action::IME(action) => match action { + iced_native::ime::Action::Allow(allow) => { + ime.set_ime_allowed(allow); + } + iced_native::ime::Action::Position(x, y) => { + ime.set_ime_position(x, y); + } + }, command::Action::Window(action) => match action { window::Action::Resize { width, height } => { window.set_inner_size(winit::dpi::LogicalSize { @@ -645,11 +661,6 @@ pub fn run_command( .send_event(tag(mode)) .expect("Send message to event loop"); } - window::Action::MoveIMECandidateWindow { x, y } => window - .set_ime_position(winit::dpi::LogicalPosition { x, y }), - window::Action::SetIMEAllow(allow) => { - window.set_ime_allowed(allow) - } }, command::Action::System(action) => match action { system::Action::QueryInformation(_tag) => { diff --git a/winit/src/ime.rs b/winit/src/ime.rs new file mode 100644 index 0000000000..6b36a2d9ad --- /dev/null +++ b/winit/src/ime.rs @@ -0,0 +1,56 @@ +//! Access to winit ime related things. +pub use iced_native::clipboard::Action; + +use crate::command::{self, Command}; + +use winit::window::Window; + +/// IME related setting interface. +/// +/// This is the wrapper of winit window reference so youd don't have to care about cost of initialize this struct. +#[derive(Debug)] +pub struct IME<'a> { + window: &'a Window, +} + +impl<'a> IME<'a> { + /// connect to winit + pub fn connect(window: &'a Window) -> Self { + Self { window } + } +} +impl<'a> IME<'a> { + /// allow input by ime or not. + pub fn set_ime_allowed(&self, allowed: bool) { + self.window.set_ime_allowed(allowed) + } + /// set the logical position of IME candidate window. + pub fn set_ime_position(&self, x: i32, y: i32) { + self.window + .set_ime_position(winit::dpi::LogicalPosition { x, y }) + } +} + +impl<'a> iced_native::ime::IME for IME<'a> { + fn set_ime_allowed(&self, allowed: bool) { + self.set_ime_allowed(allowed) + } + + fn set_ime_position(&self, x: i32, y: i32) { + self.set_ime_position(x, y) + } +} + +/// allow input by ime or not. +pub fn set_ime_allowed(allowed: bool) -> Command { + Command::single(command::Action::IME(iced_native::ime::Action::Allow( + allowed, + ))) +} + +/// set the logical position of IME candidate window. +pub fn set_position(x: i32, y: i32) -> Command { + Command::single(command::Action::IME(iced_native::ime::Action::Position( + x, y, + ))) +} diff --git a/winit/src/lib.rs b/winit/src/lib.rs index edba887b22..4993666660 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -39,6 +39,7 @@ pub use winit; pub mod application; pub mod clipboard; pub mod conversion; +pub mod ime; pub mod settings; pub mod window; diff --git a/winit/src/window.rs b/winit/src/window.rs index aaa316c00b..265139f7ec 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -30,17 +30,3 @@ pub fn fetch_mode( Box::new(f), ))) } - -/// Set IME allowed on the window -pub fn set_ime_allowed(allow: bool) -> Command { - Command::single(command::Action::Window(window::Action::SetIMEAllow(allow))) -} - -/// Set IME candidate position of the window. -/// -/// position is logical position. -pub fn set_ime_position(x: i32, y: i32) -> Command { - Command::single(command::Action::Window( - window::Action::MoveIMECandidateWindow { x, y }, - )) -}