tessera_ui_basic_components/progress.rs
1//! This module provides a customizable linear progress bar component for visualizing task completion.
2//!
3//! The `progress` component displays a horizontal bar with configurable width, height, colors, and shape,
4//! where the filled portion represents the current progress value (from 0.0 to 1.0).
5//! It is suitable for indicating the status of ongoing operations such as loading, uploading, or processing tasks
6//! in user interfaces.
7//!
8//! Typical usage involves specifying the progress value and optional appearance parameters.
9//! The component is designed for integration into Tessera UI applications.
10use derive_builder::Builder;
11use tessera_ui::{Color, ComputedData, Constraint, DimensionValue, Dp, Px, PxPosition};
12use tessera_ui_macros::tessera;
13
14use crate::{
15 shape_def::Shape,
16 surface::{SurfaceArgsBuilder, surface},
17};
18
19/// Arguments for the `progress` component.
20#[derive(Builder, Clone, Debug)]
21#[builder(pattern = "owned")]
22pub struct ProgressArgs {
23 /// The current value of the progress bar, ranging from 0.0 to 1.0.
24 #[builder(default = "0.0")]
25 pub value: f32,
26
27 /// The width of the progress bar.
28 #[builder(default = "Dp(200.0)")]
29 pub width: Dp,
30
31 /// The height of the progress bar.
32 #[builder(default = "Dp(8.0)")]
33 pub height: Dp,
34
35 /// The color of the active part of the track.
36 #[builder(default = "Color::new(0.2, 0.5, 0.8, 1.0)")]
37 pub progress_color: Color,
38
39 /// The color of the inactive part of the track.
40 #[builder(default = "Color::new(0.8, 0.8, 0.8, 1.0)")]
41 pub track_color: Color,
42}
43
44#[tessera]
45/// Draws a linear progress indicator that visualizes the completion of a task.
46///
47/// The `progress` component consists of a track and a fill, where the fill
48/// represents the current progress value.
49///
50/// # Arguments
51///
52/// * `value`: A float between `0.0` and `1.0` representing the current progress.
53/// Values outside this range will be clamped.
54///
55/// # Example
56///
57/// ```
58/// # use tessera_ui_basic_components::progress::{progress, ProgressArgsBuilder};
59/// #
60/// // Creates a progress bar that is 75% complete.
61/// progress(
62/// ProgressArgsBuilder::default()
63/// .value(0.75)
64/// .build()
65/// .unwrap(),
66/// );
67/// ```
68pub fn progress(args: impl Into<ProgressArgs>) {
69 let args: ProgressArgs = args.into();
70
71 // Child 1: The background track. It's drawn first.
72 surface(
73 SurfaceArgsBuilder::default()
74 .color(args.track_color)
75 .shape(Shape::RoundedRectangle {
76 corner_radius: args.height.to_px().to_f32() / 2.0,
77 g2_k_value: 2.0,
78 })
79 .width(DimensionValue::Fill {
80 min: None,
81 max: None,
82 })
83 .height(DimensionValue::Fill {
84 min: None,
85 max: None,
86 })
87 .build()
88 .unwrap(),
89 None,
90 || {},
91 );
92
93 // Child 2: The progress fill. It's drawn on top of the track.
94 surface(
95 SurfaceArgsBuilder::default()
96 .color(args.progress_color)
97 .shape(Shape::RoundedRectangle {
98 corner_radius: args.height.to_px().to_f32() / 2.0,
99 g2_k_value: 2.0,
100 })
101 .width(DimensionValue::Fill {
102 min: None,
103 max: None,
104 })
105 .height(DimensionValue::Fill {
106 min: None,
107 max: None,
108 })
109 .build()
110 .unwrap(),
111 None,
112 || {},
113 );
114
115 measure(Box::new(move |input| {
116 let self_width = args.width.to_px();
117 let self_height = args.height.to_px();
118
119 let track_id = input.children_ids[0];
120 let progress_id = input.children_ids[1];
121
122 // Measure and place the background track to take the full size of the component.
123 let track_constraint = Constraint::new(
124 DimensionValue::Fixed(self_width),
125 DimensionValue::Fixed(self_height),
126 );
127 input.measure_child(track_id, &track_constraint)?;
128 input.place_child(track_id, PxPosition::new(Px(0), Px(0)));
129
130 // Measure and place the progress fill based on the `value`.
131 let progress_width = Px((self_width.to_f32() * args.value.clamp(0.0, 1.0)) as i32);
132 let progress_constraint = Constraint::new(
133 DimensionValue::Fixed(progress_width),
134 DimensionValue::Fixed(self_height),
135 );
136 input.measure_child(progress_id, &progress_constraint)?;
137 input.place_child(progress_id, PxPosition::new(Px(0), Px(0)));
138
139 // The progress component itself is a container, its size is defined by the args.
140 Ok(ComputedData {
141 width: self_width,
142 height: self_height,
143 })
144 }));
145}