lv_tui/widgets/
progressbar.rs1use crate::component::{Component, EventCx, MeasureCx};
2use crate::event::Event;
3use crate::geom::{Rect, Size};
4use crate::layout::Constraint;
5use crate::render::RenderCx;
6use crate::style::Style;
7
8const BLOCKS: &[&str] = &[" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
9
10pub struct ProgressBar {
16 ratio: f64,
17 width: u16,
18 style: Style,
19 track_style: Style,
20}
21
22impl ProgressBar {
23 pub fn new() -> Self {
25 Self {
26 ratio: 0.0,
27 width: 20,
28 style: Style::default().fg(crate::style::Color::Cyan),
29 track_style: Style::default().fg(crate::style::Color::Gray),
30 }
31 }
32
33 pub fn ratio(mut self, ratio: f64) -> Self {
35 self.ratio = ratio.clamp(0.0, 1.0);
36 self
37 }
38
39 pub fn width(mut self, width: u16) -> Self {
41 self.width = width;
42 self
43 }
44
45 pub fn style(mut self, style: Style) -> Self {
47 self.style = style;
48 self
49 }
50
51 pub fn track_style(mut self, style: Style) -> Self {
53 self.track_style = style;
54 self
55 }
56
57 pub fn set_ratio(&mut self, ratio: f64, cx: &mut EventCx) {
59 let r = ratio.clamp(0.0, 1.0);
60 if (self.ratio - r).abs() > f64::EPSILON {
61 self.ratio = r;
62 cx.invalidate_paint();
63 }
64 }
65}
66
67impl Component for ProgressBar {
68 fn render(&self, cx: &mut RenderCx) {
69 let filled = (self.ratio * self.width as f64) as u16;
70 let whole = filled.min(self.width);
71 let frac = ((self.ratio * self.width as f64) - whole as f64) * 8.0;
72 let frac_idx = (frac as usize).min(BLOCKS.len() - 1);
73
74 if whole > 0 {
76 cx.set_style(self.style.clone());
77 cx.text("█".repeat(whole as usize));
78 }
79
80 if whole < self.width {
82 if frac_idx > 0 {
83 cx.set_style(self.style.clone());
84 cx.text(BLOCKS[frac_idx]);
85 }
86 let frac_used = if frac_idx > 0 { 1 } else { 0 };
87 let remaining = self.width.saturating_sub(whole).saturating_sub(frac_used);
88 if remaining > 0 {
89 cx.set_style(self.track_style.clone());
90 cx.text("░".repeat(remaining as usize));
91 }
92 }
93
94 cx.set_style(self.track_style.clone());
96 cx.line("");
97 }
98
99 fn measure(&self, _constraint: Constraint, _cx: &mut MeasureCx) -> Size {
100 Size { width: self.width, height: 1 }
101 }
102
103 fn event(&mut self, _event: &Event, _cx: &mut EventCx) {}
104 fn layout(&mut self, _rect: Rect, _cx: &mut crate::component::LayoutCx) {}
105 fn focusable(&self) -> bool { false }
106 fn style(&self) -> Style { self.style.clone() }
107}