superconsole/components/
draw_vertical.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under both the MIT license found in the
5 * LICENSE-MIT file in the root directory of this source tree and the Apache
6 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
7 * of this source tree.
8 */
9
10use crate::Component;
11use crate::Dimensions;
12use crate::DrawMode;
13use crate::Lines;
14
15/// Draw components vertically, one after the other.
16pub struct DrawVertical {
17    /// Original dimensions.
18    dim: Dimensions,
19    /// What we have drawn so far.
20    lines: Lines,
21}
22
23impl DrawVertical {
24    /// Construct rendered with given dimensions.
25    pub fn new(dimensions: Dimensions) -> DrawVertical {
26        DrawVertical {
27            dim: dimensions,
28            lines: Lines::new(),
29        }
30    }
31
32    /// Add another component.
33    /// New component `draw` is called with remaining dimensions.
34    pub fn draw(&mut self, component: &dyn Component, mode: DrawMode) -> anyhow::Result<()> {
35        // We call `draw` even if no space is left, but maybe we should not.
36        let mut output = component.draw(
37            Dimensions {
38                width: self.dim.width,
39                height: self.dim.height.saturating_sub(self.lines.0.len()),
40            },
41            mode,
42        )?;
43        self.lines.0.append(&mut output.0);
44        Ok(())
45    }
46
47    /// Finish rendering, return the result.
48    pub fn finish(mut self) -> Lines {
49        // This should be no-op, but just in case.
50        self.lines.shrink_lines_to_dimensions(self.dim);
51        self.lines
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::components::DrawVertical;
58    use crate::Component;
59    use crate::Dimensions;
60    use crate::DrawMode;
61    use crate::Line;
62    use crate::Lines;
63
64    #[test]
65    fn test_draw_vertical() {
66        #[derive(Debug)]
67        struct C0;
68        #[derive(Debug)]
69        struct C1;
70
71        impl Component for C0 {
72            fn draw_unchecked(
73                &self,
74
75                dimensions: Dimensions,
76                _mode: DrawMode,
77            ) -> anyhow::Result<Lines> {
78                assert_eq!(
79                    Dimensions {
80                        width: 10,
81                        height: 20
82                    },
83                    dimensions
84                );
85                Ok(Lines(vec![Line::sanitized("foo"), Line::sanitized("bar")]))
86            }
87        }
88
89        impl Component for C1 {
90            fn draw_unchecked(
91                &self,
92
93                dimensions: Dimensions,
94                _mode: DrawMode,
95            ) -> anyhow::Result<Lines> {
96                assert_eq!(
97                    Dimensions {
98                        width: 10,
99                        height: 18
100                    },
101                    dimensions
102                );
103                Ok(Lines(vec![
104                    Line::sanitized("baz"),
105                    Line::sanitized("qux"),
106                    Line::sanitized("quux"),
107                ]))
108            }
109        }
110
111        let mut draw = DrawVertical::new(Dimensions {
112            width: 10,
113            height: 20,
114        });
115        draw.draw(&C0, DrawMode::Normal).unwrap();
116        draw.draw(&C1, DrawMode::Normal).unwrap();
117        let output = draw.finish();
118        assert_eq!(
119            Lines(vec![
120                Line::sanitized("foo"),
121                Line::sanitized("bar"),
122                Line::sanitized("baz"),
123                Line::sanitized("qux"),
124                Line::sanitized("quux"),
125            ]),
126            output
127        );
128    }
129}