use std::iter::zip;
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::{Constraint, Layout, Rect};
use ratatui_core::style::Color;
use ratatui_core::symbols;
use ratatui_core::widgets::Widget;
#[derive(Debug, Clone)]
pub struct Equalizer {
pub bands: Vec<Band>,
pub brightness: f64,
}
#[derive(Debug, Clone)]
pub struct Band {
pub value: f64,
}
impl From<f64> for Band {
fn from(value: f64) -> Self {
Self { value }
}
}
impl Widget for Equalizer {
fn render(self, area: Rect, buf: &mut Buffer) {
(&self).render(area, buf);
}
}
impl Widget for &Equalizer {
fn render(self, area: Rect, buf: &mut Buffer) {
let areas = Layout::horizontal(vec![Constraint::Length(2); self.bands.len()]).split(area);
for (band, area) in zip(&self.bands, areas.iter()) {
band.render(*area, buf, self.brightness);
}
}
}
impl Band {
fn render(&self, area: Rect, buf: &mut Buffer, brightness: f64) {
let value = self.value.clamp(0.0, 1.0);
let height = (value * area.height as f64) as u16;
let color_step = 1.0 / area.height as f64;
for i in 0..height {
let v = i as f64 * color_step;
let vv = 1.0 - v;
let br = brightness.clamp(0.0, 1.0) * 255.0;
let r = if v < 0.5 { v * 2.0 * br } else { br } as u8;
let g = if v < 0.5 { br } else { vv * 2.0 * br } as u8;
let b = 0;
let color = Color::Rgb(r, g, b);
buf[(area.left(), area.bottom().saturating_sub(i + 1))]
.set_fg(color)
.set_symbol(symbols::bar::HALF);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn render_by_reference_matches_owned_render() {
let equalizer = Equalizer {
bands: vec![Band::from(0.25), Band::from(0.5), Band::from(1.0)],
brightness: 1.0,
};
let mut owned_buf = Buffer::empty(Rect::new(0, 0, 6, 4));
let mut borrowed_buf = Buffer::empty(Rect::new(0, 0, 6, 4));
equalizer.clone().render(owned_buf.area, &mut owned_buf);
(&equalizer).render(borrowed_buf.area, &mut borrowed_buf);
assert_eq!(borrowed_buf, owned_buf);
}
}