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)]
20#[must_use]
21pub struct Container {
22    props: Props,
23    /// Container children
24    pub children: Vec<Box<dyn MockComponent>>,
25}
26
27impl Container {
28    pub fn foreground(mut self, fg: Color) -> Self {
29        self.attr(Attribute::Foreground, AttrValue::Color(fg));
30        self
31    }
32
33    pub fn background(mut self, bg: Color) -> Self {
34        self.attr(Attribute::Background, AttrValue::Color(bg));
35        self
36    }
37
38    pub fn borders(mut self, b: Borders) -> Self {
39        self.attr(Attribute::Borders, AttrValue::Borders(b));
40        self
41    }
42
43    pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
44        self.attr(Attribute::Title, AttrValue::Title((t.into(), a)));
45        self
46    }
47
48    pub fn layout(mut self, layout: Layout) -> Self {
49        self.attr(Attribute::Layout, AttrValue::Layout(layout));
50        self
51    }
52
53    pub fn children(mut self, children: Vec<Box<dyn MockComponent>>) -> Self {
54        self.children = children;
55        self
56    }
57}
58
59impl MockComponent for Container {
60    fn view(&mut self, render: &mut Frame, area: Rect) {
61        // Make a Span
62        if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
63            // Make block
64            let borders = self
65                .props
66                .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
67                .unwrap_borders();
68            let title = self
69                .props
70                .get_ref(Attribute::Title)
71                .and_then(|x| x.as_title());
72            let div = crate::utils::get_block(borders, title, true, None);
73            // Render block
74            render.render_widget(div, area);
75            // Render children
76            if let Some(layout) = self.props.get(Attribute::Layout).map(|x| x.unwrap_layout()) {
77                // make chunks
78                let chunks = layout.chunks(area);
79                // iter chunks
80                for (i, chunk) in chunks.into_iter().enumerate() {
81                    if let Some(child) = self.children.get_mut(i) {
82                        child.view(render, chunk);
83                    }
84                }
85            }
86        }
87    }
88
89    fn query(&self, attr: Attribute) -> Option<AttrValue> {
90        self.props.get(attr)
91    }
92
93    fn attr(&mut self, attr: Attribute, value: AttrValue) {
94        self.props.set(attr, value.clone());
95        // Patch attribute to children
96        self.children
97            .iter_mut()
98            .for_each(|x| x.attr(attr, value.clone()));
99    }
100
101    fn state(&self) -> State {
102        State::None
103    }
104
105    fn perform(&mut self, cmd: Cmd) -> CmdResult {
106        // Send command to children and return batch
107        CmdResult::Batch(self.children.iter_mut().map(|x| x.perform(cmd)).collect())
108    }
109}
110
111#[cfg(test)]
112mod tests {
113
114    use super::*;
115
116    use pretty_assertions::assert_eq;
117
118    #[test]
119    fn test_components_paragraph() {
120        let component = Container::default()
121            .background(Color::Blue)
122            .foreground(Color::Red)
123            .title("title", Alignment::Center);
124        // Get value
125        assert_eq!(component.state(), State::None);
126    }
127}