tessera_ui_basic_components/
glass_progress.rs1use derive_builder::Builder;
7use tessera_ui::{Color, ComputedData, Constraint, DimensionValue, Dp, Px, PxPosition, tessera};
8
9use crate::{
10 fluid_glass::{FluidGlassArgsBuilder, GlassBorder, fluid_glass},
11 shape_def::Shape,
12};
13
14#[derive(Builder, Clone, Debug)]
16#[builder(pattern = "owned")]
17pub struct GlassProgressArgs {
18 #[builder(default = "0.0")]
20 pub value: f32,
21
22 #[builder(default = "Dp(200.0)")]
24 pub width: Dp,
25
26 #[builder(default = "Dp(12.0)")]
28 pub height: Dp,
29
30 #[builder(default = "Color::new(0.3, 0.3, 0.3, 0.15)")]
32 pub track_tint_color: Color,
33
34 #[builder(default = "Color::new(0.5, 0.7, 1.0, 0.25)")]
36 pub progress_tint_color: Color,
37
38 #[builder(default = "Dp(8.0)")]
40 pub blur_radius: Dp,
41
42 #[builder(default = "Dp(1.0)")]
44 pub track_border_width: Dp,
45}
46
47fn capsule_shape_for_height(height: Dp) -> Shape {
49 let radius = Dp(height.0 / 2.0);
50 Shape::RoundedRectangle {
51 top_left: radius,
52 top_right: radius,
53 bottom_right: radius,
54 bottom_left: radius,
55 g2_k_value: 2.0,
56 }
57}
58
59fn compute_progress_dims(args: &GlassProgressArgs) -> Option<(Px, f32)> {
62 let progress_width = (args.width.to_px().to_f32() * args.value.clamp(0.0, 1.0))
63 - (args.track_border_width.to_px().to_f32() * 2.0);
64 let effective_height =
65 args.height.to_px().to_f32() - (args.track_border_width.to_px().to_f32() * 2.0);
66
67 if progress_width > 0.0 {
68 Some((Px(progress_width as i32), effective_height))
69 } else {
70 None
71 }
72}
73
74fn render_track_and_fill(args: GlassProgressArgs) {
77 fluid_glass(
78 FluidGlassArgsBuilder::default()
79 .width(DimensionValue::Fixed(args.width.to_px()))
80 .height(DimensionValue::Fixed(args.height.to_px()))
81 .tint_color(args.track_tint_color)
82 .blur_radius(args.blur_radius)
83 .shape(capsule_shape_for_height(args.height))
84 .border(GlassBorder::new(args.track_border_width.into()))
85 .padding(args.track_border_width)
86 .build()
87 .unwrap(),
88 None,
89 move || {
90 if let Some((progress_px, effective_height)) = compute_progress_dims(&args) {
92 fluid_glass(
93 FluidGlassArgsBuilder::default()
94 .width(DimensionValue::Fixed(progress_px))
95 .height(DimensionValue::Fill {
96 min: None,
97 max: None,
98 })
99 .tint_color(args.progress_tint_color)
100 .shape(capsule_shape_for_height(Dp::from_pixels_f32(
101 effective_height,
102 )))
103 .refraction_amount(0.0)
104 .build()
105 .unwrap(),
106 None,
107 || {},
108 );
109 }
110 },
111 );
112}
113
114#[tessera]
140pub fn glass_progress(args: impl Into<GlassProgressArgs>) {
141 let args: GlassProgressArgs = args.into();
142
143 let args_for_render = args.clone();
145 render_track_and_fill(args_for_render);
146
147 measure(Box::new(move |input| {
148 let self_width = args.width.to_px();
149 let self_height = args.height.to_px();
150
151 let track_id = input.children_ids[0];
152
153 let track_constraint = Constraint::new(
155 DimensionValue::Fixed(self_width),
156 DimensionValue::Fixed(self_height),
157 );
158 input.measure_child(track_id, &track_constraint)?;
159 input.place_child(track_id, PxPosition::new(Px(0), Px(0)));
160
161 Ok(ComputedData {
162 width: self_width,
163 height: self_height,
164 })
165 }));
166}