tui_realm_stdlib/components/
progress_bar.rs1use tuirealm::command::{Cmd, CmdResult};
6use tuirealm::props::{
7 Alignment, AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, Style,
8 TextModifiers,
9};
10use tuirealm::ratatui::{layout::Rect, widgets::Gauge};
11use tuirealm::{Frame, MockComponent, State};
12
13#[derive(Default)]
19#[must_use]
20pub struct ProgressBar {
21 props: Props,
22}
23
24impl ProgressBar {
25 pub fn foreground(mut self, fg: Color) -> Self {
26 self.attr(Attribute::Foreground, AttrValue::Color(fg));
27 self
28 }
29
30 pub fn background(mut self, bg: Color) -> Self {
31 self.attr(Attribute::Background, AttrValue::Color(bg));
32 self
33 }
34
35 pub fn borders(mut self, b: Borders) -> Self {
36 self.attr(Attribute::Borders, AttrValue::Borders(b));
37 self
38 }
39
40 pub fn modifiers(mut self, m: TextModifiers) -> Self {
41 self.attr(Attribute::TextProps, AttrValue::TextModifiers(m));
42 self
43 }
44
45 pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
46 self.attr(Attribute::Title, AttrValue::Title((t.into(), a)));
47 self
48 }
49
50 pub fn label<S: Into<String>>(mut self, s: S) -> Self {
51 self.attr(Attribute::Text, AttrValue::String(s.into()));
52 self
53 }
54
55 pub fn progress(mut self, p: f64) -> Self {
56 Self::assert_progress(p);
57 self.attr(
58 Attribute::Value,
59 AttrValue::Payload(PropPayload::One(PropValue::F64(p))),
60 );
61 self
62 }
63
64 fn assert_progress(p: f64) {
65 assert!(
66 (0.0..=1.0).contains(&p),
67 "Progress value must be in range [0.0, 1.0]"
68 );
69 }
70}
71
72impl MockComponent for ProgressBar {
73 fn view(&mut self, render: &mut Frame, area: Rect) {
74 if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
76 let label = self
78 .props
79 .get_or(Attribute::Text, AttrValue::String(String::default()))
80 .unwrap_string();
81 let foreground = self
82 .props
83 .get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
84 .unwrap_color();
85 let background = self
86 .props
87 .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
88 .unwrap_color();
89 let modifiers = self
90 .props
91 .get_or(
92 Attribute::TextProps,
93 AttrValue::TextModifiers(TextModifiers::empty()),
94 )
95 .unwrap_text_modifiers();
96 let borders = self
97 .props
98 .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
99 .unwrap_borders();
100 let title = self
101 .props
102 .get_ref(Attribute::Title)
103 .and_then(|x| x.as_title());
104 let percentage = self
106 .props
107 .get_or(
108 Attribute::Value,
109 AttrValue::Payload(PropPayload::One(PropValue::F64(0.0))),
110 )
111 .unwrap_payload()
112 .unwrap_one()
113 .unwrap_f64();
114
115 let normal_style = Style::default()
116 .fg(foreground)
117 .bg(background)
118 .add_modifier(modifiers);
119
120 let div = crate::utils::get_block(borders, title, true, None);
121 render.render_widget(
123 Gauge::default()
124 .block(div)
125 .style(normal_style)
126 .gauge_style(normal_style)
127 .label(label)
128 .ratio(percentage)
129 .use_unicode(true),
130 area,
131 );
132 }
133 }
134
135 fn query(&self, attr: Attribute) -> Option<AttrValue> {
136 self.props.get(attr)
137 }
138
139 fn attr(&mut self, attr: Attribute, value: AttrValue) {
140 if let Attribute::Value = attr {
141 if let AttrValue::Payload(p) = value.clone() {
142 Self::assert_progress(p.unwrap_one().unwrap_f64());
143 }
144 }
145 self.props.set(attr, value);
146 }
147
148 fn state(&self) -> State {
149 State::None
150 }
151
152 fn perform(&mut self, _cmd: Cmd) -> CmdResult {
153 CmdResult::None
154 }
155}
156
157#[cfg(test)]
158mod test {
159
160 use super::*;
161
162 use pretty_assertions::assert_eq;
163
164 #[test]
165 fn test_components_progress_bar() {
166 let component = ProgressBar::default()
167 .background(Color::Red)
168 .foreground(Color::White)
169 .progress(0.60)
170 .title("Downloading file...", Alignment::Center)
171 .label("60% - ETA 00:20")
172 .borders(Borders::default());
173 assert_eq!(component.state(), State::None);
175 }
176
177 #[test]
178 #[should_panic = "Progress value must be in range [0.0, 1.0]"]
179 fn test_components_progress_bar_bad_prog() {
180 let _ = ProgressBar::default()
181 .background(Color::Red)
182 .foreground(Color::White)
183 .progress(6.0)
184 .title("Downloading file...", Alignment::Center)
185 .label("60% - ETA 00:20")
186 .borders(Borders::default());
187 }
188}