use core::fmt::Write as _;
use retroglyph_core::{Color, Rect, Style};
use super::{Widget, bar};
use crate::Surface;
use crate::Theme;
#[derive(Clone, Copy, Debug)]
pub struct StatBar<'a> {
label: &'a str,
current: u32,
max: u32,
label_style: Style,
}
impl<'a> StatBar<'a> {
#[must_use]
pub fn new(label: &'a str, current: u32, max: u32) -> Self {
Self {
label,
current,
max,
label_style: bar::default_label_style(),
}
}
#[must_use]
pub const fn label_style(mut self, style: Style) -> Self {
self.label_style = style;
self
}
#[must_use]
pub fn theme(self, theme: Theme) -> Self {
self.theme_on(theme, theme.panel_bg)
}
#[must_use]
pub fn theme_on(mut self, theme: Theme, bg: Color) -> Self {
self.label_style = Style::new().fg(theme.dim).bg(bg);
self
}
}
impl Widget for StatBar<'_> {
fn render(&self, area: Rect, surface: &mut Surface<'_>) {
let ratio = if self.max == 0 {
0.0
} else {
#[allow(clippy::cast_precision_loss)]
{
self.current as f32 / self.max as f32
}
};
let mut readout = bar::ReadoutBuf::<24>::new();
let _ = write!(readout, "{}/{}", self.current, self.max);
bar::render(
surface,
area,
self.label,
self.label_style,
ratio,
readout.as_str(),
);
}
}
#[cfg(test)]
mod tests {
use retroglyph_core::{Grid, Pos};
use super::*;
#[test]
fn zero_max_renders_an_empty_bar_and_zero_zero_readout() {
let area = Rect::new(0, 0, 20, 1);
let mut grid = Grid::new(20, 1);
StatBar::new("H", 0, 0).render(area, &mut Surface::new(&mut grid, area, 0));
assert_eq!(grid[Pos::new(2, 0)].glyph(), '░'); assert_eq!(grid[Pos::new(19, 0)].glyph(), '0'); }
#[test]
fn normal_case_fills_proportionally_and_shows_current_over_max() {
let area = Rect::new(0, 0, 20, 1);
let mut grid = Grid::new(20, 1);
StatBar::new("H", 45, 100).render(area, &mut Surface::new(&mut grid, area, 0));
assert_eq!(grid[Pos::new(2, 0)].glyph(), '█'); assert_eq!(grid[Pos::new(19, 0)].glyph(), '0'); }
#[test]
fn over_max_caps_the_bar_but_shows_true_numbers_in_the_readout() {
let area = Rect::new(0, 0, 20, 1);
let mut grid = Grid::new(20, 1);
StatBar::new("H", 150, 100).render(area, &mut Surface::new(&mut grid, area, 0));
assert_eq!(grid[Pos::new(11, 0)].glyph(), '█');
assert_eq!(grid[Pos::new(19, 0)].glyph(), '0'); }
#[test]
fn label_style_is_configurable() {
use retroglyph_core::Color;
let area = Rect::new(0, 0, 20, 1);
let mut grid = Grid::new(20, 1);
StatBar::new("H", 45, 100)
.label_style(Style::new().fg(Color::WHITE))
.render(area, &mut Surface::new(&mut grid, area, 0));
assert_eq!(grid[Pos::new(0, 0)].style().foreground(), Color::WHITE);
}
#[test]
fn theme_maps_dim_role_onto_label_style() {
let area = Rect::new(0, 0, 20, 1);
let mut grid = Grid::new(20, 1);
StatBar::new("H", 45, 100)
.theme(Theme::DARK)
.render(area, &mut Surface::new(&mut grid, area, 0));
assert_eq!(grid[Pos::new(0, 0)].style().foreground(), Theme::DARK.dim);
assert_eq!(
grid[Pos::new(0, 0)].style().background(),
Theme::DARK.panel_bg
);
}
#[test]
fn theme_on_uses_the_given_backdrop_instead_of_panel_bg() {
let area = Rect::new(0, 0, 20, 1);
let mut grid = Grid::new(20, 1);
StatBar::new("H", 45, 100)
.theme_on(Theme::DARK, Color::Default)
.render(area, &mut Surface::new(&mut grid, area, 0));
assert_eq!(grid[Pos::new(0, 0)].style().foreground(), Theme::DARK.dim);
assert_eq!(grid[Pos::new(0, 0)].style().background(), Color::Default);
}
}