1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! A linear progress bar component.
//!
//! ## Usage
//!
//! Use to indicate the completion of a task or a specific value in a range.
use derive_builder::Builder;
use tessera_ui::{Color, ComputedData, Constraint, DimensionValue, Dp, Px, PxPosition, tessera};
use crate::{
shape_def::Shape,
surface::{SurfaceArgsBuilder, surface},
};
/// Arguments for the `progress` component.
#[derive(Builder, Clone, Debug)]
#[builder(pattern = "owned")]
pub struct ProgressArgs {
/// The current value of the progress bar, ranging from 0.0 to 1.0.
#[builder(default = "0.0")]
pub value: f32,
/// The width of the progress bar.
#[builder(default = "Dp(200.0)")]
pub width: Dp,
/// The height of the progress bar.
#[builder(default = "Dp(8.0)")]
pub height: Dp,
/// The color of the active part of the track.
#[builder(default = "Color::new(0.2, 0.5, 0.8, 1.0)")]
pub progress_color: Color,
/// The color of the inactive part of the track.
#[builder(default = "Color::new(0.8, 0.8, 0.8, 1.0)")]
pub track_color: Color,
}
/// # progress
///
/// Renders a linear progress indicator that visualizes a value from 0.0 to 1.0.
///
/// ## Usage
///
/// Display the status of an ongoing operation, such as a download or a setup process.
///
/// ## Parameters
///
/// - `args` — configures the progress bar's value and appearance; see [`ProgressArgs`].
///
/// ## Examples
///
/// ```
/// use tessera_ui_basic_components::progress::{progress, ProgressArgsBuilder};
///
/// // Creates a progress bar that is 75% complete.
/// progress(
/// ProgressArgsBuilder::default()
/// .value(0.75)
/// .build()
/// .unwrap(),
/// );
/// ```
#[tessera]
pub fn progress(args: impl Into<ProgressArgs>) {
let args: ProgressArgs = args.into();
let radius_dp = Dp(args.height.0 / 2.0);
// Child 1: The background track. It's drawn first.
surface(
SurfaceArgsBuilder::default()
.style(args.track_color.into())
.shape({
Shape::RoundedRectangle {
top_left: radius_dp,
top_right: radius_dp,
bottom_right: radius_dp,
bottom_left: radius_dp,
g2_k_value: 2.0,
}
})
.width(DimensionValue::Fill {
min: None,
max: None,
})
.height(DimensionValue::Fill {
min: None,
max: None,
})
.build()
.unwrap(),
None,
|| {},
);
// Child 2: The progress fill. It's drawn on top of the track.
surface(
SurfaceArgsBuilder::default()
.style(args.progress_color.into())
.shape({
Shape::RoundedRectangle {
top_left: radius_dp,
top_right: radius_dp,
bottom_right: radius_dp,
bottom_left: radius_dp,
g2_k_value: 2.0,
}
})
.width(DimensionValue::Fill {
min: None,
max: None,
})
.height(DimensionValue::Fill {
min: None,
max: None,
})
.build()
.unwrap(),
None,
|| {},
);
measure(Box::new(move |input| {
let self_width = args.width.to_px();
let self_height = args.height.to_px();
let track_id = input.children_ids[0];
let progress_id = input.children_ids[1];
// Measure and place the background track to take the full size of the component.
let track_constraint = Constraint::new(
DimensionValue::Fixed(self_width),
DimensionValue::Fixed(self_height),
);
input.measure_child(track_id, &track_constraint)?;
input.place_child(track_id, PxPosition::new(Px(0), Px(0)));
// Measure and place the progress fill based on the `value`.
let clamped_value = args.value.clamp(0.0, 1.0);
let progress_width = Px::saturating_from_f32(self_width.to_f32() * clamped_value);
let progress_constraint = Constraint::new(
DimensionValue::Fixed(progress_width),
DimensionValue::Fixed(self_height),
);
input.measure_child(progress_id, &progress_constraint)?;
input.place_child(progress_id, PxPosition::new(Px(0), Px(0)));
// The progress component itself is a container, its size is defined by the args.
Ok(ComputedData {
width: self_width,
height: self_height,
})
}));
}