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            .p(px(2.0))
 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
103        Some(
104            div()
105                .flex()
106                .flex_col()
107                .gap_4()
108                .p_4()
109                .w(px(240.0))
110                .child(div().child("Progress Bar"))
111                .child(
112                    div()
113                        .flex()
114                        .flex_col()
115                        .gap_2()
116                        .child(
117                            div()
118                                .flex()
119                                .justify_between()
120                                .child(Label::new("0%"))
121                                .child(Label::new("Empty")),
122                        )
123                        .child(ProgressBar::new("empty", 0.0, max_value, cx)),
124                )
125                .child(
126                    div()
127                        .flex()
128                        .flex_col()
129                        .gap_2()
130                        .child(
131                            div()
132                                .flex()
133                                .justify_between()
134                                .child(Label::new("38%"))
135                                .child(Label::new("Partial")),
136                        )
137                        .child(ProgressBar::new("partial", max_value * 0.35, max_value, cx)),
138                )
139                .child(
140                    div()
141                        .flex()
142                        .flex_col()
143                        .gap_2()
144                        .child(
145                            div()
146                                .flex()
147                                .justify_between()
148                                .child(Label::new("100%"))
149                                .child(Label::new("Complete")),
150                        )
151                        .child(ProgressBar::new("filled", max_value, max_value, cx)),
152                )
153                .into_any_element(),
154        )
155    }
156}