use crate::component::{Component, EventCx, MeasureCx};
use crate::event::Event;
use crate::geom::{Rect, Size};
use crate::layout::Constraint;
use crate::render::RenderCx;
use crate::style::Style;
const BLOCKS: &[&str] = &[" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
pub struct ProgressBar {
ratio: f64,
width: u16,
style: Style,
track_style: Style,
}
impl ProgressBar {
pub fn new() -> Self {
Self {
ratio: 0.0,
width: 20,
style: Style::default().fg(crate::style::Color::Cyan),
track_style: Style::default().fg(crate::style::Color::Gray),
}
}
pub fn ratio(mut self, ratio: f64) -> Self {
self.ratio = ratio.clamp(0.0, 1.0);
self
}
pub fn width(mut self, width: u16) -> Self {
self.width = width;
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn track_style(mut self, style: Style) -> Self {
self.track_style = style;
self
}
pub fn set_ratio(&mut self, ratio: f64, cx: &mut EventCx) {
let r = ratio.clamp(0.0, 1.0);
if (self.ratio - r).abs() > f64::EPSILON {
self.ratio = r;
cx.invalidate_paint();
}
}
}
impl Component for ProgressBar {
fn render(&self, cx: &mut RenderCx) {
let filled = (self.ratio * self.width as f64) as u16;
let whole = filled.min(self.width);
let frac = ((self.ratio * self.width as f64) - whole as f64) * 8.0;
let frac_idx = (frac as usize).min(BLOCKS.len() - 1);
if whole > 0 {
cx.set_style(self.style.clone());
cx.text("█".repeat(whole as usize));
}
if whole < self.width {
if frac_idx > 0 {
cx.set_style(self.style.clone());
cx.text(BLOCKS[frac_idx]);
}
let frac_used = if frac_idx > 0 { 1 } else { 0 };
let remaining = self.width.saturating_sub(whole).saturating_sub(frac_used);
if remaining > 0 {
cx.set_style(self.track_style.clone());
cx.text("░".repeat(remaining as usize));
}
}
cx.set_style(self.track_style.clone());
cx.line("");
}
fn measure(&self, _constraint: Constraint, _cx: &mut MeasureCx) -> Size {
Size { width: self.width, height: 1 }
}
fn event(&mut self, _event: &Event, _cx: &mut EventCx) {}
fn layout(&mut self, _rect: Rect, _cx: &mut crate::component::LayoutCx) {}
fn focusable(&self) -> bool { false }
fn style(&self) -> Style { self.style.clone() }
}