maycoon_widgets/
container.rs

1use crate::ext::{WidgetChildrenExt, WidgetLayoutExt};
2use maycoon_core::app::info::AppInfo;
3use maycoon_core::app::update::Update;
4use maycoon_core::layout::{LayoutNode, LayoutStyle, StyleNode};
5use maycoon_core::state::{State, Val};
6use maycoon_core::vg::Scene;
7use maycoon_core::widget::Widget;
8use maycoon_theme::id::WidgetId;
9use maycoon_theme::theme::Theme;
10
11/// A container widget that can display and layout multiple child widgets.
12///
13/// The layout of the children (row, column, etc.) depends on the [`LayoutStyle`] of the container.
14///
15/// See the [`counter`](https://github.com/maycoon-ui/maycoon/blob/master/examples/counter/src/main.rs) example for how to use it in practice.
16///
17/// ### Theming
18/// The container widget doesn't actually draw anything but the child widgets, so theming is useless.
19pub struct Container<S: State> {
20    style: Val<S, LayoutStyle>,
21    children: Vec<Val<S, Box<dyn Widget<S>>>>,
22}
23
24impl<S: State> Container<S> {
25    /// Creates a new container with given children.
26    pub fn new(children: Vec<Val<S, Box<dyn Widget<S>>>>) -> Self {
27        Self {
28            style: LayoutStyle::default().into(),
29            children,
30        }
31    }
32
33    /// Adds the given children to the container.
34    pub fn add_children(&mut self, children: Vec<Val<S, Box<dyn Widget<S>>>>) {
35        self.children.extend(children);
36    }
37
38    /// Adds the given children to the container and returns itself.
39    pub fn with_children(mut self, children: Vec<Val<S, Box<dyn Widget<S>>>>) -> Self {
40        self.add_children(children);
41        self
42    }
43
44    /// Adds the given child to the container.
45    pub fn add_child(&mut self, child: impl Into<Val<S, Box<dyn Widget<S>>>>) {
46        self.children.push(child.into());
47    }
48
49    /// Adds the given child to the container and returns itself.
50    pub fn with_child(mut self, child: impl Into<Val<S, Box<dyn Widget<S>>>>) -> Self {
51        self.add_child(child);
52        self
53    }
54}
55
56impl<S: State> WidgetChildrenExt<S> for Container<S> {
57    fn set_children(&mut self, children: Vec<Val<S, impl Widget<S> + 'static>>) {
58        self.children = children
59            .into_iter()
60            .map(|val| val.map(|w| Box::new(w) as Box<dyn Widget<S> + 'static>))
61            .collect::<Vec<Val<S, Box<dyn Widget<S> + 'static>>>>();
62    }
63
64    fn add_child<W: Widget<S> + 'static>(&mut self, child: impl Into<Val<S, W>>) {
65        self.children.push(
66            child
67                .into()
68                .map(|w| Box::new(w) as Box<dyn Widget<S> + 'static>),
69        )
70    }
71}
72
73impl<S: State> WidgetLayoutExt<S> for Container<S> {
74    fn set_layout_style(&mut self, layout_style: impl Into<Val<S, LayoutStyle>>) {
75        self.style = layout_style.into();
76    }
77}
78
79impl<S: State> Default for Container<S> {
80    fn default() -> Self {
81        Self::new(Vec::new())
82    }
83}
84
85impl<S: State> Widget<S> for Container<S> {
86    fn render(
87        &mut self,
88        scene: &mut Scene,
89        theme: &mut dyn Theme,
90        info: &AppInfo,
91        layout_node: &LayoutNode,
92        state: &S,
93    ) {
94        for (i, child) in self.children.iter_mut().enumerate() {
95            let child = child.get_mut(state);
96            child.render(scene, theme, info, &layout_node.children[i], state);
97        }
98    }
99
100    fn layout_style(&mut self, state: &S) -> StyleNode {
101        let style = self.style.get_ref(state).clone();
102
103        let mut children = Vec::with_capacity(self.children.len());
104
105        for child in &mut self.children {
106            let child = child.get_mut(state);
107
108            children.push(child.layout_style(state));
109        }
110
111        StyleNode { style, children }
112    }
113
114    fn update(&mut self, layout: &LayoutNode, state: &mut S, info: &AppInfo) -> Update {
115        self.style.invalidate();
116
117        for child in &mut self.children {
118            child.invalidate();
119        }
120
121        let mut update = Update::empty();
122
123        for (i, child) in self.children.iter_mut().enumerate() {
124            let child = child.get_mut(state);
125            update.insert(child.update(&layout.children[i], state, info));
126        }
127
128        update
129    }
130
131    fn widget_id(&self) -> WidgetId {
132        WidgetId::new("maycoon-widgets", "Container")
133    }
134}