ori_core/views/
image.rs

1use glam::Vec2;
2use ori_graphics::{ImageHandle, ImageSource, Mesh};
3use ori_macro::Build;
4
5use crate::{
6    BoxConstraints, Context, DrawContext, Event, EventContext, LayoutContext, Style, View,
7};
8
9#[derive(Clone, Default, Debug, Build)]
10pub struct Image {
11    #[prop]
12    src: ImageSource,
13}
14
15impl Image {
16    pub fn new() -> Self {
17        Self::default()
18    }
19}
20
21#[derive(Clone, Debug, Default)]
22pub struct ImageState {
23    src: ImageSource,
24    handle: Option<ImageHandle>,
25}
26
27impl ImageState {
28    pub fn update(&mut self, context: &mut impl Context, src: &ImageSource) -> &ImageHandle {
29        if self.src != *src || self.handle.is_none() {
30            self.src = src.clone();
31            self.handle = Some(context.load_image(src));
32        }
33
34        self.handle.as_ref().unwrap()
35    }
36}
37
38impl View for Image {
39    type State = ImageState;
40
41    fn build(&self) -> Self::State {
42        Default::default()
43    }
44
45    fn style(&self) -> Style {
46        Style::new("image")
47    }
48
49    fn event(&self, _state: &mut Self::State, _cx: &mut EventContext, _event: &Event) {}
50
51    fn layout(&self, state: &mut Self::State, cx: &mut LayoutContext, bc: BoxConstraints) -> Vec2 {
52        let min_width = cx.style_range_group("min-width", "width", bc.width());
53        let max_width = cx.style_range_group("max-width", "width", bc.width());
54
55        let min_height = cx.style_range_group("min-width", "height", bc.height());
56        let max_height = cx.style_range_group("min-height", "height", bc.height());
57
58        let min_size = bc.constrain(Vec2::new(min_width, min_height));
59        let max_size = bc.constrain(Vec2::new(max_width, max_height));
60
61        let handle = state.update(cx, &self.src);
62
63        // try to fit the image in the min/max size
64        // while maintaining the aspect ratio
65        let mut size = handle.size();
66        let aspect = size.x / size.y;
67
68        if size.x > max_size.x {
69            size.x = max_size.x;
70            size.y = size.x / aspect;
71        }
72
73        if size.y > max_size.y {
74            size.y = max_size.y;
75            size.x = size.y * aspect;
76        }
77
78        if size.x < min_size.x {
79            size.x = min_size.x;
80            size.y = size.x / aspect;
81        }
82
83        if size.y < min_size.y {
84            size.y = min_size.y;
85            size.x = size.y * aspect;
86        }
87
88        size
89    }
90
91    fn draw(&self, state: &mut Self::State, cx: &mut DrawContext) {
92        let handle = state.update(cx, &self.src);
93        let mesh = Mesh::image(cx.rect(), handle.clone());
94        cx.draw(mesh);
95    }
96}