1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! A horizontal progress gauge: an accent bar with a centered percentage label.
//!
//! Over the filled bar the label is drawn in a readable contrast color (near
//! black or near white, whichever suits the accent) so it stays legible; over
//! the empty track it keeps the accent color.
use ratatui::{
Frame,
layout::Rect,
style::{Modifier, Style},
text::{Line, Span},
widgets::Paragraph,
};
use super::style;
use crate::theme::Palette;
/// Renders a gauge filled to `ratio` (clamped to `0.0..=1.0`) on the first row
/// of `area`, with `label` centered over the bar.
pub fn render(
frame: &mut Frame,
area: Rect,
palette: &Palette,
ratio: f64,
label: &str,
) {
let width = area.width as usize;
if width == 0 {
return;
}
let filled = (ratio.clamp(0.0, 1.0) * width as f64).round() as usize;
let label_chars: Vec<char> = label.chars().collect();
let label_start = width.saturating_sub(label_chars.len()) / 2;
let accent = style::to_ratatui(palette.accent);
let on_fill =
style::to_ratatui(palette.background.readable_on(palette.accent));
let track = style::to_ratatui(palette.selection);
let spans: Vec<Span> = (0..width)
.map(|index| {
let filled_here = index < filled;
let label_char = index
.checked_sub(label_start)
.and_then(|offset| label_chars.get(offset));
if let Some(&ch) = label_char {
// Over the filled bar the text uses a readable contrast color;
// over the track it stays in the accent color. The background
// is always the underlying bar/track.
let (foreground, background) = if filled_here {
(on_fill, accent)
} else {
(accent, track)
};
Span::styled(
ch.to_string(),
Style::default()
.fg(foreground)
.bg(background)
.add_modifier(Modifier::BOLD),
)
} else {
let background = if filled_here { accent } else { track };
Span::styled(" ".to_string(), Style::default().bg(background))
}
})
.collect();
frame.render_widget(Paragraph::new(Line::from(spans)), area);
}