tui_realm_stdlib/components/
container.rs

1//! ## Container
2//!
3//! `Container` represents an empty container where you can put other components into it.
4//! It will render components based on how you defined the layout.
5//! The way it updates properties is usually assigning the attributes to all the children components, but
6//! when defining the component you can override these behaviours implementing `attr()` by yourself.
7//! By default it will forward `Commands' to all the children and will return a `CmdResult::Batch` with all the results.
8
9use tuirealm::command::{Cmd, CmdResult};
10use tuirealm::props::{Alignment, AttrValue, Attribute, Borders, Color, Layout, Props};
11use tuirealm::ratatui::layout::Rect;
12use tuirealm::{Frame, MockComponent, State};
13
14// -- Component
15
16/// ## Container
17///
18/// represents a read-only text component without any container.
19#[derive(Default)]
20pub struct Container {
21    props: Props,
22    /// Container children
23    pub children: Vec<Box<dyn MockComponent>>,
24}
25
26impl Container {
27    pub fn foreground(mut self, fg: Color) -> Self {
28        self.attr(Attribute::Foreground, AttrValue::Color(fg));
29        self
30    }
31
32    pub fn background(mut self, bg: Color) -> Self {
33        self.attr(Attribute::Background, AttrValue::Color(bg));
34        self
35    }
36
37    pub fn borders(mut self, b: Borders) -> Self {
38        self.attr(Attribute::Borders, AttrValue::Borders(b));
39        self
40    }
41
42    pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
43        self.attr(Attribute::Title, AttrValue::Title((t.into(), a)));
44        self
45    }
46
47    pub fn layout(mut self, layout: Layout) -> Self {
48        self.attr(Attribute::Layout, AttrValue::Layout(layout));
49        self
50    }
51
52    pub fn children(mut self, children: Vec<Box<dyn MockComponent>>) -> Self {
53        self.children = children;
54        self
55    }
56}
57
58impl MockComponent for Container {
59    fn view(&mut self, render: &mut Frame, area: Rect) {
60        // Make a Span
61        if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
62            // Make block
63            let borders = self
64                .props
65                .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
66                .unwrap_borders();
67            let title = self.props.get(Attribute::Title).map(|x| x.unwrap_title());
68            let div = crate::utils::get_block(borders, title, true, None);
69            // Render block
70            render.render_widget(div, area);
71            // Render children
72            if let Some(layout) = self.props.get(Attribute::Layout).map(|x| x.unwrap_layout()) {
73                // make chunks
74                let chunks = layout.chunks(area);
75                // iter chunks
76                for (i, chunk) in chunks.into_iter().enumerate() {
77                    if let Some(child) = self.children.get_mut(i) {
78                        child.view(render, chunk);
79                    }
80                }
81            }
82        }
83    }
84
85    fn query(&self, attr: Attribute) -> Option<AttrValue> {
86        self.props.get(attr)
87    }
88
89    fn attr(&mut self, attr: Attribute, value: AttrValue) {
90        self.props.set(attr, value.clone());
91        // Patch attribute to children
92        self.children
93            .iter_mut()
94            .for_each(|x| x.attr(attr, value.clone()));
95    }
96
97    fn state(&self) -> State {
98        State::None
99    }
100
101    fn perform(&mut self, cmd: Cmd) -> CmdResult {
102        // Send command to children and return batch
103        CmdResult::Batch(self.children.iter_mut().map(|x| x.perform(cmd)).collect())
104    }
105}
106
107#[cfg(test)]
108mod tests {
109
110    use super::*;
111
112    use pretty_assertions::assert_eq;
113
114    #[test]
115    fn test_components_paragraph() {
116        let component = Container::default()
117            .background(Color::Blue)
118            .foreground(Color::Red)
119            .title("title", Alignment::Center);
120        // Get value
121        assert_eq!(component.state(), State::None);
122    }
123}