tui_realm_stdlib/components/
gauge.rs1use tuirealm::command::{Cmd, CmdResult};
4use tuirealm::component::Component;
5use tuirealm::props::{
6 AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, QueryResult, Style,
7 TextModifiers, Title,
8};
9use tuirealm::ratatui::Frame;
10use tuirealm::ratatui::layout::Rect;
11use tuirealm::ratatui::widgets::Gauge as TuiGauge;
12use tuirealm::state::State;
13
14use crate::prop_ext::CommonProps;
15
16#[derive(Default)]
24#[must_use]
25pub struct Gauge {
26 common: CommonProps,
27 props: Props,
28}
29
30impl Gauge {
31 pub fn foreground(mut self, fg: Color) -> Self {
33 self.attr(Attribute::Foreground, AttrValue::Color(fg));
34 self
35 }
36
37 pub fn background(mut self, bg: Color) -> Self {
39 self.attr(Attribute::Background, AttrValue::Color(bg));
40 self
41 }
42
43 pub fn modifiers(mut self, m: TextModifiers) -> Self {
45 self.attr(Attribute::TextProps, AttrValue::TextModifiers(m));
46 self
47 }
48
49 pub fn style(mut self, style: Style) -> Self {
53 self.attr(Attribute::Style, AttrValue::Style(style));
54 self
55 }
56
57 pub fn inactive(mut self, s: Style) -> Self {
59 self.attr(Attribute::UnfocusedBorderStyle, AttrValue::Style(s));
60 self
61 }
62
63 pub fn borders(mut self, b: Borders) -> Self {
65 self.attr(Attribute::Borders, AttrValue::Borders(b));
66 self
67 }
68
69 pub fn title<T: Into<Title>>(mut self, title: T) -> Self {
71 self.attr(Attribute::Title, AttrValue::Title(title.into()));
72 self
73 }
74
75 pub fn label<S: Into<String>>(mut self, s: S) -> Self {
77 self.attr(Attribute::Text, AttrValue::String(s.into()));
79 self
80 }
81
82 pub fn progress(mut self, p: f64) -> Self {
84 Self::assert_progress(p);
85 self.attr(
86 Attribute::Value,
87 AttrValue::Payload(PropPayload::Single(PropValue::F64(p))),
88 );
89 self
90 }
91
92 fn assert_progress(p: f64) {
93 assert!(
94 (0.0..=1.0).contains(&p),
95 "Progress value must be in range [0.0, 1.0]"
96 );
97 }
98}
99
100impl Component for Gauge {
101 fn view(&mut self, render: &mut Frame, area: Rect) {
102 if !self.common.display {
103 return;
104 }
105
106 let label = self
108 .props
109 .get(Attribute::Text)
110 .and_then(AttrValue::as_string);
111 let percentage = self
113 .props
114 .get(Attribute::Value)
115 .and_then(AttrValue::as_payload)
116 .and_then(PropPayload::as_single)
117 .and_then(PropValue::as_f64)
118 .unwrap_or_default();
119
120 let mut widget = TuiGauge::default()
122 .style(self.common.style)
123 .gauge_style(self.common.style)
124 .ratio(percentage)
125 .use_unicode(true);
126
127 if let Some(label) = label {
128 widget = widget.label(label.as_str());
129 }
130
131 if let Some(block) = self.common.get_block() {
132 widget = widget.block(block);
133 }
134
135 render.render_widget(widget, area);
136 }
137
138 fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
139 if let Some(value) = self.common.get_for_query(attr) {
140 return Some(value);
141 }
142
143 self.props.get_for_query(attr)
144 }
145
146 fn attr(&mut self, attr: Attribute, value: AttrValue) {
147 if let Some(value) = self.common.set(attr, value) {
148 if let Attribute::Value = attr
149 && let AttrValue::Payload(p) = value.clone()
150 {
151 Self::assert_progress(p.unwrap_single().unwrap_f64());
152 }
153 self.props.set(attr, value);
154 }
155 }
156
157 fn state(&self) -> State {
158 State::None
159 }
160
161 fn perform(&mut self, cmd: Cmd) -> CmdResult {
162 CmdResult::Invalid(cmd)
163 }
164}
165
166#[cfg(test)]
167mod test {
168
169 use pretty_assertions::assert_eq;
170 use tuirealm::props::HorizontalAlignment;
171
172 use super::*;
173
174 #[test]
175 fn test_components_progress_bar() {
176 let component = Gauge::default()
177 .background(Color::Red)
178 .foreground(Color::White)
179 .progress(0.60)
180 .title(Title::from("Downloading file...").alignment(HorizontalAlignment::Center))
181 .label("60% - ETA 00:20")
182 .borders(Borders::default());
183 assert_eq!(component.state(), State::None);
185 }
186
187 #[test]
188 #[should_panic = "Progress value must be in range [0.0, 1.0]"]
189 fn test_components_progress_bar_bad_prog() {
190 let _ = Gauge::default()
191 .background(Color::Red)
192 .foreground(Color::White)
193 .progress(6.0)
194 .title(Title::from("Downloading file...").alignment(HorizontalAlignment::Center))
195 .label("60% - ETA 00:20")
196 .borders(Borders::default());
197 }
198}