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::{RoundedCorner, 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 = "DimensionValue::Fixed(Dp(200.0).to_px())")]
24 pub width: DimensionValue,
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: RoundedCorner::manual(radius, 2.0),
52 top_right: RoundedCorner::manual(radius, 2.0),
53 bottom_right: RoundedCorner::manual(radius, 2.0),
54 bottom_left: RoundedCorner::manual(radius, 2.0),
55 }
56}
57
58fn compute_progress_dims(args: &GlassProgressArgs, width_px: Px) -> Option<(Px, f32)> {
61 let progress_width = (width_px.to_f32() * args.value.clamp(0.0, 1.0))
62 - (args.track_border_width.to_px().to_f32() * 2.0);
63 let effective_height =
64 args.height.to_px().to_f32() - (args.track_border_width.to_px().to_f32() * 2.0);
65
66 if progress_width > 0.0 {
67 Some((Px(progress_width as i32), effective_height))
68 } else {
69 None
70 }
71}
72
73fn resolve_width_px(args: &GlassProgressArgs, parent: Option<&Constraint>) -> Px {
74 let fallback = Dp(200.0).to_px();
75 let base = Constraint::new(args.width, DimensionValue::Fixed(args.height.to_px()));
76 let merged = match parent {
77 Some(parent_constraint) => base.merge(parent_constraint),
78 None => base,
79 };
80
81 match merged.width {
82 DimensionValue::Fixed(px) => px,
83 DimensionValue::Fill { max, .. } | DimensionValue::Wrap { max, .. } => {
84 max.unwrap_or(fallback)
85 }
86 }
87}
88
89fn render_track_and_fill(args: GlassProgressArgs, width_px: Px) {
92 fluid_glass(
93 FluidGlassArgsBuilder::default()
94 .width(DimensionValue::Fixed(width_px))
95 .height(DimensionValue::Fixed(args.height.to_px()))
96 .tint_color(args.track_tint_color)
97 .blur_radius(args.blur_radius)
98 .shape(capsule_shape_for_height(args.height))
99 .border(GlassBorder::new(args.track_border_width.into()))
100 .padding(args.track_border_width)
101 .build()
102 .expect("builder construction failed"),
103 None,
104 move || {
105 if let Some((progress_px, effective_height)) = compute_progress_dims(&args, width_px) {
107 fluid_glass(
108 FluidGlassArgsBuilder::default()
109 .width(DimensionValue::Fixed(progress_px))
110 .height(DimensionValue::Fill {
111 min: None,
112 max: None,
113 })
114 .tint_color(args.progress_tint_color)
115 .shape(capsule_shape_for_height(Dp::from_pixels_f32(
116 effective_height,
117 )))
118 .refraction_amount(0.0)
119 .build()
120 .expect("builder construction failed"),
121 None,
122 || {},
123 );
124 }
125 },
126 );
127}
128
129#[tessera]
155pub fn glass_progress(args: impl Into<GlassProgressArgs>) {
156 let args: GlassProgressArgs = args.into();
157 let fallback_width = resolve_width_px(&args, None);
158
159 let args_for_render = args.clone();
161 render_track_and_fill(args_for_render, fallback_width);
162
163 measure(Box::new(move |input| {
164 let self_width = resolve_width_px(&args, Some(input.parent_constraint));
165 let self_height = args.height.to_px();
166
167 let track_id = input.children_ids[0];
168
169 let track_constraint = Constraint::new(
171 DimensionValue::Fixed(self_width),
172 DimensionValue::Fixed(self_height),
173 );
174 input.measure_child(track_id, &track_constraint)?;
175 input.place_child(track_id, PxPosition::new(Px(0), Px(0)));
176
177 Ok(ComputedData {
178 width: self_width,
179 height: self_height,
180 })
181 }));
182}