1use ratatui_core::style::Color;
2
3use crate::canvas::{Line, Painter, Shape};
4
5#[derive(Debug, Default, Clone, PartialEq)]
10pub struct Rectangle {
11 pub x: f64,
15 pub y: f64,
19 pub width: f64,
21 pub height: f64,
23 pub color: Color,
25}
26
27impl Rectangle {
28 pub const fn new(x: f64, y: f64, width: f64, height: f64, color: Color) -> Self {
30 Self {
31 x,
32 y,
33 width,
34 height,
35 color,
36 }
37 }
38}
39
40impl Shape for Rectangle {
41 fn draw(&self, painter: &mut Painter) {
42 let lines: [Line; 4] = [
43 Line {
44 x1: self.x,
45 y1: self.y,
46 x2: self.x,
47 y2: self.y + self.height,
48 color: self.color,
49 },
50 Line {
51 x1: self.x,
52 y1: self.y + self.height,
53 x2: self.x + self.width,
54 y2: self.y + self.height,
55 color: self.color,
56 },
57 Line {
58 x1: self.x + self.width,
59 y1: self.y,
60 x2: self.x + self.width,
61 y2: self.y + self.height,
62 color: self.color,
63 },
64 Line {
65 x1: self.x,
66 y1: self.y,
67 x2: self.x + self.width,
68 y2: self.y,
69 color: self.color,
70 },
71 ];
72 for line in &lines {
73 line.draw(painter);
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use ratatui_core::buffer::Buffer;
81 use ratatui_core::layout::{Margin, Rect};
82 use ratatui_core::style::Style;
83 use ratatui_core::symbols::Marker;
84 use ratatui_core::widgets::Widget;
85
86 use super::*;
87 use crate::canvas::Canvas;
88
89 #[test]
90 fn draw_block_lines() {
91 let mut expected = Buffer::with_lines([
92 "██████████",
93 "█ █",
94 "█ █",
95 "█ █",
96 "█ █",
97 "█ █",
98 "█ █",
99 "█ █",
100 "█ █",
101 "██████████",
102 ]);
103 let rect = Rect::new(0, 0, 10, 10);
104 expected.set_style(rect, Style::new().red().on_red());
105 expected.set_style(rect.inner(Margin::new(1, 1)), Style::reset());
106 let rendered = render_rectangle(Marker::Block, rect);
107 assert_eq!(expected, rendered);
108 }
109
110 #[test]
111 fn draw_half_block_lines() {
112 let mut expected = Buffer::with_lines([
113 "█▀▀▀▀▀▀▀▀█",
114 "█ █",
115 "█ █",
116 "█ █",
117 "█ █",
118 "█ █",
119 "█ █",
120 "█ █",
121 "█ █",
122 "█▄▄▄▄▄▄▄▄█",
123 ]);
124 let rect = Rect::new(0, 0, 10, 10);
125 expected.set_style(rect, Style::new().red().on_red());
126 expected.set_style(rect.inner(Margin::new(1, 0)), Style::reset().red());
127 expected.set_style(rect.inner(Margin::new(1, 1)), Style::reset());
128 let rendered = render_rectangle(Marker::HalfBlock, rect);
129 assert_eq!(expected, rendered);
130 }
131
132 #[test]
133 fn draw_braille_lines() {
134 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
135 let canvas = Canvas::default()
136 .marker(Marker::Braille)
137 .x_bounds([0.0, 20.0])
138 .y_bounds([0.0, 20.0])
139 .paint(|context| {
140 context.draw(&Rectangle {
142 x: 0.0,
143 y: 0.0,
144 width: 20.0,
145 height: 20.0,
146 color: Color::Red,
147 });
148 context.draw(&Rectangle {
150 x: 4.0,
151 y: 4.0,
152 width: 12.0,
153 height: 12.0,
154 color: Color::Green,
155 });
156 });
157 canvas.render(buffer.area, &mut buffer);
158 let mut expected = Buffer::with_lines([
159 "⡏⠉⠉⠉⠉⠉⠉⠉⠉⢹",
160 "⡇ ⢸",
161 "⡇ ⡏⠉⠉⠉⠉⢹ ⢸",
162 "⡇ ⡇ ⢸ ⢸",
163 "⡇ ⡇ ⢸ ⢸",
164 "⡇ ⡇ ⢸ ⢸",
165 "⡇ ⡇ ⢸ ⢸",
166 "⡇ ⣇⣀⣀⣀⣀⣸ ⢸",
167 "⡇ ⢸",
168 "⣇⣀⣀⣀⣀⣀⣀⣀⣀⣸",
169 ]);
170 expected.set_style(buffer.area, Style::new().red());
171 expected.set_style(buffer.area.inner(Margin::new(1, 1)), Style::reset());
172 expected.set_style(buffer.area.inner(Margin::new(2, 2)), Style::new().green());
173 expected.set_style(buffer.area.inner(Margin::new(3, 3)), Style::reset());
174 assert_eq!(buffer, expected);
175 }
176
177 #[test]
178 fn draw_x_lines() {
179 let mut expected = Buffer::with_lines([
180 "××××××××××",
181 "× ×",
182 "× ×",
183 "× ×",
184 "× ×",
185 "× ×",
186 "× ×",
187 "× ×",
188 "× ×",
189 "××××××××××",
190 ]);
191 let rect = Rect::new(0, 0, 10, 10);
192 expected.set_style(rect, Style::new().red());
193 expected.set_style(rect.inner(Margin::new(1, 0)), Style::reset().red());
194 expected.set_style(rect.inner(Margin::new(1, 1)), Style::reset());
195 let rendered = render_rectangle(Marker::Custom('×'), rect);
196 assert_eq!(expected, rendered);
197 }
198
199 #[test]
200 fn draw_plus_lines() {
201 let mut expected = Buffer::with_lines([
202 "++++++++++",
203 "+ +",
204 "+ +",
205 "+ +",
206 "+ +",
207 "+ +",
208 "+ +",
209 "+ +",
210 "+ +",
211 "++++++++++",
212 ]);
213 let rect = Rect::new(0, 0, 10, 10);
214 expected.set_style(rect, Style::new().red());
215 expected.set_style(rect.inner(Margin::new(1, 0)), Style::reset().red());
216 expected.set_style(rect.inner(Margin::new(1, 1)), Style::reset());
217 let rendered = render_rectangle(Marker::Custom('+'), rect);
218 assert_eq!(expected, rendered);
219 }
220
221 fn render_rectangle(marker: Marker, rect: Rect) -> Buffer {
222 let mut buffer = Buffer::empty(rect);
223 let canvas = Canvas::default()
224 .marker(marker)
225 .x_bounds([0.0, 10.0])
226 .y_bounds([0.0, 10.0])
227 .paint(|context| {
228 context.draw(&Rectangle {
229 x: 0.0,
230 y: 0.0,
231 width: 10.0,
232 height: 10.0,
233 color: Color::Red,
234 });
235 });
236 canvas.render(buffer.area, &mut buffer);
237 buffer
238 }
239}