Skip to main content

scrin/widgets/
gauge.rs

1use crate::core::buffer::Buffer;
2use crate::core::color::Color;
3use crate::core::rect::Rect;
4use crate::style::Style;
5use crate::widgets::Widget;
6
7#[derive(Debug, Clone)]
8pub struct Gauge<'a> {
9    pub ratio: f64,
10    pub label: &'a str,
11    pub style: Style,
12    pub gauge_style: Style,
13    pub use_unicode: bool,
14}
15
16impl<'a> Gauge<'a> {
17    pub fn new() -> Self {
18        Self {
19            ratio: 0.0,
20            label: "",
21            style: Style::new(),
22            gauge_style: Style::new().fg(Color::rgb(88, 166, 255)),
23            use_unicode: true,
24        }
25    }
26
27    pub fn with_ratio(mut self, ratio: f64) -> Self {
28        self.ratio = ratio.clamp(0.0, 1.0);
29        self
30    }
31
32    pub fn with_label(mut self, label: &'a str) -> Self {
33        self.label = label;
34        self
35    }
36
37    pub fn with_style(mut self, style: Style) -> Self {
38        self.style = style;
39        self
40    }
41
42    pub fn with_gauge_style(mut self, style: Style) -> Self {
43        self.gauge_style = style;
44        self
45    }
46}
47
48impl<'a> Widget for Gauge<'a> {
49    fn render(&self, buffer: &mut Buffer, area: Rect) {
50        if area.width < 2 || area.height < 1 {
51            return;
52        }
53        let w = area.width as usize;
54        let fill = (w as f64 * self.ratio) as usize;
55        let fg = self.gauge_style.fg_or_default();
56        let bg = self.style.bg;
57        let empty_bg = self.style.bg;
58
59        let (full, half, empty) = if self.use_unicode {
60            ('━', '╌', '─')
61        } else {
62            ('=', '-', ' ')
63        };
64
65        for i in 0..w {
66            let x = (area.x as usize) + i;
67            let y = area.y as usize;
68            if i < fill {
69                buffer.set(
70                    x,
71                    y,
72                    crate::core::buffer::Cell {
73                        ch: full,
74                        fg,
75                        bg,
76                        bold: false,
77                        italic: false,
78                        underlined: false,
79                    },
80                );
81            } else if i == fill && self.ratio > 0.0 {
82                buffer.set(
83                    x,
84                    y,
85                    crate::core::buffer::Cell {
86                        ch: half,
87                        fg: fg.dim(0.5),
88                        bg: empty_bg,
89                        bold: false,
90                        italic: false,
91                        underlined: false,
92                    },
93                );
94            } else {
95                buffer.set(
96                    x,
97                    y,
98                    crate::core::buffer::Cell {
99                        ch: empty,
100                        fg: fg.dim(0.3),
101                        bg: empty_bg,
102                        bold: false,
103                        italic: false,
104                        underlined: false,
105                    },
106                );
107            }
108        }
109
110        if !self.label.is_empty() {
111            let pct = (self.ratio * 100.0) as u32;
112            let label = format!(" {} {}%", self.label, pct);
113            let display: String = label.chars().take(w).collect();
114            let lx = area.x as usize + (w.saturating_sub(display.len())) / 2;
115            buffer.set_str(lx, area.y as usize, &display, Color::WHITE, bg);
116        }
117    }
118}
119
120impl<'a> Default for Gauge<'a> {
121    fn default() -> Self {
122        Self::new()
123    }
124}