1use alloc::borrow::Cow;
3
4use ratatui_core::buffer::Buffer;
5use ratatui_core::layout::Rect;
6use ratatui_core::style::{Style, Styled};
7use ratatui_core::widgets::Widget;
8
9#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
51pub struct Fill<'a> {
52 symbol: Cow<'a, str>,
53 style: Style,
54}
55
56impl<'a> Fill<'a> {
57 pub fn new<S: Into<Cow<'a, str>>>(symbol: S) -> Self {
64 Self {
65 symbol: symbol.into(),
66 style: Style::default(),
67 }
68 }
69
70 #[must_use = "method moves the value of self and returns the modified value"]
78 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
79 self.style = style.into();
80 self
81 }
82
83 #[must_use = "method moves the value of self and returns the modified value"]
87 pub fn symbol<S: Into<Cow<'a, str>>>(mut self, symbol: S) -> Self {
88 self.symbol = symbol.into();
89 self
90 }
91}
92
93impl Widget for Fill<'_> {
94 fn render(self, area: Rect, buf: &mut Buffer) {
95 Widget::render(&self, area, buf);
96 }
97}
98
99impl Widget for &Fill<'_> {
100 fn render(self, area: Rect, buf: &mut Buffer) {
101 let area = area.intersection(*buf.area());
102 if area.is_empty() {
103 return;
104 }
105 for position in area.positions() {
106 buf[position].set_symbol(&self.symbol).set_style(self.style);
107 }
108 }
109}
110
111impl Styled for Fill<'_> {
112 type Item = Self;
113
114 fn style(&self) -> Style {
115 self.style
116 }
117
118 fn set_style<S: Into<Style>>(self, style: S) -> Self::Item {
119 self.style(style)
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use alloc::string::String;
126
127 use ratatui_core::buffer::Buffer;
128 use ratatui_core::layout::Rect;
129 use ratatui_core::style::{Color, Style, Stylize};
130 use ratatui_core::widgets::Widget;
131
132 use super::*;
133
134 #[test]
135 #[rustfmt::skip]
136 fn fills_area_with_symbol_and_style() {
137 let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 3));
138 Fill::new(".")
139 .style(Style::new().fg(Color::Red))
140 .render(Rect::new(1, 1, 3, 1), &mut buffer);
141
142 let mut expected = Buffer::with_lines([
143 " ",
144 " ... ",
145 " ",
146 ]);
147 for x in 1..=3 {
148 expected[(x, 1)].set_style(Style::new().fg(Color::Red));
149 }
150 assert_eq!(buffer, expected);
151 }
152
153 #[test]
154 fn clips_area_to_buffer() {
155 let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 2));
157 Fill::new("x").render(Rect::new(1, 1, 100, 100), &mut buffer);
158 assert_eq!(buffer, Buffer::with_lines([" ", " xx"]));
159 }
160
161 #[test]
162 fn render_fully_out_of_bounds_is_noop() {
163 let mut buffer = Buffer::with_lines(["xxxxx"; 3]);
164 Fill::new(".").render(Rect::new(100, 100, 5, 5), &mut buffer);
165 assert_eq!(buffer, Buffer::with_lines(["xxxxx"; 3]));
166 }
167
168 #[test]
169 fn renders_with_offset_buffer_area() {
170 let mut buffer = Buffer::empty(Rect::new(2, 2, 2, 2));
172 Fill::new("#").render(Rect::new(0, 0, 4, 4), &mut buffer);
173 let mut expected = Buffer::empty(Rect::new(2, 2, 2, 2));
174 for y in 2..4 {
175 for x in 2..4 {
176 expected[(x, y)].set_symbol("#");
177 }
178 }
179 assert_eq!(buffer, expected);
180 }
181
182 #[test]
183 fn stylize_shorthand_works() {
184 let mut buffer = Buffer::empty(Rect::new(0, 0, 2, 1));
186 Fill::new("*")
187 .blue()
188 .bold()
189 .render(Rect::new(0, 0, 2, 1), &mut buffer);
190 let mut expected = Buffer::with_lines(["**"]);
191 for x in 0..2 {
192 expected[(x, 0)].set_style(Style::new().fg(Color::Blue).bold());
193 }
194 assert_eq!(buffer, expected);
195 }
196
197 #[test]
198 fn accepts_owned_string_symbol() {
199 let mut buffer = Buffer::empty(Rect::new(0, 0, 2, 1));
200 Fill::new(String::from("•")).render(Rect::new(0, 0, 2, 1), &mut buffer);
201 assert_eq!(buffer, Buffer::with_lines(["••"]));
202 }
203
204 #[test]
205 fn symbol_setter_replaces_symbol() {
206 let mut buffer = Buffer::empty(Rect::new(0, 0, 2, 1));
207 Fill::new("a")
208 .symbol("b")
209 .render(Rect::new(0, 0, 2, 1), &mut buffer);
210 assert_eq!(buffer, Buffer::with_lines(["bb"]));
211 }
212}