1use documented::Documented;
2use gpui::{Hsla, point};
3
4use crate::components::Label;
5use crate::prelude::*;
6
7/// A progress bar is a horizontal bar that communicates the status of a process.
8///
9/// A progress bar should not be used to represent indeterminate progress.
10#[derive(IntoElement, RegisterComponent, Documented)]
11pub struct ProgressBar {
12 id: ElementId,
13 value: f32,
14 max_value: f32,
15 bg_color: Hsla,
16 fg_color: Hsla,
17}
18
19impl ProgressBar {
20 pub fn new(id: impl Into<ElementId>, value: f32, max_value: f32, cx: &App) -> Self {
21 Self {
22 id: id.into(),
23 value,
24 max_value,
25 bg_color: cx.theme().colors().background,
26 fg_color: cx.theme().status().info,
27 }
28 }
29
30 /// Sets the current value of the progress bar.
31 pub fn value(mut self, value: f32) -> Self {
32 self.value = value;
33 self
34 }
35
36 /// Sets the maximum value of the progress bar.
37 pub fn max_value(mut self, max_value: f32) -> Self {
38 self.max_value = max_value;
39 self
40 }
41
42 /// Sets the background color of the progress bar.
43 pub fn bg_color(mut self, color: Hsla) -> Self {
44 self.bg_color = color;
45 self
46 }
47
48 /// Sets the foreground color of the progress bar.
49 pub fn fg_color(mut self, color: Hsla) -> Self {
50 self.fg_color = color;
51 self
52 }
53}
54
55impl RenderOnce for ProgressBar {
56 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
57 let fill_width = (self.value / self.max_value).clamp(0.02, 1.0);
58
59 div()
60 .id(self.id.clone())
61 .w_full()
62 .h(px(8.0))
63 .rounded_full()
64 .py(px(2.0))
65 .px(px(4.0))
66 .bg(self.bg_color)
67 .shadow(smallvec::smallvec![gpui::BoxShadow {
68 color: gpui::black().opacity(0.08),
69 offset: point(px(0.), px(1.)),
70 blur_radius: px(0.),
71 spread_radius: px(0.),
72 }])
73 .child(
74 div()
75 .h_full()
76 .rounded_full()
77 .bg(self.fg_color)
78 .w(relative(fill_width)),
79 )
80 }
81}
82
83impl Component for ProgressBar {
84 fn scope() -> ComponentScope {
85 ComponentScope::Status
86 }
87
88 fn description() -> Option<&'static str> {
89 Some(Self::DOCS)
90 }
91
92 fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
93 let max_value = 180.0;
94
95 Some(
96 div()
97 .flex()
98 .flex_col()
99 .gap_4()
100 .p_4()
101 .w(px(240.0))
102 .child(div().child("Progress Bar"))
103 .child(
104 div()
105 .flex()
106 .flex_col()
107 .gap_2()
108 .child(
109 div()
110 .flex()
111 .justify_between()
112 .child(Label::new("0%"))
113 .child(Label::new("Empty")),
114 )
115 .child(ProgressBar::new("empty", 0.0, max_value, cx)),
116 )
117 .child(
118 div()
119 .flex()
120 .flex_col()
121 .gap_2()
122 .child(
123 div()
124 .flex()
125 .justify_between()
126 .child(Label::new("38%"))
127 .child(Label::new("Partial")),
128 )
129 .child(ProgressBar::new("partial", max_value * 0.35, max_value, cx)),
130 )
131 .child(
132 div()
133 .flex()
134 .flex_col()
135 .gap_2()
136 .child(
137 div()
138 .flex()
139 .justify_between()
140 .child(Label::new("100%"))
141 .child(Label::new("Complete")),
142 )
143 .child(ProgressBar::new("filled", max_value, max_value, cx)),
144 )
145 .into_any_element(),
146 )
147 }
148}