1use gpui::prelude::*;
6use gpui::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum ProgressVariant {
11 #[default]
13 Default,
14 Success,
16 Warning,
18 Error,
20}
21
22impl ProgressVariant {
23 fn color(&self) -> Rgba {
24 match self {
25 ProgressVariant::Default => rgb(0x007acc),
26 ProgressVariant::Success => rgb(0x2da44e),
27 ProgressVariant::Warning => rgb(0xd29922),
28 ProgressVariant::Error => rgb(0xcc3333),
29 }
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub enum ProgressSize {
36 Xs,
38 Sm,
40 #[default]
42 Md,
43 Lg,
45}
46
47impl ProgressSize {
48 fn height(&self) -> Pixels {
49 match self {
50 ProgressSize::Xs => px(2.0),
51 ProgressSize::Sm => px(4.0),
52 ProgressSize::Md => px(8.0),
53 ProgressSize::Lg => px(12.0),
54 }
55 }
56}
57
58pub struct Progress {
60 value: f32,
61 max: f32,
62 variant: ProgressVariant,
63 size: ProgressSize,
64 show_label: bool,
65 striped: bool,
66 animated: bool,
67}
68
69impl Progress {
70 pub fn new(value: f32) -> Self {
72 Self {
73 value,
74 max: 100.0,
75 variant: ProgressVariant::default(),
76 size: ProgressSize::default(),
77 show_label: false,
78 striped: false,
79 animated: false,
80 }
81 }
82
83 pub fn max(mut self, max: f32) -> Self {
85 self.max = max;
86 self
87 }
88
89 pub fn variant(mut self, variant: ProgressVariant) -> Self {
91 self.variant = variant;
92 self
93 }
94
95 pub fn size(mut self, size: ProgressSize) -> Self {
97 self.size = size;
98 self
99 }
100
101 pub fn show_label(mut self, show: bool) -> Self {
103 self.show_label = show;
104 self
105 }
106
107 pub fn striped(mut self, striped: bool) -> Self {
109 self.striped = striped;
110 self
111 }
112
113 pub fn animated(mut self, animated: bool) -> Self {
115 self.animated = animated;
116 self
117 }
118
119 pub fn build(self) -> Div {
121 let height = self.size.height();
122 let color = self.variant.color();
123 let percentage = (self.value / self.max * 100.0).clamp(0.0, 100.0);
124
125 let mut container = div().flex().flex_col().gap_1().w_full();
126
127 if self.show_label {
129 container = container.child(
130 div()
131 .flex()
132 .justify_between()
133 .text_xs()
134 .text_color(rgb(0xcccccc))
135 .child(format!("{:.0}%", percentage)),
136 );
137 }
138
139 let track = div()
141 .w_full()
142 .h(height)
143 .bg(rgb(0x2a2a2a))
144 .rounded_full()
145 .overflow_hidden()
146 .child(
147 div()
148 .h_full()
149 .bg(color)
150 .rounded_full()
151 .w(relative(percentage / 100.0)),
152 );
153
154 container = container.child(track);
155
156 container
157 }
158}
159
160impl IntoElement for Progress {
161 type Element = Div;
162
163 fn into_element(self) -> Self::Element {
164 self.build()
165 }
166}
167
168pub struct CircularProgress {
170 value: f32,
171 max: f32,
172 size: Pixels,
173 thickness: Pixels,
174 variant: ProgressVariant,
175 show_label: bool,
176}
177
178impl CircularProgress {
179 pub fn new(value: f32) -> Self {
181 Self {
182 value,
183 max: 100.0,
184 size: px(48.0),
185 thickness: px(4.0),
186 variant: ProgressVariant::default(),
187 show_label: false,
188 }
189 }
190
191 pub fn max(mut self, max: f32) -> Self {
193 self.max = max;
194 self
195 }
196
197 pub fn size(mut self, size: Pixels) -> Self {
199 self.size = size;
200 self
201 }
202
203 pub fn thickness(mut self, thickness: Pixels) -> Self {
205 self.thickness = thickness;
206 self
207 }
208
209 pub fn variant(mut self, variant: ProgressVariant) -> Self {
211 self.variant = variant;
212 self
213 }
214
215 pub fn show_label(mut self, show: bool) -> Self {
217 self.show_label = show;
218 self
219 }
220
221 pub fn build(self) -> Div {
225 let percentage = (self.value / self.max * 100.0).clamp(0.0, 100.0);
226 let color = self.variant.color();
227
228 let mut container = div()
229 .flex()
230 .items_center()
231 .justify_center()
232 .w(self.size)
233 .h(self.size)
234 .rounded_full()
235 .border(self.thickness)
236 .border_color(rgb(0x2a2a2a))
237 .relative();
238
239 if percentage > 0.0 {
242 container = container.border_color(color);
243 }
244
245 if self.show_label {
247 container = container.child(
248 div()
249 .text_xs()
250 .font_weight(FontWeight::BOLD)
251 .text_color(rgb(0xcccccc))
252 .child(format!("{:.0}%", percentage)),
253 );
254 }
255
256 container
257 }
258}
259
260impl IntoElement for CircularProgress {
261 type Element = Div;
262
263 fn into_element(self) -> Self::Element {
264 self.build()
265 }
266}