retroglyph_widgets/widget/
sparkline.rs1use retroglyph_core::{Backend, Rect, Style, Terminal};
3
4use super::{Meter, Widget};
5
6const BLOCKS: [char; 9] = [' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
8
9#[derive(Clone, Copy, Debug)]
16pub struct Sparkline<'a> {
17 samples: &'a [f32],
18}
19
20impl<'a> Sparkline<'a> {
21 #[must_use]
23 pub const fn new(samples: &'a [f32]) -> Self {
24 Self { samples }
25 }
26}
27
28impl<B: Backend> Widget<B> for Sparkline<'_> {
29 fn render(self, area: Rect, term: &mut Terminal<B>) {
30 let width = area.width_usize();
31 if width == 0 {
32 return;
33 }
34 let y = area.top();
35 let max = self
36 .samples
37 .iter()
38 .copied()
39 .fold(0.0_f32, f32::max)
40 .max(1e-6);
41
42 let start = self.samples.len().saturating_sub(width);
44 let recent = &self.samples[start..];
45 let pad = width - recent.len();
46
47 for i in 0..width {
48 let x = area.left() + i as u16;
49 if i < pad {
50 term.put_styled(x, y, ' ', Style::new());
51 continue;
52 }
53 let ratio = (recent[i - pad] / max).clamp(0.0, 1.0);
54 let level = (ratio * 8.0).round() as usize;
55 term.put_styled(
56 x,
57 y,
58 BLOCKS[level.min(8)],
59 Style::new().fg(Meter::new(ratio).color()),
60 );
61 }
62 term.reset_style();
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use retroglyph_core::Headless;
69
70 use super::*;
71
72 #[test]
73 fn right_aligns_recent_samples_and_pads_the_rest() {
74 let area = Rect::new(0, 0, 5, 1);
75 let mut term = Terminal::new(Headless::new(5, 1));
76 Sparkline::new(&[1.0, 2.0]).render(area, &mut term);
77
78 assert_eq!(term.grid().get(0, 0).glyph(), ' ');
79 assert_eq!(term.grid().get(2, 0).glyph(), ' ');
80 assert_eq!(term.grid().get(3, 0).glyph(), BLOCKS[4]); assert_eq!(term.grid().get(4, 0).glyph(), BLOCKS[8]); }
83
84 #[test]
85 fn empty_samples_is_a_no_op_beyond_blank_padding() {
86 let area = Rect::new(0, 0, 3, 1);
87 let mut term = Terminal::new(Headless::new(3, 1));
88 Sparkline::new(&[]).render(area, &mut term);
89 for x in 0..3 {
90 assert_eq!(term.grid().get(x, 0).glyph(), ' ');
91 }
92 }
93}