progress_bar.rs

  1use documented::Documented;
  2use gpui::{Hsla, Role, 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: cx.theme().colors().background,
 27            over_color: cx.theme().status().error,
 28            fg_color: cx.theme().status().info,
 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            .role(Role::ProgressIndicator)
 70            .aria_numeric_value(self.value as f64)
 71            .aria_min_numeric_value(0.0)
 72            .aria_max_numeric_value(self.max_value as f64)
 73            .w_full()
 74            .h_2()
 75            .p_0p5()
 76            .rounded_full()
 77            .bg(self.bg_color)
 78            .shadow(vec![gpui::BoxShadow {
 79                color: gpui::black().opacity(0.08),
 80                offset: point(px(0.), px(1.)),
 81                blur_radius: px(0.),
 82                spread_radius: px(0.),
 83            }])
 84            .child(
 85                div()
 86                    .h_full()
 87                    .rounded_full()
 88                    .when(self.value > self.max_value, |div| div.bg(self.over_color))
 89                    .when(self.value <= self.max_value, |div| div.bg(self.fg_color))
 90                    .w(relative(fill_width)),
 91            )
 92    }
 93}
 94
 95impl Component for ProgressBar {
 96    fn scope() -> ComponentScope {
 97        ComponentScope::Status
 98    }
 99
100    fn description() -> Option<&'static str> {
101        Some(Self::DOCS)
102    }
103
104    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
105        let max_value = 180.0;
106        let container = || v_flex().w_full().gap_1();
107
108        Some(
109            example_group(vec![single_example(
110                "Examples",
111                v_flex()
112                    .w_full()
113                    .gap_2()
114                    .child(
115                        container()
116                            .child(
117                                h_flex()
118                                    .justify_between()
119                                    .child(Label::new("0%"))
120                                    .child(Label::new("Empty")),
121                            )
122                            .child(ProgressBar::new("empty", 0.0, max_value, cx)),
123                    )
124                    .child(
125                        container()
126                            .child(
127                                h_flex()
128                                    .justify_between()
129                                    .child(Label::new("38%"))
130                                    .child(Label::new("Partial")),
131                            )
132                            .child(ProgressBar::new("partial", max_value * 0.35, max_value, cx)),
133                    )
134                    .child(
135                        container()
136                            .child(
137                                h_flex()
138                                    .justify_between()
139                                    .child(Label::new("100%"))
140                                    .child(Label::new("Complete")),
141                            )
142                            .child(ProgressBar::new("filled", max_value, max_value, cx)),
143                    )
144                    .into_any_element(),
145            )])
146            .into_any_element(),
147        )
148    }
149}