tui_temp_fork/widgets/
gauge.rs

1use unicode_width::UnicodeWidthStr;
2
3use crate::buffer::Buffer;
4use crate::layout::Rect;
5use crate::style::{Color, Style};
6use crate::widgets::{Block, Widget};
7
8/// A widget to display a task progress.
9///
10/// # Examples:
11///
12/// ```
13/// # use tui_temp_fork::widgets::{Widget, Gauge, Block, Borders};
14/// # use tui_temp_fork::style::{Style, Color, Modifier};
15/// # fn main() {
16/// Gauge::default()
17///     .block(Block::default().borders(Borders::ALL).title("Progress"))
18///     .style(Style::default().fg(Color::White).bg(Color::Black).modifier(Modifier::ITALIC))
19///     .percent(20);
20/// # }
21/// ```
22pub struct Gauge<'a> {
23    block: Option<Block<'a>>,
24    ratio: f64,
25    label: Option<&'a str>,
26    style: Style,
27}
28
29impl<'a> Default for Gauge<'a> {
30    fn default() -> Gauge<'a> {
31        Gauge {
32            block: None,
33            ratio: 0.0,
34            label: None,
35            style: Default::default(),
36        }
37    }
38}
39
40impl<'a> Gauge<'a> {
41    pub fn block(mut self, block: Block<'a>) -> Gauge<'a> {
42        self.block = Some(block);
43        self
44    }
45
46    pub fn percent(mut self, percent: u16) -> Gauge<'a> {
47        assert!(
48            percent <= 100,
49            "Percentage should be between 0 and 100 inclusively."
50        );
51        self.ratio = f64::from(percent) / 100.0;
52        self
53    }
54
55    /// Sets ratio ([0.0, 1.0]) directly.
56    pub fn ratio(mut self, ratio: f64) -> Gauge<'a> {
57        assert!(
58            ratio <= 1.0 && ratio >= 0.0,
59            "Ratio should be between 0 and 1 inclusively."
60        );
61        self.ratio = ratio;
62        self
63    }
64
65    pub fn label(mut self, string: &'a str) -> Gauge<'a> {
66        self.label = Some(string);
67        self
68    }
69
70    pub fn style(mut self, style: Style) -> Gauge<'a> {
71        self.style = style;
72        self
73    }
74}
75
76impl<'a> Widget for Gauge<'a> {
77    fn draw(&mut self, area: Rect, buf: &mut Buffer) {
78        let gauge_area = match self.block {
79            Some(ref mut b) => {
80                b.draw(area, buf);
81                b.inner(area)
82            }
83            None => area,
84        };
85        if gauge_area.height < 1 {
86            return;
87        }
88
89        if self.style.bg != Color::Reset {
90            self.background(gauge_area, buf, self.style.bg);
91        }
92
93        let center = gauge_area.height / 2 + gauge_area.top();
94        let width = (f64::from(gauge_area.width) * self.ratio).round() as u16;
95        let end = gauge_area.left() + width;
96        for y in gauge_area.top()..gauge_area.bottom() {
97            // Gauge
98            for x in gauge_area.left()..end {
99                buf.get_mut(x, y).set_symbol(" ");
100            }
101
102            if y == center {
103                // Label
104                let precent_label = format!("{}%", (self.ratio * 100.0).round());
105                let label = self.label.unwrap_or(&precent_label);
106                let label_width = label.width() as u16;
107                let middle = (gauge_area.width - label_width) / 2 + gauge_area.left();
108                buf.set_string(middle, y, label, self.style);
109            }
110
111            // Fix colors
112            for x in gauge_area.left()..end {
113                buf.get_mut(x, y)
114                    .set_fg(self.style.bg)
115                    .set_bg(self.style.fg);
116            }
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    #[should_panic]
127    fn gauge_invalid_percentage() {
128        Gauge::default().percent(110);
129    }
130
131    #[test]
132    #[should_panic]
133    fn gauge_invalid_ratio_upper_bound() {
134        Gauge::default().ratio(1.1);
135    }
136
137    #[test]
138    #[should_panic]
139    fn gauge_invalid_ratio_lower_bound() {
140        Gauge::default().ratio(-0.5);
141    }
142}