feather_ui/component/
image.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 Fundament Research Institute <https://fundament.institute>
3
4use crate::layout::{Layout, leaf};
5use crate::render::atlas::Size;
6use crate::{DAbsPoint, SourceID, UNSIZED_AXIS};
7use derive_where::derive_where;
8use std::rc::Rc;
9use std::sync::Arc;
10
11#[derive(feather_macro::StateMachineChild)]
12#[derive_where(Clone)]
13pub struct Image<T> {
14    pub id: Arc<SourceID>,
15    pub props: Rc<T>,
16    pub resource: Box<dyn crate::resource::Location>,
17    pub size: DAbsPoint,
18    pub dynamic: bool,
19}
20
21impl<T: leaf::Padded + 'static> Image<T> {
22    pub fn new(
23        id: Arc<SourceID>,
24        props: T,
25        resource: &dyn crate::resource::Location,
26        size: DAbsPoint,
27        dynamic: bool,
28    ) -> Self {
29        Self {
30            id,
31            props: props.into(),
32            resource: dyn_clone::clone_box(resource),
33            size,
34            dynamic,
35        }
36    }
37}
38
39fn zero_float(f: f32) -> i32 {
40    if f.is_finite() && f != UNSIZED_AXIS {
41        f.ceil() as i32
42    } else {
43        0
44    }
45}
46
47impl<T: leaf::Padded + 'static> super::Component for Image<T>
48where
49    for<'a> &'a T: Into<&'a (dyn leaf::Padded + 'static)>,
50{
51    type Props = T;
52
53    fn layout(
54        &self,
55        manager: &mut crate::StateManager,
56        driver: &crate::graphics::Driver,
57        window: &Arc<SourceID>,
58    ) -> Box<dyn Layout<T>> {
59        let dpi = manager
60            .get::<super::window::WindowStateMachine>(window)
61            .map(|x| x.state.dpi)
62            .unwrap_or(crate::BASE_DPI);
63
64        let size = self.size.resolve(dpi);
65
66        // TODO: Layout cannot easily return an error because this messes up the
67        // persistent functions
68        let uvsize = driver
69            .load_and_resize(
70                self.resource.as_ref(),
71                Size::new(zero_float(size.x), zero_float(size.y)),
72                dpi.width,
73                self.dynamic,
74            )
75            .unwrap();
76
77        Box::new(leaf::Sized::<T> {
78            props: self.props.clone(),
79            id: Arc::downgrade(&self.id),
80            size: uvsize.cast().cast_unit(),
81            renderable: Some(Rc::new(crate::render::image::Instance {
82                image: self.resource.clone(),
83                padding: self.props.padding().as_perimeter(dpi),
84                dpi: dpi.width,
85                resize: self.dynamic,
86            })),
87        })
88    }
89}