use std::hash::{DefaultHasher, Hash, Hasher};
use crate::{
buffer::Buffer,
enums::{Color, RGB},
geometry::Padding,
prelude::{Direction, Rect, Vec2},
text::get_step,
widgets::{Element, LayoutNode, Spacer, Widget},
};
#[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) {
node.children[0].layout(&self.child, area.inner(self.padding));
}
}
impl<M> BgGrad<M> {
fn hor_render(&self, buffer: &mut Buffer, rect: &Rect) {
let ((mut r, mut g, mut b), (rs, gs, bs)) =
get_step(&self.bg_start, &self.bg_end, rect.width());
for x in rect.x()..rect.width() + rect.x() {
let bg = Color::Rgb(r as u8, g as u8, b as u8);
(r, g, b) = (r + rs, g + gs, b + bs);
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 ((mut r, mut g, mut b), (rs, gs, bs)) =
get_step(&self.bg_start, &self.bg_end, rect.height());
for y in rect.y()..rect.height() + rect.y() {
let bg = Color::Rgb(r as u8, g as u8, b as u8);
(r, g, b) = (r + rs, g + gs, b + bs);
for x in rect.x()..rect.width() + rect.x() {
buffer.set_bg(bg, &Vec2::new(x, y));
}
}
}
}
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)
}
}