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