charm-ui 0.1.0

an immediate-mode gui library with a friendly API
Documentation
use crate::{
    component::{IntoParameter, Parameter, RenderCtx, Renderer},
    layout::{Bounds, Padding, Size},
    CharmResult, Color, Component,
};

pub struct VStack<Store> {
    children: Vec<Box<dyn Component<Store>>>,
    size: Option<Parameter<Size, Store>>,
    padding: Option<Parameter<Padding, Store>>,
    background: Option<Parameter<Color, Store>>,
}

impl<Store> VStack<Store> {
    pub fn new() -> Self {
        Self {
            children: Vec::new(),
            size: None,
            padding: None,
            background: None,
        }
    }

    pub fn add_child<C>(mut self, component: C) -> Self
    where
        C: Component<Store> + 'static,
    {
        self.children.push(Box::new(component));

        self
    }

    pub fn size(mut self, size: Parameter<Size, Store>) -> Self {
        self.size = Some(size);

        self
    }

    pub fn padding(mut self, padding: Parameter<Padding, Store>) -> Self {
        self.padding = Some(padding);

        self
    }

    pub fn background(mut self, fill: Parameter<Color, Store>) -> Self {
        self.background = Some(fill);

        self
    }
}

impl<Store> Component<Store> for VStack<Store> {
    fn render<'a>(&self, ctx: RenderCtx, r: &mut Renderer, store: &Store) -> CharmResult<Bounds> {
        let Bounds { x, y, .. } = ctx.bounds.clone();
        let Size { width, height } = self
            .size
            .as_ref()
            .map(|s| s.resolve(store))
            .unwrap_or(ctx.bounds.into());

        if let Some(ref background_fill) = self.background {
            r.fill_rect((x, y, width, height), background_fill.resolve(&store))?;
        }

        let mut child_ctx = ctx.clone();
        child_ctx.bounds.width = width;
        child_ctx.bounds.height = height;

        if let Some(ref padding) = self.padding {
            let padding = padding.resolve(&store);
            child_ctx.bounds.x += padding.left;
            child_ctx.bounds.y += padding.top;
            child_ctx.bounds.width -= padding.left + padding.right;
            child_ctx.bounds.height -= padding.top + padding.bottom;
        }

        for child in &self.children {
            let bounds = child.render(child_ctx.clone(), r, &store)?;

            child_ctx.bounds.y += bounds.height;
            child_ctx.bounds.height -= bounds.height;
        }

        Ok(Bounds {
            x,
            y,
            width,
            height,
        })
    }
}