Skip to main content

ui/components/progress/
progress_bar.rs

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    over_color: Hsla,
17    fg_color: Hsla,
18}
19
20impl ProgressBar {
21    pub fn new(id: impl Into<ElementId>, value: f32, max_value: f32, cx: &App) -> Self {
22        Self {
23            id: id.into(),
24            value,
25            max_value,
26            bg_color: semantic::border_muted(cx),
27            over_color: palette::danger(600),
28            fg_color: palette::primary(600),
29        }
30    }
31
32    /// Sets the current value of the progress bar.
33    pub fn value(mut self, value: f32) -> Self {
34        self.value = value;
35        self
36    }
37
38    /// Sets the maximum value of the progress bar.
39    pub fn max_value(mut self, max_value: f32) -> Self {
40        self.max_value = max_value;
41        self
42    }
43
44    /// Sets the background color of the progress bar.
45    pub fn bg_color(mut self, color: Hsla) -> Self {
46        self.bg_color = color;
47        self
48    }
49
50    /// Sets the foreground color of the progress bar.
51    pub fn fg_color(mut self, color: Hsla) -> Self {
52        self.fg_color = color;
53        self
54    }
55
56    /// Sets the over limit color of the progress bar.
57    pub fn over_color(mut self, color: Hsla) -> Self {
58        self.over_color = color;
59        self
60    }
61}
62
63impl RenderOnce for ProgressBar {
64    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
65        let fill_width = (self.value / self.max_value).clamp(0.02, 1.0);
66
67        div()
68            .id(self.id.clone())
69            .w_full()
70            .h_2()
71            .p_0p5()
72            .rounded_full()
73            .bg(self.bg_color)
74            .shadow(vec![gpui::BoxShadow {
75                color: gpui::black().opacity(0.08),
76                offset: point(px(0.), px(1.)),
77                blur_radius: px(0.),
78                spread_radius: px(0.),
79            }])
80            .child(
81                div()
82                    .h_full()
83                    .rounded_full()
84                    .when(self.value > self.max_value, |div| div.bg(self.over_color))
85                    .when(self.value <= self.max_value, |div| div.bg(self.fg_color))
86                    .w(relative(fill_width)),
87            )
88    }
89}
90
91impl Component for ProgressBar {
92    fn scope() -> ComponentScope {
93        ComponentScope::Status
94    }
95
96    fn description() -> Option<&'static str> {
97        Some(Self::DOCS)
98    }
99
100    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
101        let max_value = 180.0;
102        let container = || v_flex().w_full().gap_1();
103
104        Some(
105            example_group(vec![single_example(
106                "Examples",
107                v_flex()
108                    .w_full()
109                    .gap_2()
110                    .child(
111                        container()
112                            .child(
113                                h_flex()
114                                    .justify_between()
115                                    .child(Label::new("0%"))
116                                    .child(Label::new("Empty")),
117                            )
118                            .child(ProgressBar::new("empty", 0.0, max_value, cx)),
119                    )
120                    .child(
121                        container()
122                            .child(
123                                h_flex()
124                                    .justify_between()
125                                    .child(Label::new("38%"))
126                                    .child(Label::new("Partial")),
127                            )
128                            .child(ProgressBar::new("partial", max_value * 0.35, max_value, cx)),
129                    )
130                    .child(
131                        container()
132                            .child(
133                                h_flex()
134                                    .justify_between()
135                                    .child(Label::new("100%"))
136                                    .child(Label::new("Complete")),
137                            )
138                            .child(ProgressBar::new("filled", max_value, max_value, cx)),
139                    )
140                    .into_any_element(),
141            )])
142            .into_any_element(),
143        )
144    }
145}