Skip to main content

retroglyph_ui/widget/
meter.rs

1//! [`Meter`]: a load ratio mapped to a green→yellow→red color.
2use retroglyph_core::color::Color;
3
4/// A load ratio in `0.0..=1.0`, mapped to a green→yellow→red color ramp.
5///
6/// Low load is green, mid load yellow, high load red. Values outside the
7/// range are clamped. Delegates to [`Color::lerp`] (backed by `gem`) rather
8/// than hand-rolling RGB interpolation.
9///
10/// Not a drawing widget: there's no [`Terminal`](retroglyph_core::terminal::Terminal)
11/// involved, just a ratio-to-color mapping, but kept as its own small
12/// struct rather than a free function so [`Gauge`](super::Gauge),
13/// [`StatBar`](super::StatBar), and [`Sparkline`](super::Sparkline) share
14/// one place that owns the ramp.
15///
16/// # Examples
17///
18/// ```
19/// use retroglyph_ui::Meter;
20///
21/// let meter = Meter::new(0.9);
22/// assert_ne!(meter.color(), Meter::new(0.1).color());
23/// ```
24#[derive(Clone, Copy, Debug, PartialEq)]
25pub struct Meter {
26    ratio: f32,
27}
28
29impl Meter {
30    // Ramp endpoints, lerped through YELLOW at the midpoint (see `color`). Picked by eye for a
31    // readable green/amber/red load ramp on a dark background; not measured against any palette.
32    const GREEN: Color = Color::Rgb {
33        r: 80,
34        g: 200,
35        b: 120,
36    };
37    const YELLOW: Color = Color::Rgb {
38        r: 220,
39        g: 200,
40        b: 90,
41    };
42    const RED: Color = Color::Rgb {
43        r: 220,
44        g: 90,
45        b: 90,
46    };
47
48    /// A meter reading `ratio` (clamped to `0.0..=1.0` when colored).
49    #[must_use]
50    pub const fn new(ratio: f32) -> Self {
51        Self { ratio }
52    }
53
54    /// The ramped color for this meter's ratio.
55    #[must_use]
56    pub fn color(self) -> Color {
57        let t = self.ratio.clamp(0.0, 1.0);
58        if t < 0.5 {
59            Color::lerp(Self::GREEN, Self::YELLOW, t * 2.0)
60        } else {
61            Color::lerp(Self::YELLOW, Self::RED, (t - 0.5) * 2.0)
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn low_load_is_green() {
72        assert_eq!(Meter::new(0.0).color(), Meter::GREEN);
73    }
74
75    #[test]
76    fn mid_load_is_yellow() {
77        assert_eq!(Meter::new(0.5).color(), Meter::YELLOW);
78    }
79
80    #[test]
81    fn high_load_is_red() {
82        assert_eq!(Meter::new(1.0).color(), Meter::RED);
83    }
84
85    #[test]
86    fn out_of_range_ratios_are_clamped() {
87        assert_eq!(Meter::new(-1.0).color(), Meter::GREEN);
88        assert_eq!(Meter::new(2.0).color(), Meter::RED);
89    }
90}