Skip to main content

azul_layout/widgets/
progressbar.rs

1//! Native progress bar widget with customizable backgrounds, height, and
2//! gradient styling. The main type is [`ProgressBar`], which is rendered
3//! into a DOM via [`ProgressBar::dom()`].
4
5use azul_core::dom::{Dom, IdOrClass, IdOrClass::Class, IdOrClassVec};
6#[allow(clippy::wildcard_imports)] // widget/render module pulls in the css property/value types it builds with
7use azul_css::{
8    dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
9    props::{
10        basic::*,
11        layout::*,
12        property::{CssProperty, *},
13        style::*,
14    },
15    *,
16};
17use azul_css::css::BoxOrStatic;
18
19const STYLE_BACKGROUND_CONTENT_2688422633177340412_ITEMS: &[StyleBackgroundContent] =
20    &[StyleBackgroundContent::LinearGradient(LinearGradient {
21        direction: Direction::FromTo(DirectionCorners {
22            dir_from: DirectionCorner::Top,
23            dir_to: DirectionCorner::Bottom,
24        }),
25        extend_mode: ExtendMode::Clamp,
26        stops: NormalizedLinearColorStopVec::from_const_slice(
27            LINEAR_COLOR_STOP_12009347504665939_ITEMS,
28        ),
29    })];
30const STYLE_BACKGROUND_CONTENT_14586281004485141058_ITEMS: &[StyleBackgroundContent] =
31    &[StyleBackgroundContent::LinearGradient(LinearGradient {
32        direction: Direction::FromTo(DirectionCorners {
33            dir_from: DirectionCorner::Top,
34            dir_to: DirectionCorner::Bottom,
35        }),
36        extend_mode: ExtendMode::Clamp,
37        stops: NormalizedLinearColorStopVec::from_const_slice(
38            LINEAR_COLOR_STOP_3104396762583413726_ITEMS,
39        ),
40    })];
41const LINEAR_COLOR_STOP_12009347504665939_ITEMS: &[NormalizedLinearColorStop] = &[
42    NormalizedLinearColorStop {
43        offset: PercentageValue::const_new(0),
44        color: ColorOrSystem::color(ColorU {
45            r: 193,
46            g: 255,
47            b: 187,
48            a: 255,
49        }),
50    },
51    NormalizedLinearColorStop {
52        offset: PercentageValue::const_new(10),
53        color: ColorOrSystem::color(ColorU {
54            r: 205,
55            g: 255,
56            b: 205,
57            a: 255,
58        }),
59    },
60    NormalizedLinearColorStop {
61        offset: PercentageValue::const_new(15),
62        color: ColorOrSystem::color(ColorU {
63            r: 156,
64            g: 238,
65            b: 172,
66            a: 255,
67        }),
68    },
69    NormalizedLinearColorStop {
70        offset: PercentageValue::const_new(20),
71        color: ColorOrSystem::color(ColorU {
72            r: 0,
73            g: 211,
74            b: 40,
75            a: 255,
76        }),
77    },
78    NormalizedLinearColorStop {
79        offset: PercentageValue::const_new(30),
80        color: ColorOrSystem::color(ColorU {
81            r: 0,
82            g: 211,
83            b: 40,
84            a: 255,
85        }),
86    },
87    NormalizedLinearColorStop {
88        offset: PercentageValue::const_new(70),
89        color: ColorOrSystem::color(ColorU {
90            r: 32,
91            g: 219,
92            b: 65,
93            a: 255,
94        }),
95    },
96    NormalizedLinearColorStop {
97        offset: PercentageValue::const_new(100),
98        color: ColorOrSystem::color(ColorU {
99            r: 32,
100            g: 219,
101            b: 65,
102            a: 255,
103        }),
104    },
105];
106const LINEAR_COLOR_STOP_3104396762583413726_ITEMS: &[NormalizedLinearColorStop] = &[
107    NormalizedLinearColorStop {
108        offset: PercentageValue::const_new(0),
109        color: ColorOrSystem::color(ColorU {
110            r: 243,
111            g: 243,
112            b: 243,
113            a: 255,
114        }),
115    },
116    NormalizedLinearColorStop {
117        offset: PercentageValue::const_new(10),
118        color: ColorOrSystem::color(ColorU {
119            r: 252,
120            g: 252,
121            b: 252,
122            a: 255,
123        }),
124    },
125    NormalizedLinearColorStop {
126        offset: PercentageValue::const_new(15),
127        color: ColorOrSystem::color(ColorU {
128            r: 218,
129            g: 218,
130            b: 218,
131            a: 255,
132        }),
133    },
134    NormalizedLinearColorStop {
135        offset: PercentageValue::const_new(20),
136        color: ColorOrSystem::color(ColorU {
137            r: 201,
138            g: 201,
139            b: 201,
140            a: 255,
141        }),
142    },
143    NormalizedLinearColorStop {
144        offset: PercentageValue::const_new(30),
145        color: ColorOrSystem::color(ColorU {
146            r: 218,
147            g: 218,
148            b: 218,
149            a: 255,
150        }),
151    },
152    NormalizedLinearColorStop {
153        offset: PercentageValue::const_new(70),
154        color: ColorOrSystem::color(ColorU {
155            r: 203,
156            g: 203,
157            b: 203,
158            a: 255,
159        }),
160    },
161    NormalizedLinearColorStop {
162        offset: PercentageValue::const_new(100),
163        color: ColorOrSystem::color(ColorU {
164            r: 203,
165            g: 203,
166            b: 203,
167            a: 255,
168        }),
169    },
170];
171
172/// A native progress bar widget with customizable bar/container backgrounds and height.
173#[derive(Debug, Clone)]
174#[repr(C)]
175pub struct ProgressBar {
176    pub progressbar_state: ProgressBarState,
177    pub height: PixelValue,
178    pub bar_background: StyleBackgroundContentVec,
179    pub container_background: StyleBackgroundContentVec,
180}
181
182/// Internal state for a [`ProgressBar`], tracking completion percentage.
183#[derive(Copy, Debug, Clone)]
184#[repr(C)]
185pub struct ProgressBarState {
186    pub percent_done: f32,
187    pub display_percentage: bool,
188}
189
190impl ProgressBar {
191    /// Creates a new progress bar with the given completion percentage (0.0 to 100.0).
192    #[inline]
193    #[must_use] pub const fn create(percent_done: f32) -> Self {
194        Self {
195            progressbar_state: ProgressBarState {
196                percent_done,
197                display_percentage: false,
198            },
199            height: PixelValue::const_px(15),
200            bar_background: StyleBackgroundContentVec::from_const_slice(
201                STYLE_BACKGROUND_CONTENT_2688422633177340412_ITEMS,
202            ),
203            container_background: StyleBackgroundContentVec::from_const_slice(
204                STYLE_BACKGROUND_CONTENT_14586281004485141058_ITEMS,
205            ),
206        }
207    }
208
209    /// Replaces `self` with a default (0%) progress bar, returning the previous value.
210    #[inline]
211    #[must_use]
212    pub const fn swap_with_default(&mut self) -> Self {
213        let mut s = Self::create(0.0);
214        core::mem::swap(&mut s, self);
215        s
216    }
217
218    pub fn set_container_background(&mut self, background: StyleBackgroundContentVec) {
219        self.container_background = background;
220    }
221
222    #[must_use] pub fn with_container_background(mut self, background: StyleBackgroundContentVec) -> Self {
223        self.set_container_background(background);
224        self
225    }
226
227    pub fn set_bar_background(&mut self, background: StyleBackgroundContentVec) {
228        self.bar_background = background;
229    }
230
231    #[must_use] pub fn with_bar_background(mut self, background: StyleBackgroundContentVec) -> Self {
232        self.set_bar_background(background);
233        self
234    }
235
236    pub const fn set_height(&mut self, height: PixelValue) {
237        self.height = height;
238    }
239
240    #[must_use] pub const fn with_height(mut self, height: PixelValue) -> Self {
241        self.set_height(height);
242        self
243    }
244
245    /// Renders this progress bar into a [`Dom`] tree consisting of a container div
246    /// with two children: the filled bar and the remaining empty space.
247    #[allow(clippy::too_many_lines)] // large but cohesive: single-purpose layout/render/parse routine (one branch per case)
248    #[must_use] pub fn dom(self) -> Dom {
249        use azul_core::dom::DomVec;
250
251        // Use percentage widths for the progress bar and remaining space.
252        // The container uses flex-direction: row, and we set explicit widths
253        // on the children using CSS percentages.
254        let percent_done = self.progressbar_state.percent_done.clamp(0.0, 100.0);
255
256        Dom::create_div()
257            .with_css_props(CssPropertyWithConditionsVec::from_vec(vec![
258                // .__azul-native-progress-bar-container
259                CssPropertyWithConditions::simple(CssProperty::Height(LayoutHeightValue::Exact(
260                    LayoutHeight::Px(self.height),
261                ))),
262                CssPropertyWithConditions::simple(CssProperty::FlexDirection(
263                    LayoutFlexDirectionValue::Exact(LayoutFlexDirection::Row),
264                )),
265                CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
266                    StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
267                        offset_x: PixelValueNoPercent {
268                            inner: PixelValue::const_px(0),
269                        },
270                        offset_y: PixelValueNoPercent {
271                            inner: PixelValue::const_px(0),
272                        },
273                        color: ColorU {
274                            r: 0,
275                            g: 0,
276                            b: 0,
277                            a: 9,
278                        },
279                        blur_radius: PixelValueNoPercent {
280                            inner: PixelValue::const_px(15),
281                        },
282                        spread_radius: PixelValueNoPercent {
283                            inner: PixelValue::const_px(2),
284                        },
285                        clip_mode: BoxShadowClipMode::Inset,
286                    })),
287                )),
288                CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(
289                    StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
290                        offset_x: PixelValueNoPercent {
291                            inner: PixelValue::const_px(0),
292                        },
293                        offset_y: PixelValueNoPercent {
294                            inner: PixelValue::const_px(0),
295                        },
296                        color: ColorU {
297                            r: 0,
298                            g: 0,
299                            b: 0,
300                            a: 9,
301                        },
302                        blur_radius: PixelValueNoPercent {
303                            inner: PixelValue::const_px(15),
304                        },
305                        spread_radius: PixelValueNoPercent {
306                            inner: PixelValue::const_px(2),
307                        },
308                        clip_mode: BoxShadowClipMode::Inset,
309                    })),
310                )),
311                CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(
312                    StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
313                        offset_x: PixelValueNoPercent {
314                            inner: PixelValue::const_px(0),
315                        },
316                        offset_y: PixelValueNoPercent {
317                            inner: PixelValue::const_px(0),
318                        },
319                        color: ColorU {
320                            r: 0,
321                            g: 0,
322                            b: 0,
323                            a: 9,
324                        },
325                        blur_radius: PixelValueNoPercent {
326                            inner: PixelValue::const_px(15),
327                        },
328                        spread_radius: PixelValueNoPercent {
329                            inner: PixelValue::const_px(2),
330                        },
331                        clip_mode: BoxShadowClipMode::Inset,
332                    })),
333                )),
334                CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(
335                    StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
336                        offset_x: PixelValueNoPercent {
337                            inner: PixelValue::const_px(0),
338                        },
339                        offset_y: PixelValueNoPercent {
340                            inner: PixelValue::const_px(0),
341                        },
342                        color: ColorU {
343                            r: 0,
344                            g: 0,
345                            b: 0,
346                            a: 9,
347                        },
348                        blur_radius: PixelValueNoPercent {
349                            inner: PixelValue::const_px(15),
350                        },
351                        spread_radius: PixelValueNoPercent {
352                            inner: PixelValue::const_px(2),
353                        },
354                        clip_mode: BoxShadowClipMode::Inset,
355                    })),
356                )),
357                CssPropertyWithConditions::simple(CssProperty::BorderBottomRightRadius(
358                    StyleBorderBottomRightRadiusValue::Exact(StyleBorderBottomRightRadius {
359                        inner: PixelValue::const_px(3),
360                    }),
361                )),
362                CssPropertyWithConditions::simple(CssProperty::BorderBottomLeftRadius(
363                    StyleBorderBottomLeftRadiusValue::Exact(StyleBorderBottomLeftRadius {
364                        inner: PixelValue::const_px(3),
365                    }),
366                )),
367                CssPropertyWithConditions::simple(CssProperty::BorderTopRightRadius(
368                    StyleBorderTopRightRadiusValue::Exact(StyleBorderTopRightRadius {
369                        inner: PixelValue::const_px(3),
370                    }),
371                )),
372                CssPropertyWithConditions::simple(CssProperty::BorderTopLeftRadius(
373                    StyleBorderTopLeftRadiusValue::Exact(StyleBorderTopLeftRadius {
374                        inner: PixelValue::const_px(3),
375                    }),
376                )),
377                CssPropertyWithConditions::simple(CssProperty::BorderBottomWidth(
378                    LayoutBorderBottomWidthValue::Exact(LayoutBorderBottomWidth {
379                        inner: PixelValue::const_px(1),
380                    }),
381                )),
382                CssPropertyWithConditions::simple(CssProperty::BorderLeftWidth(
383                    LayoutBorderLeftWidthValue::Exact(LayoutBorderLeftWidth {
384                        inner: PixelValue::const_px(1),
385                    }),
386                )),
387                CssPropertyWithConditions::simple(CssProperty::BorderRightWidth(
388                    LayoutBorderRightWidthValue::Exact(LayoutBorderRightWidth {
389                        inner: PixelValue::const_px(1),
390                    }),
391                )),
392                CssPropertyWithConditions::simple(CssProperty::BorderTopWidth(
393                    LayoutBorderTopWidthValue::Exact(LayoutBorderTopWidth {
394                        inner: PixelValue::const_px(1),
395                    }),
396                )),
397                CssPropertyWithConditions::simple(CssProperty::BorderBottomStyle(
398                    StyleBorderBottomStyleValue::Exact(StyleBorderBottomStyle {
399                        inner: BorderStyle::Solid,
400                    }),
401                )),
402                CssPropertyWithConditions::simple(CssProperty::BorderLeftStyle(
403                    StyleBorderLeftStyleValue::Exact(StyleBorderLeftStyle {
404                        inner: BorderStyle::Solid,
405                    }),
406                )),
407                CssPropertyWithConditions::simple(CssProperty::BorderRightStyle(
408                    StyleBorderRightStyleValue::Exact(StyleBorderRightStyle {
409                        inner: BorderStyle::Solid,
410                    }),
411                )),
412                CssPropertyWithConditions::simple(CssProperty::BorderTopStyle(
413                    StyleBorderTopStyleValue::Exact(StyleBorderTopStyle {
414                        inner: BorderStyle::Solid,
415                    }),
416                )),
417                CssPropertyWithConditions::simple(CssProperty::BorderBottomColor(
418                    StyleBorderBottomColorValue::Exact(StyleBorderBottomColor {
419                        inner: ColorU {
420                            r: 178,
421                            g: 178,
422                            b: 178,
423                            a: 255,
424                        },
425                    }),
426                )),
427                CssPropertyWithConditions::simple(CssProperty::BorderLeftColor(
428                    StyleBorderLeftColorValue::Exact(StyleBorderLeftColor {
429                        inner: ColorU {
430                            r: 178,
431                            g: 178,
432                            b: 178,
433                            a: 255,
434                        },
435                    }),
436                )),
437                CssPropertyWithConditions::simple(CssProperty::BorderRightColor(
438                    StyleBorderRightColorValue::Exact(StyleBorderRightColor {
439                        inner: ColorU {
440                            r: 178,
441                            g: 178,
442                            b: 178,
443                            a: 255,
444                        },
445                    }),
446                )),
447                CssPropertyWithConditions::simple(CssProperty::BorderTopColor(
448                    StyleBorderTopColorValue::Exact(StyleBorderTopColor {
449                        inner: ColorU {
450                            r: 178,
451                            g: 178,
452                            b: 178,
453                            a: 255,
454                        },
455                    }),
456                )),
457                CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
458                    StyleBackgroundContentVecValue::Exact(self.container_background.clone()),
459                )),
460            ]))
461            .with_ids_and_classes({
462                const IDS_AND_CLASSES_10874511710181900075: &[IdOrClass] = &[Class(
463                    AzString::from_const_str("__azul-native-progress-bar-container"),
464                )];
465                IdOrClassVec::from_const_slice(IDS_AND_CLASSES_10874511710181900075)
466            })
467            .with_children(DomVec::from_vec(vec![
468                Dom::create_div()
469                    .with_css_props(CssPropertyWithConditionsVec::from_vec(vec![
470                        // .__azul-native-progress-bar-bar
471                        // Use percentage width instead of flex-grow hack
472                        CssPropertyWithConditions::simple(CssProperty::Width(
473                            LayoutWidthValue::Exact(LayoutWidth::Px(
474                                PixelValue::percent(percent_done),
475                            )),
476                        )),
477                        CssPropertyWithConditions::simple(CssProperty::BoxShadowBottom(
478                            StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
479                                offset_x: PixelValueNoPercent {
480                                    inner: PixelValue::const_px(0),
481                                },
482                                offset_y: PixelValueNoPercent {
483                                    inner: PixelValue::const_px(0),
484                                },
485                                color: ColorU {
486                                    r: 0,
487                                    g: 51,
488                                    b: 0,
489                                    a: 51,
490                                },
491                                blur_radius: PixelValueNoPercent {
492                                    inner: PixelValue::const_px(15),
493                                },
494                                spread_radius: PixelValueNoPercent {
495                                    inner: PixelValue::const_px(12),
496                                },
497                                clip_mode: BoxShadowClipMode::Inset,
498                            })),
499                        )),
500                        CssPropertyWithConditions::simple(CssProperty::BoxShadowTop(
501                            StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
502                                offset_x: PixelValueNoPercent {
503                                    inner: PixelValue::const_px(0),
504                                },
505                                offset_y: PixelValueNoPercent {
506                                    inner: PixelValue::const_px(0),
507                                },
508                                color: ColorU {
509                                    r: 0,
510                                    g: 51,
511                                    b: 0,
512                                    a: 51,
513                                },
514                                blur_radius: PixelValueNoPercent {
515                                    inner: PixelValue::const_px(15),
516                                },
517                                spread_radius: PixelValueNoPercent {
518                                    inner: PixelValue::const_px(12),
519                                },
520                                clip_mode: BoxShadowClipMode::Inset,
521                            })),
522                        )),
523                        CssPropertyWithConditions::simple(CssProperty::BoxShadowRight(
524                            StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
525                                offset_x: PixelValueNoPercent {
526                                    inner: PixelValue::const_px(0),
527                                },
528                                offset_y: PixelValueNoPercent {
529                                    inner: PixelValue::const_px(0),
530                                },
531                                color: ColorU {
532                                    r: 0,
533                                    g: 51,
534                                    b: 0,
535                                    a: 51,
536                                },
537                                blur_radius: PixelValueNoPercent {
538                                    inner: PixelValue::const_px(15),
539                                },
540                                spread_radius: PixelValueNoPercent {
541                                    inner: PixelValue::const_px(12),
542                                },
543                                clip_mode: BoxShadowClipMode::Inset,
544                            })),
545                        )),
546                        CssPropertyWithConditions::simple(CssProperty::BoxShadowLeft(
547                            StyleBoxShadowValue::Exact(BoxOrStatic::heap(StyleBoxShadow {
548                                offset_x: PixelValueNoPercent {
549                                    inner: PixelValue::const_px(0),
550                                },
551                                offset_y: PixelValueNoPercent {
552                                    inner: PixelValue::const_px(0),
553                                },
554                                color: ColorU {
555                                    r: 0,
556                                    g: 51,
557                                    b: 0,
558                                    a: 51,
559                                },
560                                blur_radius: PixelValueNoPercent {
561                                    inner: PixelValue::const_px(15),
562                                },
563                                spread_radius: PixelValueNoPercent {
564                                    inner: PixelValue::const_px(12),
565                                },
566                                clip_mode: BoxShadowClipMode::Inset,
567                            })),
568                        )),
569                        CssPropertyWithConditions::simple(CssProperty::BorderBottomRightRadius(
570                            StyleBorderBottomRightRadiusValue::Exact(
571                                StyleBorderBottomRightRadius {
572                                    inner: PixelValue::const_px(1),
573                                },
574                            ),
575                        )),
576                        CssPropertyWithConditions::simple(CssProperty::BorderBottomLeftRadius(
577                            StyleBorderBottomLeftRadiusValue::Exact(StyleBorderBottomLeftRadius {
578                                inner: PixelValue::const_px(1),
579                            }),
580                        )),
581                        CssPropertyWithConditions::simple(CssProperty::BorderTopRightRadius(
582                            StyleBorderTopRightRadiusValue::Exact(StyleBorderTopRightRadius {
583                                inner: PixelValue::const_px(1),
584                            }),
585                        )),
586                        CssPropertyWithConditions::simple(CssProperty::BorderTopLeftRadius(
587                            StyleBorderTopLeftRadiusValue::Exact(StyleBorderTopLeftRadius {
588                                inner: PixelValue::const_px(1),
589                            }),
590                        )),
591                        CssPropertyWithConditions::simple(CssProperty::BackgroundContent(
592                            StyleBackgroundContentVecValue::Exact(self.bar_background),
593                        )),
594                    ]))
595                    .with_ids_and_classes({
596                        const IDS_AND_CLASSES_16512648314570682783: &[IdOrClass] = &[Class(
597                            AzString::from_const_str("__azul-native-progress-bar-bar"),
598                        )];
599                        IdOrClassVec::from_const_slice(IDS_AND_CLASSES_16512648314570682783)
600                    }),
601                Dom::create_div()
602                    .with_css_props(CssPropertyWithConditionsVec::from_vec(vec![
603                        // .__azul-native-progress-bar-remaining
604                        // Use percentage width for the remaining space
605                        CssPropertyWithConditions::simple(CssProperty::Width(
606                            LayoutWidthValue::Exact(LayoutWidth::Px(
607                                PixelValue::percent(100.0 - percent_done),
608                            )),
609                        )),
610                    ]))
611                    .with_ids_and_classes({
612                        const IDS_AND_CLASSES_2492405364126620395: &[IdOrClass] = &[Class(
613                            AzString::from_const_str("__azul-native-progress-bar-remaining"),
614                        )];
615                        IdOrClassVec::from_const_slice(IDS_AND_CLASSES_2492405364126620395)
616                    }),
617            ]))
618    }
619}