Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: image operations #199

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions core/src/widget/operation.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -47,6 +49,9 @@ pub trait Operation<T = ()>: 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>) {}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
42 changes: 42 additions & 0 deletions core/src/widget/operation/image.rs
Original file line number Diff line number Diff line change
@@ -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<T>(target: Id, handle: Handle) -> impl Operation<T> {
struct SetHandle {
target: Id,
handle: Handle,
}

impl<T> Operation<T> 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<T>),
) {
operate_on_children(self);
}
}

SetHandle { target, handle }
}
75 changes: 69 additions & 6 deletions widget/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
//! ```
//! <img src="https://github.com/iced-rs/iced/blob/9712b319bb7a32848001b96bd84977430f14b623/examples/resources/ferris.png?raw=true" width="300">
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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -185,7 +193,8 @@ impl<'a, Handle> Image<'a, Handle> {
}

/// Computes the layout of an [`Image`].
pub fn layout<Renderer, Handle>(
pub fn layout<Renderer, Handle: 'static>(
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
handle: &Handle,
Expand All @@ -198,6 +207,13 @@ pub fn layout<Renderer, Handle>(
where
Renderer: image::Renderer<Handle = Handle>,
{
let state = tree.state.downcast_ref::<State<Handle>>();
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 =
Expand Down Expand Up @@ -229,6 +245,7 @@ where

/// Draws an [`Image`]
pub fn draw<Renderer, Handle>(
tree: &Tree,
renderer: &mut Renderer,
layout: Layout<'_>,
handle: &Handle,
Expand All @@ -239,8 +256,15 @@ pub fn draw<Renderer, Handle>(
border_radius: [f32; 4],
) where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone,
Handle: Clone + 'static,
{
let state = tree.state.downcast_ref::<State<Handle>>();
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);
Expand Down Expand Up @@ -296,7 +320,7 @@ impl<'a, Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer>
for Image<'a, Handle>
where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone,
Handle: Clone + 'static,
{
fn size(&self) -> Size<Length> {
Size {
Expand All @@ -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,
Expand All @@ -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::<State>();
operation.image(state, Some(&self.id));
}

fn draw(
&self,
_state: &Tree,
tree: &Tree,
renderer: &mut Renderer,
_theme: &Theme,
_style: &renderer::Style,
Expand All @@ -334,6 +370,7 @@ where
_viewport: &Rectangle,
) {
draw(
tree,
renderer,
layout,
&self.handle,
Expand Down Expand Up @@ -404,15 +441,41 @@ where
fn set_id(&mut self, id: Id) {
self.id = id;
}

fn state(&self) -> tree::State {
tree::State::new(State::<Handle>::new())
}
}

/// The state of a [`Image`].
#[derive(Debug, Default, Clone)]
pub struct State<Handle = image::Handle> {
handle: Option<Handle>,
}

impl<Handle> State<Handle> {
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<Image<'a, Handle>>
for Element<'a, Message, Theme, Renderer>
where
Renderer: image::Renderer<Handle = Handle>,
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);
}
}
Loading