diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index a912eb1bf7..1d525a8cbc 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -1,10 +1,12 @@ //! Query or update internal widget state. pub mod focusable; +pub mod image; pub mod scrollable; pub mod search_id; pub mod text_input; pub use focusable::Focusable; +pub use image::Image; pub use scrollable::Scrollable; pub use text_input::TextInput; @@ -47,6 +49,9 @@ pub trait Operation: Send { /// Operates on a widget that has text input. fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {} + /// Operates on a widget that displays an image. + fn image(&mut self, _state: &mut dyn Image, _id: Option<&Id>) {} + /// Operates on a custom widget. fn custom(&mut self, _state: &mut dyn Any, _id: Option<&Id>) {} @@ -94,6 +99,10 @@ where self.as_mut().text_input(state, id); } + fn image(&mut self, state: &mut dyn Image, id: Option<&Id>) { + self.as_mut().image(state, id); + } + fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { self.as_mut().custom(state, id); } @@ -176,6 +185,10 @@ where self.operation.text_input(state, id); } + fn image(&mut self, state: &mut dyn Image, id: Option<&Id>) { + self.operation.image(state, id); + } + fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { self.operation.custom(state, id); } @@ -266,6 +279,10 @@ where self.operation.text_input(state, id); } + fn image(&mut self, state: &mut dyn Image, id: Option<&Id>) { + self.operation.image(state, id); + } + fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { self.operation.custom(state, id); } @@ -301,6 +318,10 @@ where self.operation.text_input(state, id); } + fn image(&mut self, state: &mut dyn Image, id: Option<&Id>) { + self.operation.image(state, id); + } + fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) { self.operation.custom(state, id); } @@ -387,6 +408,10 @@ where self.operation.text_input(state, id); } + fn image(&mut self, state: &mut dyn Image, id: Option<&Id>) { + self.operation.image(state, id); + } + fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) { self.operation.custom(state, id); } diff --git a/core/src/widget/operation/image.rs b/core/src/widget/operation/image.rs new file mode 100644 index 0000000000..9ed7159cde --- /dev/null +++ b/core/src/widget/operation/image.rs @@ -0,0 +1,42 @@ +//! Operate on widgets that display an image. +use crate::widget::Id; + +use crate::image::Handle; + +use super::Operation; + +/// The internal state of a widget that displays an image. +pub trait Image { + /// Sets the handle of the image. + fn set_handle(&mut self, handle: Handle); +} + +/// Produces an [`Operation`] that sets the handle of the widget with the given [`Id`]. +pub fn set_handle(target: Id, handle: Handle) -> impl Operation { + struct SetHandle { + target: Id, + handle: Handle, + } + + impl Operation for SetHandle { + fn image(&mut self, state: &mut dyn Image, id: Option<&Id>) { + match id { + Some(id) if id == &self.target => { + state.set_handle(self.handle.clone()); + } + _ => println!("Invalid id for image widget: {:?}", id), + } + } + + fn container( + &mut self, + _id: Option<&Id>, + _bounds: crate::Rectangle, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self); + } + } + + SetHandle { target, handle } +} diff --git a/widget/src/image.rs b/widget/src/image.rs index 20cb300c22..594d343e72 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -17,6 +17,8 @@ //! ``` //! pub mod viewer; +use iced_runtime::core::widget::operation; +use iced_runtime::core::widget::tree; use iced_runtime::core::widget::Id; pub use viewer::Viewer; @@ -101,6 +103,12 @@ impl<'a, Handle> Image<'a, Handle> { } } + /// Sets the [`Id`] of the [`TextInput`]. + pub fn id(mut self, id: Id) -> Self { + self.id = id; + self + } + /// Sets the border radius of the image. pub fn border_radius(mut self, border_radius: [f32; 4]) -> Self { self.border_radius = border_radius; @@ -185,7 +193,8 @@ impl<'a, Handle> Image<'a, Handle> { } /// Computes the layout of an [`Image`]. -pub fn layout( +pub fn layout( + tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, handle: &Handle, @@ -198,6 +207,13 @@ pub fn layout( where Renderer: image::Renderer, { + let state = tree.state.downcast_ref::>(); + let handle = if let Some(state_handle) = &state.handle { + state_handle + } else { + handle + }; + // The raw w/h of the underlying image let image_size = renderer.measure_image(handle); let image_size = @@ -229,6 +245,7 @@ where /// Draws an [`Image`] pub fn draw( + tree: &Tree, renderer: &mut Renderer, layout: Layout<'_>, handle: &Handle, @@ -239,8 +256,15 @@ pub fn draw( border_radius: [f32; 4], ) where Renderer: image::Renderer, - Handle: Clone, + Handle: Clone + 'static, { + let state = tree.state.downcast_ref::>(); + let handle = if let Some(state_handle) = &state.handle { + state_handle + } else { + handle + }; + let Size { width, height } = renderer.measure_image(handle); let image_size = Size::new(width as f32, height as f32); let rotated_size = rotation.apply(image_size); @@ -296,7 +320,7 @@ impl<'a, Message, Theme, Renderer, Handle> Widget for Image<'a, Handle> where Renderer: image::Renderer, - Handle: Clone, + Handle: Clone + 'static, { fn size(&self) -> Size { Size { @@ -307,11 +331,12 @@ where fn layout( &self, - _tree: &mut Tree, + tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { layout( + tree, renderer, limits, &self.handle, @@ -323,9 +348,20 @@ where ) } + fn operate( + &self, + tree: &mut Tree, + _layout: Layout<'_>, + _renderer: &Renderer, + operation: &mut dyn operation::Operation, + ) { + let state = tree.state.downcast_mut::(); + operation.image(state, Some(&self.id)); + } + fn draw( &self, - _state: &Tree, + tree: &Tree, renderer: &mut Renderer, _theme: &Theme, _style: &renderer::Style, @@ -334,6 +370,7 @@ where _viewport: &Rectangle, ) { draw( + tree, renderer, layout, &self.handle, @@ -404,15 +441,41 @@ where fn set_id(&mut self, id: Id) { self.id = id; } + + fn state(&self) -> tree::State { + tree::State::new(State::::new()) + } +} + +/// The state of a [`Image`]. +#[derive(Debug, Default, Clone)] +pub struct State { + handle: Option, +} + +impl State { + pub fn new() -> Self { + Self { handle: None } + } + + pub fn set_handle(&mut self, handle: Handle) { + self.handle = Some(handle); + } } impl<'a, Message, Theme, Renderer, Handle> From> for Element<'a, Message, Theme, Renderer> where Renderer: image::Renderer, - Handle: Clone + 'a, + Handle: Clone + 'static, { fn from(image: Image<'a, Handle>) -> Element<'a, Message, Theme, Renderer> { Element::new(image) } } + +impl operation::Image for State { + fn set_handle(&mut self, handle: image::Handle) { + State::set_handle(self, handle); + } +}