charm_ui/charms/
stack.rs

1use crate::{
2    component::{IntoParameter, Parameter, RenderCtx, Renderer},
3    layout::{Bounds, Padding, Size},
4    CharmResult, Color, Component,
5};
6
7pub struct VStack<Store> {
8    children: Vec<Box<dyn Component<Store>>>,
9    size: Option<Parameter<Size, Store>>,
10    padding: Option<Parameter<Padding, Store>>,
11    background: Option<Parameter<Color, Store>>,
12}
13
14impl<Store> VStack<Store> {
15    pub fn new() -> Self {
16        Self {
17            children: Vec::new(),
18            size: None,
19            padding: None,
20            background: None,
21        }
22    }
23
24    pub fn add_child<C>(mut self, component: C) -> Self
25    where
26        C: Component<Store> + 'static,
27    {
28        self.children.push(Box::new(component));
29
30        self
31    }
32
33    pub fn size(mut self, size: Parameter<Size, Store>) -> Self {
34        self.size = Some(size);
35
36        self
37    }
38
39    pub fn padding(mut self, padding: Parameter<Padding, Store>) -> Self {
40        self.padding = Some(padding);
41
42        self
43    }
44
45    pub fn background(mut self, fill: Parameter<Color, Store>) -> Self {
46        self.background = Some(fill);
47
48        self
49    }
50}
51
52impl<Store> Component<Store> for VStack<Store> {
53    fn render<'a>(&self, ctx: RenderCtx, r: &mut Renderer, store: &Store) -> CharmResult<Bounds> {
54        let Bounds { x, y, .. } = ctx.bounds.clone();
55        let Size { width, height } = self
56            .size
57            .as_ref()
58            .map(|s| s.resolve(store))
59            .unwrap_or(ctx.bounds.into());
60
61        if let Some(ref background_fill) = self.background {
62            r.fill_rect((x, y, width, height), background_fill.resolve(&store))?;
63        }
64
65        let mut child_ctx = ctx.clone();
66        child_ctx.bounds.width = width;
67        child_ctx.bounds.height = height;
68
69        if let Some(ref padding) = self.padding {
70            let padding = padding.resolve(&store);
71            child_ctx.bounds.x += padding.left;
72            child_ctx.bounds.y += padding.top;
73            child_ctx.bounds.width -= padding.left + padding.right;
74            child_ctx.bounds.height -= padding.top + padding.bottom;
75        }
76
77        for child in &self.children {
78            let bounds = child.render(child_ctx.clone(), r, &store)?;
79
80            child_ctx.bounds.y += bounds.height;
81            child_ctx.bounds.height -= bounds.height;
82        }
83
84        Ok(Bounds {
85            x,
86            y,
87            width,
88            height,
89        })
90    }
91}