1use std::marker::PhantomData;
2
3use crate::{
4 buffer::Buffer,
5 enums::{Color, RGB},
6 geometry::{Constraint, Direction, Padding, Rect, Vec2},
7 style::Style,
8 widgets::cache::Cache,
9};
10
11use super::{widget::Widget, Element, Layout, Spacer};
12
13#[derive(Debug)]
37pub struct BgGrad<W = Element> {
38 bg_start: RGB,
39 bg_end: RGB,
40 direction: Direction,
41 padding: Padding,
42 child: Element,
43 child_type: PhantomData<W>,
44}
45
46impl BgGrad<Spacer> {
47 #[must_use]
66 pub fn new<T1, T2>(dir: Direction, start: T1, end: T2) -> Self
67 where
68 T1: Into<RGB>,
69 T2: Into<RGB>,
70 {
71 Self::construct(start.into(), end.into(), dir, Spacer::new())
72 }
73
74 #[must_use]
88 pub fn vertical<T1, T2>(start: T1, end: T2) -> Self
89 where
90 T1: Into<RGB>,
91 T2: Into<RGB>,
92 {
93 Self::construct(
94 start.into(),
95 end.into(),
96 Direction::Vertical,
97 Spacer::new(),
98 )
99 }
100
101 #[must_use]
116 pub fn horizontal<T1, T2>(start: T1, end: T2) -> Self
117 where
118 T1: Into<RGB>,
119 T2: Into<RGB>,
120 {
121 Self::construct(
122 start.into(),
123 end.into(),
124 Direction::Horizontal,
125 Spacer::new(),
126 )
127 }
128}
129
130impl<W> BgGrad<W> {
131 #[must_use]
141 pub fn child<CW>(self, child: CW) -> BgGrad<CW>
142 where
143 CW: Into<Element>,
144 {
145 BgGrad {
146 bg_start: self.bg_start,
147 bg_end: self.bg_end,
148 direction: self.direction,
149 padding: self.padding,
150 child: child.into(),
151 child_type: PhantomData,
152 }
153 }
154}
155
156impl<W> BgGrad<W>
157where
158 W: Widget,
159{
160 #[must_use]
164 pub fn bg_dir(mut self, direction: Direction) -> Self {
165 self.direction = direction;
166 self
167 }
168
169 #[must_use]
174 pub fn padding<T>(mut self, padding: T) -> Self
175 where
176 T: Into<Padding>,
177 {
178 self.padding = padding.into();
179 self
180 }
181
182 fn construct(start: RGB, end: RGB, dir: Direction, child: W) -> Self {
183 Self {
184 bg_start: start,
185 bg_end: end,
186 direction: dir,
187 padding: Default::default(),
188 child: Element::new(child),
189 child_type: PhantomData,
190 }
191 }
192}
193
194impl BgGrad<Layout> {
195 #[must_use]
197 pub fn direction(mut self, direction: Direction) -> Self {
198 self.child = self.child.map::<Layout, _>(|l| l.direction(direction));
199 self
200 }
201
202 #[must_use]
204 pub fn style<T>(mut self, style: T) -> Self
205 where
206 T: Into<Style>,
207 {
208 self.child = self.child.map::<Layout, _>(|l| l.style(style));
209 self
210 }
211
212 #[must_use]
214 pub fn bg<T>(mut self, bg: T) -> Self
215 where
216 T: Into<Option<Color>>,
217 {
218 self.child = self.child.map::<Layout, _>(|l| l.bg(bg));
219 self
220 }
221
222 #[must_use]
224 pub fn fg<T>(mut self, fg: T) -> Self
225 where
226 T: Into<Option<Color>>,
227 {
228 self.child = self.child.map::<Layout, _>(|l| l.fg(fg));
229 self
230 }
231
232 #[must_use]
237 pub fn center(mut self) -> Self {
238 self.child = self.child.map::<Layout, _>(|l| l.center());
239 self
240 }
241
242 #[deprecated(
244 since = "0.6.0",
245 note = "Kept for compatibility purposes; use `push` function instead"
246 )]
247 pub fn add_child<T, C>(&mut self, child: T, constraint: C)
248 where
249 T: Into<Element>,
250 C: Into<Constraint>,
251 {
252 if let Some(layout) = self.child.downcast_mut::<Layout>() {
253 layout.push(child, constraint);
254 }
255 }
256
257 pub fn push<T, C>(&mut self, child: T, constraint: C)
264 where
265 T: Into<Element>,
266 C: Into<Constraint>,
267 {
268 if let Some(layout) = self.child.downcast_mut::<Layout>() {
269 layout.push(child, constraint);
270 }
271 }
272}
273
274impl<W> Widget for BgGrad<W>
275where
276 W: Widget,
277{
278 fn render(&self, buffer: &mut Buffer, rect: Rect, cache: &mut Cache) {
279 if rect.is_empty() {
280 return;
281 }
282
283 match self.direction {
284 Direction::Vertical => self.ver_render(buffer, &rect),
285 Direction::Horizontal => self.hor_render(buffer, &rect),
286 };
287 self.child.render(
288 buffer,
289 rect.inner(self.padding),
290 &mut cache.children[0],
291 );
292 }
293
294 fn height(&self, size: &Vec2) -> usize {
295 let size = Vec2::new(
296 size.x.saturating_sub(self.padding.get_horizontal()),
297 size.y.saturating_sub(self.padding.get_vertical()),
298 );
299 self.child.height(&size) + self.padding.get_vertical()
300 }
301
302 fn width(&self, size: &Vec2) -> usize {
303 let size = Vec2::new(
304 size.x.saturating_sub(self.padding.get_horizontal()),
305 size.y.saturating_sub(self.padding.get_vertical()),
306 );
307 self.child.width(&size) + self.padding.get_horizontal()
308 }
309
310 fn children(&self) -> Vec<&Element> {
311 vec![&self.child]
312 }
313}
314
315impl<W> BgGrad<W>
316where
317 W: Widget,
318{
319 fn hor_render(&self, buffer: &mut Buffer, rect: &Rect) {
321 let step = self.get_step(rect.width() as i16 - 1);
322 let (mut r, mut g, mut b) =
323 (self.bg_start.r, self.bg_start.g, self.bg_start.b);
324
325 for x in rect.x()..rect.width() + rect.x() {
326 let bg = Color::Rgb(r, g, b);
327 (r, g, b) = self.add_step((r, g, b), step);
328
329 for y in rect.y()..rect.height() + rect.y() {
330 buffer.set_bg(bg, &Vec2::new(x, y));
331 }
332 }
333 }
334
335 fn ver_render(&self, buffer: &mut Buffer, rect: &Rect) {
337 let step = self.get_step(rect.height() as i16 - 1);
338 let (mut r, mut g, mut b) =
339 (self.bg_start.r, self.bg_start.g, self.bg_start.b);
340
341 for y in rect.y()..rect.height() + rect.y() {
342 let bg = Color::Rgb(r, g, b);
343 (r, g, b) = self.add_step((r, g, b), step);
344
345 for x in rect.x()..rect.width() + rect.x() {
346 buffer.set_bg(bg, &Vec2::new(x, y));
347 }
348 }
349 }
350
351 fn get_step(&self, len: i16) -> (i16, i16, i16) {
353 if len <= 0 {
354 return (0, 0, 0);
355 }
356 (
357 (self.bg_end.r as i16 - self.bg_start.r as i16) / len,
358 (self.bg_end.g as i16 - self.bg_start.g as i16) / len,
359 (self.bg_end.b as i16 - self.bg_start.b as i16) / len,
360 )
361 }
362
363 fn add_step(
365 &self,
366 rgb: (u8, u8, u8),
367 step: (i16, i16, i16),
368 ) -> (u8, u8, u8) {
369 (
370 (rgb.0 as i16 + step.0) as u8,
371 (rgb.1 as i16 + step.1) as u8,
372 (rgb.2 as i16 + step.2) as u8,
373 )
374 }
375}
376
377impl<W> From<BgGrad<W>> for Box<dyn Widget>
379where
380 W: Widget + 'static,
381{
382 fn from(value: BgGrad<W>) -> Self {
383 Box::new(value)
384 }
385}
386
387impl<W> From<BgGrad<W>> for Element
388where
389 W: Widget + 'static,
390{
391 fn from(value: BgGrad<W>) -> Self {
392 Element::new(value)
393 }
394}