1use std::sync::Arc;
4
5use crate::core::element::{Element, IntoElement};
6use crate::style::{Justify, Length, Padding, Style, Theme};
7use crate::widgets::status_bar_layout::StatusBarLayout;
8use crate::widgets::{HStack, Spacer, Spinner, SpinnerSpeed, SpinnerStyle, ThemeProvider};
9
10#[derive(Clone)]
12pub struct StatusBar {
13 left: Vec<Element>,
14 center: Vec<Element>,
15 right: Vec<Element>,
16 style: Style,
17 left_style: Style,
18 center_style: Style,
19 right_style: Style,
20 padding: Padding,
21 gap: u16,
22 width: Length,
23 height: Length,
24 reserve_center_space: bool,
25 loading: bool,
26 loading_label: Arc<str>,
27 loading_style: Style,
28 loading_spinner_style: SpinnerStyle,
29 loading_spinner_speed: SpinnerSpeed,
30}
31
32impl Default for StatusBar {
33 fn default() -> Self {
34 Self {
35 left: Vec::new(),
36 center: Vec::new(),
37 right: Vec::new(),
38 style: Style::default(),
39 left_style: Style::default(),
40 center_style: Style::default(),
41 right_style: Style::default(),
42 padding: (0, 1).into(),
43 gap: 1,
44 width: Length::Flex(1),
45 height: Length::Px(1),
46 reserve_center_space: false,
47 loading: false,
48 loading_label: "Loading".into(),
49 loading_style: Style::default(),
50 loading_spinner_style: SpinnerStyle::Dots,
51 loading_spinner_speed: SpinnerSpeed::Normal,
52 }
53 }
54}
55
56impl StatusBar {
57 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn child(self, item: impl IntoElement) -> Self {
64 self.left(item)
65 }
66
67 pub fn left(mut self, item: impl IntoElement) -> Self {
69 self.left.push(item.into());
70 self
71 }
72
73 pub fn center(mut self, item: impl IntoElement) -> Self {
75 self.center.push(item.into());
76 self
77 }
78
79 pub fn right(mut self, item: impl IntoElement) -> Self {
81 self.right.push(item.into());
82 self
83 }
84
85 pub fn style(mut self, style: Style) -> Self {
87 self.style = style;
88 self
89 }
90
91 pub fn left_style(mut self, style: Style) -> Self {
93 self.left_style = style;
94 self
95 }
96
97 pub fn center_style(mut self, style: Style) -> Self {
99 self.center_style = style;
100 self
101 }
102
103 pub fn right_style(mut self, style: Style) -> Self {
105 self.right_style = style;
106 self
107 }
108
109 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
111 self.padding = padding.into();
112 self
113 }
114
115 pub fn gap(mut self, gap: u16) -> Self {
117 self.gap = gap;
118 self
119 }
120
121 pub fn width(mut self, width: Length) -> Self {
123 self.width = width;
124 self
125 }
126
127 pub fn height(mut self, height: Length) -> Self {
129 self.height = height;
130 self
131 }
132
133 pub fn reserve_center_space(mut self, reserve: bool) -> Self {
138 self.reserve_center_space = reserve;
139 self
140 }
141
142 pub fn loading(mut self, loading: bool) -> Self {
144 self.loading = loading;
145 self
146 }
147
148 pub fn loading_label(mut self, label: impl Into<Arc<str>>) -> Self {
150 self.loading_label = label.into();
151 self
152 }
153
154 pub fn loading_style(mut self, style: Style) -> Self {
156 self.loading_style = style;
157 self
158 }
159
160 pub fn loading_spinner_style(mut self, style: SpinnerStyle) -> Self {
162 self.loading_spinner_style = style;
163 self
164 }
165
166 pub fn loading_spinner_speed(mut self, speed: SpinnerSpeed) -> Self {
168 self.loading_spinner_speed = speed;
169 self
170 }
171}
172
173impl From<StatusBar> for Element {
174 fn from(bar: StatusBar) -> Self {
175 let left_items = bar.left;
176 let center_items = bar.center;
177 let mut right_items = bar.right;
178
179 if bar.loading {
180 let spinner = Spinner::new()
181 .spinner_style(bar.loading_spinner_style)
182 .speed(bar.loading_spinner_speed)
183 .style(bar.loading_style)
184 .label(bar.loading_label)
185 .label_style(bar.loading_style);
186 right_items.push(spinner.into());
187 }
188
189 let center_has_content = !center_items.is_empty();
190 let left_has_content = !left_items.is_empty();
191 let right_has_content = !right_items.is_empty();
192
193 let left_theme_style = bar.style.patch(bar.left_style);
194 let center_theme_style = bar.style.patch(bar.center_style);
195 let right_theme_style = bar.style.patch(bar.right_style);
196
197 let content: Element = if center_has_content || bar.reserve_center_space {
198 StatusBarLayout {
199 left: Box::new(build_section(
200 left_items,
201 bar.gap,
202 Justify::Start,
203 left_theme_style,
204 )),
205 center: Box::new(build_section(
206 center_items,
207 bar.gap,
208 Justify::Center,
209 center_theme_style,
210 )),
211 right: Box::new(build_section(
212 right_items,
213 bar.gap,
214 Justify::End,
215 right_theme_style,
216 )),
217 style: bar.style,
218 padding: bar.padding,
219 gap: bar.gap,
220 width: bar.width,
221 height: bar.height,
222 }
223 .into()
224 } else {
225 let left_section = build_section(left_items, bar.gap, Justify::Start, left_theme_style);
227 let right_section =
228 build_section(right_items, bar.gap, Justify::End, right_theme_style);
229
230 let base = HStack::new()
231 .height(bar.height)
232 .width(bar.width)
233 .style(bar.style)
234 .padding(bar.padding);
235
236 match (left_has_content, right_has_content) {
237 (true, true) => base
238 .child(left_section)
239 .child(Spacer::new())
240 .child(right_section)
241 .into(),
242 (true, false) => base.child(left_section).into(),
243 (false, true) => base.child(Spacer::new()).child(right_section).into(),
244 (false, false) => base.into(),
245 }
246 };
247
248 content
249 }
250}
251
252fn build_section(items: Vec<Element>, gap: u16, justify: Justify, style: Style) -> Element {
253 let mut section = HStack::new()
254 .width(Length::Auto)
255 .gap(gap)
256 .justify(justify)
257 .style(style);
258
259 for item in items {
260 section = section.child(item);
261 }
262
263 let local_theme = Theme {
264 primary: style,
265 ..Theme::default()
266 };
267
268 ThemeProvider::new(local_theme).child(section).into()
269}