feather_ui/component/
region.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 Fundament Research Institute <https://fundament.institute>
3
4use crate::color::sRGB32;
5use crate::component::ChildOf;
6use crate::layout::{Desc, Layout, fixed};
7use crate::persist::{FnPersist, VectorMap};
8use crate::{SourceID, layout};
9use derive_where::derive_where;
10use std::rc::Rc;
11use std::sync::Arc;
12
13#[derive(feather_macro::StateMachineChild)]
14#[derive_where(Clone, Default)]
15pub struct Region<T: Default> {
16    pub id: Arc<SourceID>,
17    pub color: Option<sRGB32>,
18    pub rotation: Option<f32>,
19    props: Rc<T>,
20    children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
21}
22
23impl<T: fixed::Prop + Default + 'static> Region<T> {
24    pub fn new(
25        id: Arc<SourceID>,
26        props: T,
27        children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
28    ) -> Self {
29        Self {
30            id,
31            props: props.into(),
32            children,
33            ..Default::default()
34        }
35    }
36
37    pub fn new_layer(
38        id: Arc<SourceID>,
39        props: T,
40        color: sRGB32,
41        rotation: f32,
42        children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
43    ) -> Self {
44        Self {
45            id,
46            props: props.into(),
47            color: Some(color),
48            rotation: Some(rotation),
49            children,
50        }
51    }
52}
53
54impl<T: fixed::Prop + Default + 'static> super::Component for Region<T>
55where
56    for<'a> &'a T: Into<&'a (dyn fixed::Prop + 'static)>,
57{
58    type Props = T;
59
60    fn layout(
61        &self,
62        manager: &mut crate::StateManager,
63        driver: &crate::graphics::Driver,
64        window: &Arc<SourceID>,
65    ) -> Box<dyn Layout<T>> {
66        let mut map = VectorMap::new(crate::persist::Persist::new(
67            |child: &Option<Box<ChildOf<dyn fixed::Prop>>>| -> Option<Box<dyn Layout<<dyn fixed::Prop as Desc>::Child>>> {
68                Some(child.as_ref()?.layout(manager, driver, window))
69            }));
70
71        let layer = if self.color.is_some() || self.rotation.is_some() {
72            Some((
73                self.color.unwrap_or(sRGB32::white()),
74                self.rotation.unwrap_or_default(),
75            ))
76        } else {
77            None
78        };
79
80        let (_, children) = map.call(Default::default(), &self.children);
81        Box::new(layout::Node::<T, dyn fixed::Prop> {
82            props: self.props.clone(),
83            children,
84            id: Arc::downgrade(&self.id),
85            renderable: None,
86            layer,
87        })
88    }
89}