feather_ui/component/
button.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 Fundament Research Institute <https://fundament.institute>
3
4use super::mouse_area::MouseArea;
5
6use crate::component::{ChildOf, Desc};
7use crate::layout::{Layout, fixed};
8use crate::persist::{FnPersist, VectorMap};
9use crate::{Component, DRect, Slot, SourceID, layout};
10use derive_where::derive_where;
11use std::rc::Rc;
12use std::sync::Arc;
13
14// A button component that contains a mousearea alongside it's children
15#[derive_where(Clone)]
16pub struct Button<T> {
17    pub id: Arc<SourceID>,
18    props: Rc<T>,
19    marea: MouseArea<DRect>,
20    children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
21}
22
23impl<T: fixed::Prop + 'static> Button<T> {
24    pub fn new(
25        id: Arc<SourceID>,
26        props: T,
27        onclick: Slot,
28        children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
29    ) -> Self {
30        Self {
31            id: id.clone(),
32            props: props.into(),
33            marea: MouseArea::new(
34                id.child(crate::DataID::Named("__marea_internal__")),
35                crate::FILL_DRECT,
36                None,
37                [Some(onclick), None, None, None, None, None],
38            ),
39            children,
40        }
41    }
42}
43
44impl<T: fixed::Prop + 'static> crate::StateMachineChild for Button<T> {
45    fn id(&self) -> Arc<SourceID> {
46        self.id.clone()
47    }
48
49    fn apply_children(
50        &self,
51        f: &mut dyn FnMut(&dyn crate::StateMachineChild) -> eyre::Result<()>,
52    ) -> eyre::Result<()> {
53        self.children
54            .iter()
55            .try_for_each(|x| f(x.as_ref().unwrap().as_ref()))?;
56        f(&self.marea)
57    }
58}
59
60impl<T: fixed::Prop + 'static> Component for Button<T>
61where
62    for<'a> &'a T: Into<&'a (dyn fixed::Prop + 'static)>,
63{
64    type Props = T;
65
66    fn layout(
67        &self,
68        manager: &mut crate::StateManager,
69        driver: &crate::graphics::Driver,
70        window: &Arc<SourceID>,
71    ) -> Box<dyn Layout<T>> {
72        let mut map = VectorMap::new(crate::persist::Persist::new(
73            |child: &Option<Box<ChildOf<dyn fixed::Prop>>>| -> Option<Box<dyn Layout<<dyn fixed::Prop as Desc>::Child>>> {
74                Some(child.as_ref()?.layout(manager, driver, window))
75            })
76        );
77
78        let (_, mut children) = map.call(Default::default(), &self.children);
79        children.push_back(Some(Box::new(self.marea.layout(manager, driver, window))));
80
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: None,
87        })
88    }
89}