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: 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            .w_full()
 70            .h(px(8.0))
 71            .rounded_full()
 72            .py(px(2.0))
 73            .px(px(4.0))
 74            .bg(self.bg_color)
 75            .shadow(vec![gpui::BoxShadow {
 76                color: gpui::black().opacity(0.08),
 77                offset: point(px(0.), px(1.)),
 78                blur_radius: px(0.),
 79                spread_radius: px(0.),
 80            }])
 81            .child(
 82                div()
 83                    .h_full()
 84                    .rounded_full()
 85                    .when(self.value > self.max_value, |div| div.bg(self.over_color))
 86                    .when(self.value <= self.max_value, |div| div.bg(self.fg_color))
 87                    .w(relative(fill_width)),
 88            )
 89    }
 90}
 91
 92impl Component for ProgressBar {
 93    fn scope() -> ComponentScope {
 94        ComponentScope::Status
 95    }
 96
 97    fn description() -> Option<&'static str> {
 98        Some(Self::DOCS)
 99    }
100
101    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
102        let max_value = 180.0;
103
104        Some(
105            div()
106                .flex()
107                .flex_col()
108                .gap_4()
109                .p_4()
110                .w(px(240.0))
111                .child(div().child("Progress Bar"))
112                .child(
113                    div()
114                        .flex()
115                        .flex_col()
116                        .gap_2()
117                        .child(
118                            div()
119                                .flex()
120                                .justify_between()
121                                .child(Label::new("0%"))
122                                .child(Label::new("Empty")),
123                        )
124                        .child(ProgressBar::new("empty", 0.0, max_value, cx)),
125                )
126                .child(
127                    div()
128                        .flex()
129                        .flex_col()
130                        .gap_2()
131                        .child(
132                            div()
133                                .flex()
134                                .justify_between()
135                                .child(Label::new("38%"))
136                                .child(Label::new("Partial")),
137                        )
138                        .child(ProgressBar::new("partial", max_value * 0.35, max_value, cx)),
139                )
140                .child(
141                    div()
142                        .flex()
143                        .flex_col()
144                        .gap_2()
145                        .child(
146                            div()
147                                .flex()
148                                .justify_between()
149                                .child(Label::new("100%"))
150                                .child(Label::new("Complete")),
151                        )
152                        .child(ProgressBar::new("filled", max_value, max_value, cx)),
153                )
154                .into_any_element(),
155        )
156    }
157}