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