use std::hash::{DefaultHasher, Hash, Hasher};
use crate::{
buffer::Buffer,
enums::{Color, RGB},
geometry::Padding,
prelude::{Direction, Rect, Vec2},
widgets::{Element, LayoutNode, Spacer, Widget, layout},
};
#[derive(Debug)]
pub struct BgGrad<M: 'static = ()> {
bg_start: RGB,
bg_end: RGB,
direction: Direction,
padding: Padding,
child: Element<M>,
}
impl<M: Clone + 'static> BgGrad<M> {
#[must_use]
pub fn new<T1, T2>(dir: Direction, start: T1, end: T2) -> Self
where
T1: Into<RGB>,
T2: Into<RGB>,
{
Self::construct(start.into(), end.into(), dir, Spacer::new())
}
#[must_use]
pub fn vertical<T1, T2>(start: T1, end: T2) -> Self
where
T1: Into<RGB>,
T2: Into<RGB>,
{
Self::construct(
start.into(),
end.into(),
Direction::Vertical,
Spacer::new(),
)
}
#[must_use]
pub fn horizontal<T1, T2>(start: T1, end: T2) -> Self
where
T1: Into<RGB>,
T2: Into<RGB>,
{
Self::construct(
start.into(),
end.into(),
Direction::Horizontal,
Spacer::new(),
)
}
fn construct<W>(start: RGB, end: RGB, dir: Direction, child: W) -> Self
where
W: Widget<M>,
{
Self {
bg_start: start,
bg_end: end,
direction: dir,
padding: Default::default(),
child: Element::new(child),
}
}
}
impl<M> BgGrad<M> {
#[must_use]
pub fn child<I>(mut self, child: I) -> Self
where
I: Into<Element<M>>,
{
self.child = child.into();
self
}
#[deprecated(
since = "0.7.0",
note = "Replaced by `BgGrad::direction` for clarity."
)]
#[must_use]
pub fn bg_dir(mut self, direction: Direction) -> Self {
self.direction = direction;
self
}
#[must_use]
pub fn direction(mut self, direction: Direction) -> Self {
self.direction = direction;
self
}
#[must_use]
pub fn padding<T>(mut self, padding: T) -> Self
where
T: Into<Padding>,
{
self.padding = padding.into();
self
}
}
impl<M> Widget<M> for BgGrad<M>
where
M: Clone + 'static,
{
fn render(&self, buffer: &mut Buffer, layout: &LayoutNode) {
let rect = layout.area;
if rect.is_empty() {
return;
}
match self.direction {
Direction::Vertical => self.ver_render(buffer, &rect),
Direction::Horizontal => self.hor_render(buffer, &rect),
};
self.child.render(buffer, &layout.children[0]);
}
fn height(&self, size: &Vec2) -> usize {
let size = Vec2::new(
size.x.saturating_sub(self.padding.get_horizontal()),
size.y.saturating_sub(self.padding.get_vertical()),
);
self.child.height(&size) + self.padding.get_vertical()
}
fn width(&self, size: &Vec2) -> usize {
let size = Vec2::new(
size.x.saturating_sub(self.padding.get_horizontal()),
size.y.saturating_sub(self.padding.get_vertical()),
);
self.child.width(&size) + self.padding.get_horizontal()
}
fn children(&self) -> Vec<&Element<M>> {
vec![&self.child]
}
fn layout_hash(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.padding.hash(&mut hasher);
hasher.finish()
}
fn layout(&self, node: &mut LayoutNode, area: Rect) {
layout::padded(node, area, self.padding, |n, a| {
self.child.layout(&mut n.children[0], a)
});
}
}
impl<M> BgGrad<M> {
fn hor_render(&self, buffer: &mut Buffer, rect: &Rect) {
let step = self.get_step(rect.width() as i16 - 1);
let (mut r, mut g, mut b) =
(self.bg_start.r, self.bg_start.g, self.bg_start.b);
for x in rect.x()..rect.width() + rect.x() {
let bg = Color::Rgb(r, g, b);
(r, g, b) = self.add_step((r, g, b), step);
for y in rect.y()..rect.height() + rect.y() {
buffer.set_bg(bg, &Vec2::new(x, y));
}
}
}
fn ver_render(&self, buffer: &mut Buffer, rect: &Rect) {
let step = self.get_step(rect.height() as i16 - 1);
let (mut r, mut g, mut b) =
(self.bg_start.r, self.bg_start.g, self.bg_start.b);
for y in rect.y()..rect.height() + rect.y() {
let bg = Color::Rgb(r, g, b);
(r, g, b) = self.add_step((r, g, b), step);
for x in rect.x()..rect.width() + rect.x() {
buffer.set_bg(bg, &Vec2::new(x, y));
}
}
}
fn get_step(&self, len: i16) -> (i16, i16, i16) {
if len <= 0 {
return (0, 0, 0);
}
(
(self.bg_end.r as i16 - self.bg_start.r as i16) / len,
(self.bg_end.g as i16 - self.bg_start.g as i16) / len,
(self.bg_end.b as i16 - self.bg_start.b as i16) / len,
)
}
fn add_step(
&self,
rgb: (u8, u8, u8),
step: (i16, i16, i16),
) -> (u8, u8, u8) {
(
(rgb.0 as i16 + step.0) as u8,
(rgb.1 as i16 + step.1) as u8,
(rgb.2 as i16 + step.2) as u8,
)
}
}
impl<M> From<BgGrad<M>> for Box<dyn Widget<M>>
where
M: Clone + 'static,
{
fn from(value: BgGrad<M>) -> Self {
Box::new(value)
}
}
impl<M> From<BgGrad<M>> for Element<M>
where
M: Clone + 'static,
{
fn from(value: BgGrad<M>) -> Self {
Element::new(value)
}
}