1use crate::ui::{Ui, DrawCommand, RectDraw};
2use zenthra_core::{Color, EdgeInsets, Response, Id};
3use zenthra_render::RectInstance;
4
5pub struct ProgressBarBuilder<'u, 'a> {
6 ui: &'u mut Ui<'a>,
7 id: Id,
8 value: f32, width: Option<f32>,
12 height: Option<f32>,
13 padding: EdgeInsets,
14 bg: Option<Color>,
15 radius: [f32; 4],
16 border_color: Option<Color>,
17 border_width: f32,
18 opacity: f32,
19
20 track_h: f32,
22 track_color: Color,
23
24 fill_color: Color,
26
27 shadow_color: Option<Color>,
29 shadow_offset: [f32; 2],
30 shadow_blur: f32,
31 shadow_opacity: f32,
32
33 fill_shadow_color: Option<Color>,
34 fill_shadow_offset: [f32; 2],
35 fill_shadow_blur: f32,
36 fill_shadow_opacity: f32,
37
38 shimmer: bool,
40}
41
42impl<'u, 'a> ProgressBarBuilder<'u, 'a> {
43 pub fn new(ui: &'u mut Ui<'a>, value: f32) -> Self {
44 let id = ui.id();
45 Self {
46 ui,
47 id,
48 value: value.clamp(0.0, 1.0),
49
50 width: None,
51 height: None,
52 padding: EdgeInsets::symmetric(4.0, 4.0),
53 bg: None,
54 radius: [0.0; 4],
55 border_color: None,
56 border_width: 0.0,
57 opacity: 1.0,
58
59 track_h: 8.0,
60 track_color: Color::rgb(0.15, 0.15, 0.15),
61
62 fill_color: Color::rgb(0.4, 0.6, 1.0),
63
64 shadow_color: None,
65 shadow_offset: [0.0, 2.0],
66 shadow_blur: 5.0,
67 shadow_opacity: 0.3,
68
69 fill_shadow_color: None,
70 fill_shadow_offset: [0.0, 1.0],
71 fill_shadow_blur: 3.0,
72 fill_shadow_opacity: 0.4,
73
74 shimmer: false,
75 }
76 }
77
78 pub fn id(mut self, id: impl std::hash::Hash) -> Self {
79 let mut hasher = std::collections::hash_map::DefaultHasher::new();
80 use std::hash::Hasher;
81 id.hash(&mut hasher);
82 self.id = Id::from_u64(hasher.finish());
83 self
84 }
85
86 pub fn size(mut self, w: f32, h: f32) -> Self {
88 self.width = Some(w);
89 self.height = Some(h);
90 self
91 }
92
93 pub fn width(mut self, w: f32) -> Self {
94 self.width = Some(w);
95 self
96 }
97
98 pub fn height(mut self, h: f32) -> Self {
99 self.height = Some(h);
100 self
101 }
102
103 pub fn track_size(mut self, w: f32, h: f32) -> Self {
104 self.width = Some(w);
105 self.track_h = h;
106 self
107 }
108
109 pub fn radius(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
110 self.radius = [tl, tr, br, bl];
111 self
112 }
113
114 pub fn radius_all(mut self, r: f32) -> Self {
115 self.radius = [r; 4];
116 self
117 }
118
119 pub fn radius_top(mut self, r: f32) -> Self {
120 self.radius[0] = r;
121 self.radius[1] = r;
122 self
123 }
124
125 pub fn radius_bottom(mut self, r: f32) -> Self {
126 self.radius[2] = r;
127 self.radius[3] = r;
128 self
129 }
130
131 pub fn radius_top_left(mut self, r: f32) -> Self {
132 self.radius[0] = r;
133 self
134 }
135
136 pub fn radius_top_right(mut self, r: f32) -> Self {
137 self.radius[1] = r;
138 self
139 }
140
141 pub fn radius_bottom_right(mut self, r: f32) -> Self {
142 self.radius[2] = r;
143 self
144 }
145
146 pub fn radius_bottom_left(mut self, r: f32) -> Self {
147 self.radius[3] = r;
148 self
149 }
150
151 pub fn radius_left(mut self, r: f32) -> Self {
152 self.radius[0] = r;
153 self.radius[3] = r;
154 self
155 }
156
157 pub fn radius_right(mut self, r: f32) -> Self {
158 self.radius[1] = r;
159 self.radius[2] = r;
160 self
161 }
162
163 pub fn bg(mut self, color: Color) -> Self {
164 self.bg = Some(color);
165 self
166 }
167
168 pub fn border(mut self, color: Color, width: f32) -> Self {
169 self.border_color = Some(color);
170 self.border_width = width;
171 self
172 }
173
174 pub fn opacity(mut self, o: f32) -> Self {
175 self.opacity = o;
176 self
177 }
178
179 pub fn padding(mut self, t: f32, r: f32, b: f32, l: f32) -> Self {
180 self.padding = EdgeInsets { top: t, right: r, bottom: b, left: l };
181 self
182 }
183
184 pub fn track_height(mut self, h: f32) -> Self {
186 self.track_h = h;
187 self
188 }
189
190 pub fn track_color(mut self, color: Color) -> Self {
191 self.track_color = color;
192 self
193 }
194
195 pub fn fill_color(mut self, color: Color) -> Self {
197 self.fill_color = color;
198 self
199 }
200
201 pub fn shadow(mut self, color: Color, x: f32, y: f32, blur: f32) -> Self {
203 self.shadow_color = Some(color);
204 self.shadow_offset = [x, y];
205 self.shadow_blur = blur;
206 self
207 }
208
209 pub fn shadow_opacity(mut self, opacity: f32) -> Self {
210 self.shadow_opacity = opacity;
211 self
212 }
213
214 pub fn fill_shadow(mut self, color: Color, x: f32, y: f32, blur: f32) -> Self {
215 self.fill_shadow_color = Some(color);
216 self.fill_shadow_offset = [x, y];
217 self.fill_shadow_blur = blur;
218 self
219 }
220
221 pub fn fill_shadow_opacity(mut self, opacity: f32) -> Self {
222 self.fill_shadow_opacity = opacity;
223 self
224 }
225
226 pub fn shimmer(mut self, enabled: bool) -> Self {
227 self.shimmer = enabled;
228 self
229 }
230
231 pub fn show(self) -> Response {
232 let x = self.ui.cursor_x;
233 let y = self.ui.cursor_y;
234 let w = self.width.unwrap_or(self.ui.available_width.max(100.0));
235
236 let h = self.height.unwrap_or_else(|| {
237 self.track_h.max(12.0) + self.padding.vertical()
238 });
239
240 let is_hovered = self.ui.mouse_in_rect(x + self.ui.offset_x, y + self.ui.offset_y, w, h);
241 let start_draw = self.ui.draws.len();
242
243 if let Some(bg) = self.bg {
245 self.ui.draws.push(DrawCommand::Rect(RectDraw {
246 instance: RectInstance {
247 pos: [x, y],
248 size: [w, h],
249 color: bg.to_array(),
250 radius: self.radius,
251 border_width: self.border_width,
252 border_color: self.border_color.map(|c| c.to_array()).unwrap_or([0.0; 4]),
253 opacity: self.opacity,
254 ..Default::default()
255 }
256 }));
257 }
258
259 let track_w = w - self.padding.horizontal();
261 let track_x = x + self.padding.left;
262 let ty = y + (h - self.track_h) / 2.0;
263
264 let mut track_shadow_color = [0.0; 4];
265 if let Some(c) = self.shadow_color {
266 track_shadow_color = c.to_array();
267 track_shadow_color[3] *= self.shadow_opacity;
268 }
269
270 self.ui.draws.push(DrawCommand::Rect(RectDraw {
271 instance: RectInstance {
272 pos: [track_x, ty],
273 size: [track_w, self.track_h],
274 color: self.track_color.to_array(),
275 radius: self.radius,
276 opacity: self.opacity,
277 shadow_color: track_shadow_color,
278 shadow_offset: self.shadow_offset,
279 shadow_blur: self.shadow_blur,
280 ..Default::default()
281 }
282 }));
283
284 let fill_w = track_w * self.value;
286 if fill_w > 0.1 {
287 let mut fill_shadow_color = [0.0; 4];
288 if let Some(c) = self.fill_shadow_color {
289 fill_shadow_color = c.to_array();
290 fill_shadow_color[3] *= self.fill_shadow_opacity;
291 }
292
293 self.ui.draws.push(DrawCommand::Rect(RectDraw {
294 instance: RectInstance {
295 pos: [track_x, ty],
296 size: [fill_w, self.track_h],
297 color: self.fill_color.to_array(),
298 radius: self.radius,
299 opacity: self.opacity,
300 shadow_color: fill_shadow_color,
301 shadow_offset: self.fill_shadow_offset,
302 shadow_blur: self.fill_shadow_blur,
303 ..Default::default()
304 }
305 }));
306
307 if self.shimmer {
309 let shimmer_t = (self.ui.elapsed_time * 1.5) % 2.0 - 0.5; let shimmer_x = track_x + shimmer_t * track_w;
311 let shimmer_w = track_w * 0.2;
312
313 if shimmer_x + shimmer_w > track_x && shimmer_x < track_x + fill_w {
314 let actual_shimmer_x = shimmer_x.max(track_x);
316 let actual_shimmer_w = (shimmer_x + shimmer_w).min(track_x + fill_w) - actual_shimmer_x;
317
318 if actual_shimmer_w > 0.0 {
319 self.ui.draws.push(DrawCommand::Rect(RectDraw {
320 instance: RectInstance {
321 pos: [actual_shimmer_x, ty],
322 size: [actual_shimmer_w, self.track_h],
323 color: [1.0, 1.0, 1.0, 0.2],
324 radius: self.radius,
325 brightness: 1.5,
326 ..Default::default()
327 }
328 }));
329 self.ui.request_redraw(); }
331 }
332 }
333 }
334
335 self.ui.record_layout(self.id, zenthra_core::Rect::new(x, y, w, h));
336 self.ui.advance(w, h, start_draw);
337
338 Response {
339 clicked: self.ui.clicked && is_hovered,
340 hovered: is_hovered,
341 pressed: self.ui.mouse_down && is_hovered,
342 }
343 }
344}