diff options
author | 2024-10-28 16:58:00 +0100 | |
---|---|---|
committer | 2024-11-05 23:52:58 +0100 | |
commit | 920596ed6f44acf8d87d2135c1b8967bab23d5b9 (patch) | |
tree | ca3f673c01bf9abba79f6b5f172df189788d50de /widget/src | |
parent | a84b328dcc3e2f941f9595a2f8c3b1d061442722 (diff) | |
download | iced-920596ed6f44acf8d87d2135c1b8967bab23d5b9.tar.gz iced-920596ed6f44acf8d87d2135c1b8967bab23d5b9.tar.bz2 iced-920596ed6f44acf8d87d2135c1b8967bab23d5b9.zip |
Implement `reactive-rendering` for `canvas`
Diffstat (limited to 'widget/src')
-rw-r--r-- | widget/src/action.rs | 89 | ||||
-rw-r--r-- | widget/src/canvas.rs | 63 | ||||
-rw-r--r-- | widget/src/canvas/event.rs | 21 | ||||
-rw-r--r-- | widget/src/canvas/program.rs | 15 | ||||
-rw-r--r-- | widget/src/lib.rs | 2 |
5 files changed, 142 insertions, 48 deletions
diff --git a/widget/src/action.rs b/widget/src/action.rs new file mode 100644 index 00000000..1dd3a787 --- /dev/null +++ b/widget/src/action.rs @@ -0,0 +1,89 @@ +use crate::core::event; +use crate::core::time::Instant; +use crate::core::window; + +/// A runtime action that can be performed by some widgets. +#[derive(Debug, Clone)] +pub struct Action<Message> { + message_to_publish: Option<Message>, + redraw_request: Option<window::RedrawRequest>, + event_status: event::Status, +} + +impl<Message> Action<Message> { + fn new() -> Self { + Self { + message_to_publish: None, + redraw_request: None, + event_status: event::Status::Ignored, + } + } + + /// Creates a new "capturing" [`Action`]. A capturing [`Action`] + /// will make other widgets consider it final and prevent further + /// processing. + /// + /// Prevents "event bubbling". + pub fn capture() -> Self { + Self { + event_status: event::Status::Captured, + ..Self::new() + } + } + + /// Creates a new [`Action`] that publishes the given `Message` for + /// the application to handle. + /// + /// Publishing a `Message` always produces a redraw. + pub fn publish(message: Message) -> Self { + Self { + message_to_publish: Some(message), + ..Self::new() + } + } + + /// Creates a new [`Action`] that requests a redraw to happen as + /// soon as possible; without publishing any `Message`. + pub fn request_redraw() -> Self { + Self { + redraw_request: Some(window::RedrawRequest::NextFrame), + ..Self::new() + } + } + + /// Creates a new [`Action`] that requests a redraw to happen at + /// the given [`Instant`]; without publishing any `Message`. + /// + /// This can be useful to efficiently animate content, like a + /// blinking caret on a text input. + pub fn request_redraw_at(at: Instant) -> Self { + Self { + redraw_request: Some(window::RedrawRequest::At(at)), + ..Self::new() + } + } + + /// Marks the [`Action`] as "capturing". See [`Self::capture`]. + pub fn and_capture(mut self) -> Self { + self.event_status = event::Status::Captured; + self + } + + /// Converts the [`Action`] into its internal parts. + /// + /// This method is meant to be used by runtimes, libraries, or internal + /// widget implementations. + pub fn into_inner( + self, + ) -> ( + Option<Message>, + Option<window::RedrawRequest>, + event::Status, + ) { + ( + self.message_to_publish, + self.redraw_request, + self.event_status, + ) + } +} diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 63a25064..23cc3f2b 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -48,24 +48,24 @@ //! canvas(Circle { radius: 50.0 }).into() //! } //! ``` -pub mod event; - mod program; -pub use event::Event; pub use program::Program; +pub use crate::core::event::Event; pub use crate::graphics::cache::Group; pub use crate::graphics::geometry::{ fill, gradient, path, stroke, Fill, Gradient, Image, LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, }; +pub use crate::Action; -use crate::core; +use crate::core::event; use crate::core::layout::{self, Layout}; use crate::core::mouse; use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; +use crate::core::window; use crate::core::{ Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget, }; @@ -148,6 +148,7 @@ where message_: PhantomData<Message>, theme_: PhantomData<Theme>, renderer_: PhantomData<Renderer>, + last_mouse_interaction: Option<mouse::Interaction>, } impl<P, Message, Theme, Renderer> Canvas<P, Message, Theme, Renderer> @@ -166,6 +167,7 @@ where message_: PhantomData, theme_: PhantomData, renderer_: PhantomData, + last_mouse_interaction: None, } } @@ -216,39 +218,60 @@ where fn update( &mut self, tree: &mut Tree, - event: core::Event, + event: Event, layout: Layout<'_>, cursor: mouse::Cursor, - _renderer: &Renderer, + renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - _viewport: &Rectangle, + viewport: &Rectangle, ) { let bounds = layout.bounds(); - let canvas_event = match event { - core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)), - core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)), - core::Event::Keyboard(keyboard_event) => { - Some(Event::Keyboard(keyboard_event)) - } - core::Event::Window(_) => None, - }; - - if let Some(canvas_event) = canvas_event { - let state = tree.state.downcast_mut::<P::State>(); + let state = tree.state.downcast_mut::<P::State>(); + let is_redraw_request = matches!( + event, + Event::Window(window::Event::RedrawRequested(_now)), + ); - let (event_status, message) = - self.program.update(state, canvas_event, bounds, cursor); + if let Some(action) = self.program.update(state, event, bounds, cursor) + { + let (message, redraw_request, event_status) = action.into_inner(); if let Some(message) = message { shell.publish(message); } + if let Some(redraw_request) = redraw_request { + match redraw_request { + window::RedrawRequest::NextFrame => { + shell.request_redraw(); + } + window::RedrawRequest::At(at) => { + shell.request_redraw_at(at); + } + } + } + if event_status == event::Status::Captured { shell.capture_event(); } } + + if shell.redraw_request() != Some(window::RedrawRequest::NextFrame) { + let mouse_interaction = self + .mouse_interaction(tree, layout, cursor, viewport, renderer); + + if is_redraw_request { + self.last_mouse_interaction = Some(mouse_interaction); + } else if self.last_mouse_interaction.is_some_and( + |last_mouse_interaction| { + last_mouse_interaction != mouse_interaction + }, + ) { + shell.request_redraw(); + } + } } fn mouse_interaction( diff --git a/widget/src/canvas/event.rs b/widget/src/canvas/event.rs deleted file mode 100644 index a8eb47f7..00000000 --- a/widget/src/canvas/event.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Handle events of a canvas. -use crate::core::keyboard; -use crate::core::mouse; -use crate::core::touch; - -pub use crate::core::event::Status; - -/// A [`Canvas`] event. -/// -/// [`Canvas`]: crate::Canvas -#[derive(Debug, Clone, PartialEq)] -pub enum Event { - /// A mouse event. - Mouse(mouse::Event), - - /// A touch event. - Touch(touch::Event), - - /// A keyboard event. - Keyboard(keyboard::Event), -} diff --git a/widget/src/canvas/program.rs b/widget/src/canvas/program.rs index a7ded0f4..c68b2830 100644 --- a/widget/src/canvas/program.rs +++ b/widget/src/canvas/program.rs @@ -1,8 +1,8 @@ -use crate::canvas::event::{self, Event}; use crate::canvas::mouse; -use crate::canvas::Geometry; +use crate::canvas::{Event, Geometry}; use crate::core::Rectangle; use crate::graphics::geometry; +use crate::Action; /// The state and logic of a [`Canvas`]. /// @@ -22,8 +22,9 @@ where /// When a [`Program`] is used in a [`Canvas`], the runtime will call this /// method for each [`Event`]. /// - /// This method can optionally return a `Message` to notify an application - /// of any meaningful interactions. + /// This method can optionally return an [`Action`] to either notify an + /// application of any meaningful interactions, capture the event, or + /// request a redraw. /// /// By default, this method does and returns nothing. /// @@ -34,8 +35,8 @@ where _event: Event, _bounds: Rectangle, _cursor: mouse::Cursor, - ) -> (event::Status, Option<Message>) { - (event::Status::Ignored, None) + ) -> Option<Action<Message>> { + None } /// Draws the state of the [`Program`], producing a bunch of [`Geometry`]. @@ -84,7 +85,7 @@ where event: Event, bounds: Rectangle, cursor: mouse::Cursor, - ) -> (event::Status, Option<Message>) { + ) -> Option<Action<Message>> { T::update(self, state, event, bounds, cursor) } diff --git a/widget/src/lib.rs b/widget/src/lib.rs index a68720d6..776a04a0 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -8,6 +8,7 @@ pub use iced_renderer::graphics; pub use iced_runtime as runtime; pub use iced_runtime::core; +mod action; mod column; mod mouse_area; mod row; @@ -131,4 +132,5 @@ pub use qr_code::QRCode; pub mod markdown; pub use crate::core::theme::{self, Theme}; +pub use action::Action; pub use renderer::Renderer; |