maycoon_widgets/
image.rs

1use crate::ext::WidgetLayoutExt;
2use maycoon_core::app::info::AppInfo;
3use maycoon_core::app::update::Update;
4use maycoon_core::layout::{LayoutNode, LayoutStyle, StyleNode};
5use maycoon_core::state::{State, Val};
6use maycoon_core::vg::kurbo::{Affine, Vec2};
7use maycoon_core::vg::peniko::{Blob, ImageFormat};
8use maycoon_core::vg::{peniko, Scene};
9use maycoon_core::widget::Widget;
10use maycoon_theme::id::WidgetId;
11use maycoon_theme::theme::Theme;
12use nalgebra::Vector2;
13
14/// An image widget. Pretty self-explanatory.
15///
16/// See the [image](https://github.com/maycoon-ui/maycoon/blob/master/examples/image/src/main.rs) example for how to use it in practice.
17///
18/// ### Theming
19/// The widget itself only draws the underlying image, so theming is useless.
20pub struct Image<S: State> {
21    image: Val<S, peniko::Image>,
22    style: Val<S, LayoutStyle>,
23}
24
25impl<S: State> Image<S> {
26    /// Create an image widget from the given [`ImageData`].
27    pub fn new(image: impl Into<Val<S, ImageData>>) -> Self {
28        Self {
29            image: image.into().map(|data| {
30                peniko::Image::new(
31                    Blob::from(data.image),
32                    data.format,
33                    data.size.x,
34                    data.size.y,
35                )
36            }),
37            style: LayoutStyle::default().into(),
38        }
39    }
40
41    /// Set the image.
42    pub fn with_image(mut self, image: impl Into<Val<S, ImageData>>) -> Self {
43        self.image = image.into().map(|data| {
44            peniko::Image::new(
45                Blob::from(data.image),
46                data.format,
47                data.size.x,
48                data.size.y,
49            )
50        });
51        self
52    }
53}
54
55impl<S: State> WidgetLayoutExt<S> for Image<S> {
56    fn set_layout_style(&mut self, layout_style: impl Into<Val<S, LayoutStyle>>) {
57        self.style = layout_style.into();
58    }
59}
60
61impl<S: State> Widget<S> for Image<S> {
62    fn render(
63        &mut self,
64        scene: &mut Scene,
65        _: &mut dyn Theme,
66        _: &AppInfo,
67        layout_node: &LayoutNode,
68        state: &S,
69    ) {
70        let image = self.image.get_ref(state);
71
72        scene.draw_image(
73            image,
74            Affine::translate(Vec2::new(
75                layout_node.layout.location.x as f64,
76                layout_node.layout.location.y as f64,
77            )),
78        );
79    }
80
81    fn layout_style(&mut self, state: &S) -> StyleNode {
82        StyleNode {
83            style: self.style.get_ref(state).clone(),
84            children: Vec::new(),
85        }
86    }
87
88    fn update(&mut self, _: &LayoutNode, _: &mut S, _: &AppInfo) -> Update {
89        self.image.invalidate();
90        self.style.invalidate();
91        Update::empty()
92    }
93
94    fn widget_id(&self) -> WidgetId {
95        WidgetId::new("maycoon-widgets", "Image")
96    }
97}
98
99/// Contains data about the image itself, size and the image format.
100///
101/// If you have trouble with getting the image in the right format, use the `image` crate for converting it.
102#[derive(Clone, Debug, PartialEq, Eq)]
103pub struct ImageData {
104    image: Vec<u8>,
105    size: Vector2<u32>,
106    format: ImageFormat,
107}
108
109impl ImageData {
110    /// Creates a new [`ImageData`] from the image itself, its size and the image format.
111    pub fn new(image: Vec<u8>, size: Vector2<u32>, format: ImageFormat) -> Self {
112        Self {
113            image,
114            size,
115            format,
116        }
117    }
118}