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

            
5
use azul_core::dom::{Dom, IdOrClass, IdOrClass::Class, IdOrClassVec};
6
use azul_css::{
7
    dynamic_selector::{CssPropertyWithConditions, CssPropertyWithConditionsVec},
8
    props::{
9
        basic::*,
10
        layout::*,
11
        property::{CssProperty, *},
12
        style::*,
13
    },
14
    *,
15
};
16
use azul_css::css::BoxOrStatic;
17

            
18
const 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
    })];
29
const 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
    })];
40
const 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
];
105
const 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)]
174
pub 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)]
184
pub struct ProgressBarState {
185
    pub percent_done: f32,
186
    pub display_percentage: bool,
187
}
188

            
189
impl 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
}