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