ratatui/widgets/canvas/
rectangle.rs

1use crate::{
2    style::Color,
3    widgets::canvas::{Line, Painter, Shape},
4};
5
6/// A rectangle to draw on a [`Canvas`](crate::widgets::canvas::Canvas)
7///
8/// Sizes used here are **not** in terminal cell. This is much more similar to the
9/// mathematic coordinate system.
10#[derive(Debug, Default, Clone, PartialEq)]
11pub struct Rectangle {
12    /// The `x` position of the rectangle.
13    ///
14    /// The rectangle is positioned from its bottom left corner.
15    pub x: f64,
16    /// The `y` position of the rectangle.
17    ///
18    /// The rectangle is positioned from its bottom left corner.
19    pub y: f64,
20    /// The width of the rectangle.
21    pub width: f64,
22    /// The height of the rectangle.
23    pub height: f64,
24    /// The color of the rectangle.
25    pub color: Color,
26}
27
28impl Shape for Rectangle {
29    fn draw(&self, painter: &mut Painter) {
30        let lines: [Line; 4] = [
31            Line {
32                x1: self.x,
33                y1: self.y,
34                x2: self.x,
35                y2: self.y + self.height,
36                color: self.color,
37            },
38            Line {
39                x1: self.x,
40                y1: self.y + self.height,
41                x2: self.x + self.width,
42                y2: self.y + self.height,
43                color: self.color,
44            },
45            Line {
46                x1: self.x + self.width,
47                y1: self.y,
48                x2: self.x + self.width,
49                y2: self.y + self.height,
50                color: self.color,
51            },
52            Line {
53                x1: self.x,
54                y1: self.y,
55                x2: self.x + self.width,
56                y2: self.y,
57                color: self.color,
58            },
59        ];
60        for line in &lines {
61            line.draw(painter);
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::{
70        buffer::Buffer,
71        layout::{Margin, Rect},
72        style::{Style, Stylize},
73        symbols::Marker,
74        widgets::{canvas::Canvas, Widget},
75    };
76
77    #[test]
78    fn draw_block_lines() {
79        let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
80        let canvas = Canvas::default()
81            .marker(Marker::Block)
82            .x_bounds([0.0, 10.0])
83            .y_bounds([0.0, 10.0])
84            .paint(|context| {
85                context.draw(&Rectangle {
86                    x: 0.0,
87                    y: 0.0,
88                    width: 10.0,
89                    height: 10.0,
90                    color: Color::Red,
91                });
92            });
93        canvas.render(buffer.area, &mut buffer);
94        let mut expected = Buffer::with_lines([
95            "██████████",
96            "█        █",
97            "█        █",
98            "█        █",
99            "█        █",
100            "█        █",
101            "█        █",
102            "█        █",
103            "█        █",
104            "██████████",
105        ]);
106        expected.set_style(buffer.area, Style::new().red());
107        expected.set_style(buffer.area.inner(Margin::new(1, 1)), Style::reset());
108        assert_eq!(buffer, expected);
109    }
110
111    #[test]
112    fn draw_half_block_lines() {
113        let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
114        let canvas = Canvas::default()
115            .marker(Marker::HalfBlock)
116            .x_bounds([0.0, 10.0])
117            .y_bounds([0.0, 10.0])
118            .paint(|context| {
119                context.draw(&Rectangle {
120                    x: 0.0,
121                    y: 0.0,
122                    width: 10.0,
123                    height: 10.0,
124                    color: Color::Red,
125                });
126            });
127        canvas.render(buffer.area, &mut buffer);
128        let mut expected = Buffer::with_lines([
129            "█▀▀▀▀▀▀▀▀█",
130            "█        █",
131            "█        █",
132            "█        █",
133            "█        █",
134            "█        █",
135            "█        █",
136            "█        █",
137            "█        █",
138            "█▄▄▄▄▄▄▄▄█",
139        ]);
140        expected.set_style(buffer.area, Style::new().red().on_red());
141        expected.set_style(buffer.area.inner(Margin::new(1, 0)), Style::reset().red());
142        expected.set_style(buffer.area.inner(Margin::new(1, 1)), Style::reset());
143        assert_eq!(buffer, expected);
144    }
145
146    #[test]
147    fn draw_braille_lines() {
148        let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
149        let canvas = Canvas::default()
150            .marker(Marker::Braille)
151            .x_bounds([0.0, 10.0])
152            .y_bounds([0.0, 10.0])
153            .paint(|context| {
154                // a rectangle that will draw the outside part of the braille
155                context.draw(&Rectangle {
156                    x: 0.0,
157                    y: 0.0,
158                    width: 10.0,
159                    height: 10.0,
160                    color: Color::Red,
161                });
162                // a rectangle that will draw the inside part of the braille
163                context.draw(&Rectangle {
164                    x: 2.0,
165                    y: 1.75,
166                    width: 6.5,
167                    height: 6.5,
168                    color: Color::Green,
169                });
170            });
171        canvas.render(buffer.area, &mut buffer);
172        let mut expected = Buffer::with_lines([
173            "⡏⠉⠉⠉⠉⠉⠉⠉⠉⢹",
174            "⡇⢠⠤⠤⠤⠤⠤⠤⡄⢸",
175            "⡇⢸      ⡇⢸",
176            "⡇⢸      ⡇⢸",
177            "⡇⢸      ⡇⢸",
178            "⡇⢸      ⡇⢸",
179            "⡇⢸      ⡇⢸",
180            "⡇⢸      ⡇⢸",
181            "⡇⠈⠉⠉⠉⠉⠉⠉⠁⢸",
182            "⣇⣀⣀⣀⣀⣀⣀⣀⣀⣸",
183        ]);
184        expected.set_style(buffer.area, Style::new().red());
185        expected.set_style(buffer.area.inner(Margin::new(1, 1)), Style::new().green());
186        expected.set_style(buffer.area.inner(Margin::new(2, 2)), Style::reset());
187        assert_eq!(buffer, expected);
188    }
189}