tessera_ui_basic_components/
glass_progress.rs

1//! Provides a glassmorphism-style progress bar component for visualizing task completion.
2//!
3//! The `glass_progress` module implements a customizable, frosted glass effect progress bar,
4//! featuring a blurred background, tint colors, and borders. It is designed to display a
5//! progress value from 0.0 to 1.0, making it suitable for loading screens, dashboards, or
6//! any interface requiring a modern and visually appealing progress indicator.
7
8use derive_builder::Builder;
9use tessera_ui::{Color, ComputedData, Constraint, DimensionValue, Dp, Px, PxPosition};
10use tessera_ui_macros::tessera;
11
12use crate::{
13    fluid_glass::{FluidGlassArgsBuilder, GlassBorder, fluid_glass},
14    shape_def::Shape,
15};
16
17/// Arguments for the `glass_progress` component.
18#[derive(Builder, Clone, Debug)]
19#[builder(pattern = "owned")]
20pub struct GlassProgressArgs {
21    /// The current value of the progress bar, ranging from 0.0 to 1.0.
22    #[builder(default = "0.0")]
23    pub value: f32,
24
25    /// The width of the progress bar.
26    #[builder(default = "Dp(200.0)")]
27    pub width: Dp,
28
29    /// The height of the progress bar.
30    #[builder(default = "Dp(12.0)")]
31    pub height: Dp,
32
33    /// Glass tint color for the track background.
34    #[builder(default = "Color::new(0.3, 0.3, 0.3, 0.15)")]
35    pub track_tint_color: Color,
36
37    /// Glass tint color for the progress fill.
38    #[builder(default = "Color::new(0.5, 0.7, 1.0, 0.25)")]
39    pub progress_tint_color: Color,
40
41    /// Glass blur radius for all components.
42    #[builder(default = "8.0")]
43    pub blur_radius: f32,
44
45    /// Border width for the track.
46    #[builder(default = "Dp(1.0)")]
47    pub track_border_width: Dp,
48}
49
50/// Creates a progress bar component with a frosted glass effect.
51///
52/// The `glass_progress` displays a value from a continuous range (0.0 to 1.0)
53/// with a modern, semi-transparent "glassmorphism" aesthetic, including a
54/// blurred background and subtle highlights.
55///
56/// # Arguments
57///
58/// * `args` - An instance of `GlassProgressArgs` or `GlassProgressArgsBuilder`
59///   to configure the progress bar's appearance.
60///   - `value`: The current progress value, must be between 0.0 and 1.0.
61///
62/// # Example
63///
64/// ```rust,no_run
65/// use tessera_ui_basic_components::glass_progress::{glass_progress, GlassProgressArgsBuilder};
66///
67/// // In your component function
68/// glass_progress(
69///     GlassProgressArgsBuilder::default()
70///         .value(0.75)
71///         .build()
72///         .unwrap(),
73/// );
74/// ```
75#[tessera]
76pub fn glass_progress(args: impl Into<GlassProgressArgs>) {
77    let args: GlassProgressArgs = args.into();
78
79    // External track (background) with border - capsule shape
80    fluid_glass(
81        FluidGlassArgsBuilder::default()
82            .width(DimensionValue::Fixed(args.width.to_px()))
83            .height(DimensionValue::Fixed(args.height.to_px()))
84            .tint_color(args.track_tint_color)
85            .blur_radius(args.blur_radius)
86            .shape(Shape::RoundedRectangle {
87                corner_radius: args.height.to_px().to_f32() / 2.0,
88                g2_k_value: 2.0, // Capsule shape
89            })
90            .border(GlassBorder::new(args.track_border_width.into()))
91            .padding(args.track_border_width)
92            .build()
93            .unwrap(),
94        None,
95        move || {
96            // Internal progress fill - capsule shape
97            let progress_width = (args.width.to_px().to_f32() * args.value.clamp(0.0, 1.0))
98                - (args.track_border_width.to_px().to_f32() * 2.0);
99            let effective_height =
100                args.height.to_px().to_f32() - (args.track_border_width.to_px().to_f32() * 2.0);
101
102            if progress_width > 0.0 {
103                fluid_glass(
104                    FluidGlassArgsBuilder::default()
105                        .width(DimensionValue::Fixed(Px(progress_width as i32)))
106                        .height(DimensionValue::Fill {
107                            min: None,
108                            max: None,
109                        })
110                        .tint_color(args.progress_tint_color)
111                        .shape(Shape::RoundedRectangle {
112                            corner_radius: effective_height / 2.0,
113                            g2_k_value: 2.0, // Capsule shape
114                        })
115                        .refraction_amount(0.0)
116                        .build()
117                        .unwrap(),
118                    None,
119                    || {},
120                );
121            }
122        },
123    );
124
125    measure(Box::new(move |input| {
126        let self_width = args.width.to_px();
127        let self_height = args.height.to_px();
128
129        let track_id = input.children_ids[0];
130
131        // Measure track
132        let track_constraint = Constraint::new(
133            DimensionValue::Fixed(self_width),
134            DimensionValue::Fixed(self_height),
135        );
136        input.measure_child(track_id, &track_constraint)?;
137        input.place_child(track_id, PxPosition::new(Px(0), Px(0)));
138
139        Ok(ComputedData {
140            width: self_width,
141            height: self_height,
142        })
143    }));
144}