fui-rs 0.2.3-alpha2

Retained-mode Rust UI SDK for EffinDOM
Documentation
use super::core::*;
use super::*;

#[derive(Clone)]
pub struct Grid {
    base: FlexBox,
    props: Rc<RefCell<GridProps>>,
    placements: Rc<RefCell<Vec<(NodeRef, GridPlacement)>>>,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GridTrack {
    pub value: f32,
    pub unit: GridUnit,
}

impl GridTrack {
    pub const fn px(value: f32) -> Self {
        Self {
            value,
            unit: GridUnit::Pixel,
        }
    }

    pub const fn star(value: f32) -> Self {
        Self {
            value,
            unit: GridUnit::Star,
        }
    }

    pub const fn auto() -> Self {
        Self {
            value: 0.0,
            unit: GridUnit::Auto,
        }
    }
}

impl Default for Grid {
    fn default() -> Self {
        let base = FlexBox::default();
        base.core.borrow_mut().kind = NodeKind::Grid;
        Self {
            base,
            props: Rc::new(RefCell::new(GridProps::default())),
            placements: Rc::new(RefCell::new(Vec::new())),
        }
    }
}

impl Node for Grid {
    fn retained_node_ref(&self) -> NodeRef {
        NodeRef::from_node(self.base.core.clone(), self.clone())
    }

    fn build_self(&self) {
        self.base.build_self();
        apply_grid_props(self.handle(), &self.props.borrow());
    }

    fn build_children(&self) {
        self.base.build_children();
        self.apply_grid_placements();
    }
}

impl Grid {
    pub fn shared_size_scope<T: Node>(target: &T, enabled: bool) {
        let node = target.retained_node_ref();
        node.set_shared_size_scope(enabled);
        if target.has_built_handle() {
            target.notify_retained_layout_mutation();
        }
    }

    pub(crate) fn downgrade(&self) -> WeakFlexBox {
        self.base.downgrade()
    }

    fn apply_grid_placements(&self) {
        for (child, placement) in self.placements.borrow().iter() {
            let handle = child.handle();
            if handle != NodeHandle::INVALID {
                ui::set_grid_placement(
                    handle.raw(),
                    placement.row,
                    placement.col,
                    placement.row_span,
                    placement.col_span,
                );
            }
        }
    }

    pub fn columns<I>(&self, tracks: I) -> &Self
    where
        I: IntoIterator<Item = GridTrack>,
    {
        let tracks: Vec<GridTrack> = tracks.into_iter().collect();
        let mut props = self.props.borrow_mut();
        props.columns = tracks.iter().map(|track| track.value).collect();
        props.column_types = tracks.iter().map(|track| track.unit).collect();
        drop(props);
        if self.has_built_handle() {
            self.build_self();
            self.notify_retained_layout_mutation();
        }
        self
    }

    pub fn rows<I>(&self, tracks: I) -> &Self
    where
        I: IntoIterator<Item = GridTrack>,
    {
        let tracks: Vec<GridTrack> = tracks.into_iter().collect();
        let mut props = self.props.borrow_mut();
        props.rows = tracks.iter().map(|track| track.value).collect();
        props.row_types = tracks.iter().map(|track| track.unit).collect();
        drop(props);
        if self.has_built_handle() {
            self.build_self();
            self.notify_retained_layout_mutation();
        }
        self
    }

    pub fn column_shared_size_group(&self, index: u32, group: impl Into<String>) -> &Self {
        let group = group.into();
        let mut props = self.props.borrow_mut();
        if let Some((_, existing)) = props
            .column_shared_size_groups
            .iter_mut()
            .find(|(existing_index, _)| *existing_index == index)
        {
            *existing = group;
        } else {
            props.column_shared_size_groups.push((index, group));
        }
        drop(props);
        if self.has_built_handle() {
            self.build_self();
            self.notify_retained_layout_mutation();
        }
        self
    }

    pub fn clear_column_shared_size_group(&self, index: u32) -> &Self {
        self.column_shared_size_group(index, "")
    }

    pub fn row_shared_size_group(&self, index: u32, group: impl Into<String>) -> &Self {
        let group = group.into();
        let mut props = self.props.borrow_mut();
        if let Some((_, existing)) = props
            .row_shared_size_groups
            .iter_mut()
            .find(|(existing_index, _)| *existing_index == index)
        {
            *existing = group;
        } else {
            props.row_shared_size_groups.push((index, group));
        }
        drop(props);
        if self.has_built_handle() {
            self.build_self();
            self.notify_retained_layout_mutation();
        }
        self
    }

    pub fn clear_row_shared_size_group(&self, index: u32) -> &Self {
        self.row_shared_size_group(index, "")
    }

    pub fn place_child<T: Node>(
        &self,
        child: &T,
        row: u32,
        col: u32,
        row_span: u32,
        col_span: u32,
    ) -> &Self {
        self.append_child(child);
        self.placements.borrow_mut().push((
            child.node_ref(),
            GridPlacement {
                row,
                col,
                row_span,
                col_span,
            },
        ));
        if self.has_built_handle() {
            self.apply_grid_placements();
        }
        self
    }
}

impl HasFlexBoxRoot for Grid {
    fn flex_box_root(&self) -> &FlexBox {
        &self.base
    }
}

impl ThemeBindable for Grid {
    fn theme_binding_node(&self) -> NodeRef {
        self.retained_node_ref()
    }

    fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
        let base = self.base.downgrade();
        let props = Rc::downgrade(&self.props);
        let placements = Rc::downgrade(&self.placements);
        Box::new(move || {
            Some(Self {
                base: base.upgrade()?,
                props: props.upgrade()?,
                placements: placements.upgrade()?,
            })
        })
    }
}