feather-ui 0.4.0

Feather UI library
Documentation
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2025 Fundament Research Institute <https://fundament.institute>

use super::mouse_area::MouseArea;

use crate::component::{ChildOf, Desc};
use crate::layout::{Layout, fixed};
use crate::persist::{FnPersist, VectorMap};
use crate::{Component, DRect, Slot, SourceID, layout};
use derive_where::derive_where;
use std::rc::Rc;
use std::sync::Arc;

// A button component that contains a mousearea alongside it's children
#[derive_where(Clone)]
pub struct Button<T> {
    pub id: Arc<SourceID>,
    props: Rc<T>,
    marea: MouseArea<DRect>,
    children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
}

impl<T: fixed::Prop> Button<T> {
    pub fn new(
        id: Arc<SourceID>,
        props: T,
        onclick: Slot,
        children: im::Vector<Option<Box<ChildOf<dyn fixed::Prop>>>>,
    ) -> Self {
        Self {
            id: id.clone(),
            props: props.into(),
            marea: MouseArea::new(
                id.child(crate::DataID::Named("__marea_internal__")),
                crate::FILL_DRECT,
                None,
                [Some(onclick), None, None, None, None, None],
            ),
            children,
        }
    }
}

impl<T: fixed::Prop> crate::StateMachineChild for Button<T> {
    fn id(&self) -> Arc<SourceID> {
        self.id.clone()
    }

    fn apply_children(
        &self,
        f: &mut dyn FnMut(&dyn crate::StateMachineChild) -> eyre::Result<()>,
    ) -> eyre::Result<()> {
        self.children
            .iter()
            .try_for_each(|x| f(x.as_ref().unwrap().as_ref()))?;
        f(&self.marea)
    }
}

impl<T: fixed::Prop + 'static> Component for Button<T>
where
    for<'a> &'a T: Into<&'a (dyn fixed::Prop + 'static)>,
{
    type Props = T;

    fn layout(
        &self,
        manager: &mut crate::StateManager,
        driver: &crate::graphics::Driver,
        window: &Arc<SourceID>,
    ) -> Box<dyn Layout<T>> {
        let mut map = VectorMap::new(crate::persist::Persist::new(
            |child: &Option<Box<ChildOf<dyn fixed::Prop>>>| -> Option<Box<dyn Layout<<dyn fixed::Prop as Desc>::Child>>> {
                Some(child.as_ref()?.layout(manager, driver, window))
            })
        );

        let (_, mut children) = map.call(Default::default(), &self.children);
        children.push_back(Some(Box::new(self.marea.layout(manager, driver, window))));

        Box::new(layout::Node::<T, dyn fixed::Prop> {
            props: self.props.clone(),
            children,
            id: Arc::downgrade(&self.id),
            renderable: None,
            layer: None,
        })
    }
}