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(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    /// Create a new progress bar with the given value and maximum value.
 21    pub fn new(
 22        id: impl Into<ElementId>,
 23        value: f32,
 24        max_value: f32,
 25        cx: &mut Context<Self>,
 26    ) -> Self {
 27        Self {
 28            id: id.into(),
 29            value,
 30            max_value,
 31            bg_color: cx.theme().colors().background,
 32            fg_color: cx.theme().status().info,
 33        }
 34    }
 35
 36    /// Set the current value of the progress bar.
 37    pub fn value(&mut self, value: f32) -> &mut Self {
 38        self.value = value;
 39        self
 40    }
 41
 42    /// Set the maximum value of the progress bar.
 43    pub fn max_value(&mut self, max_value: f32) -> &mut Self {
 44        self.max_value = max_value;
 45        self
 46    }
 47
 48    /// Set the background color of the progress bar.
 49    pub fn bg_color(&mut self, color: Hsla) -> &mut Self {
 50        self.bg_color = color;
 51        self
 52    }
 53
 54    /// Set the foreground color of the progress bar.
 55    pub fn fg_color(&mut self, color: Hsla) -> &mut Self {
 56        self.fg_color = color;
 57        self
 58    }
 59}
 60
 61impl Render for ProgressBar {
 62    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 63        let fill_width = (self.value / self.max_value).clamp(0.02, 1.0);
 64
 65        div()
 66            .id(self.id.clone())
 67            .w_full()
 68            .h(px(8.0))
 69            .rounded_full()
 70            .py(px(2.0))
 71            .px(px(4.0))
 72            .bg(self.bg_color)
 73            .shadow(smallvec::smallvec![gpui::BoxShadow {
 74                color: gpui::black().opacity(0.08),
 75                offset: point(px(0.), px(1.)),
 76                blur_radius: px(0.),
 77                spread_radius: px(0.),
 78            }])
 79            .child(
 80                div()
 81                    .h_full()
 82                    .rounded_full()
 83                    .bg(self.fg_color)
 84                    .w(relative(fill_width)),
 85            )
 86    }
 87}
 88
 89impl Component for ProgressBar {
 90    fn scope() -> ComponentScope {
 91        ComponentScope::Status
 92    }
 93
 94    fn description() -> Option<&'static str> {
 95        Some(Self::DOCS)
 96    }
 97
 98    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
 99        let max_value = 180.0;
100
101        let empty_progress_bar = cx.new(|cx| ProgressBar::new("empty", 0.0, max_value, cx));
102        let partial_progress_bar =
103            cx.new(|cx| ProgressBar::new("partial", max_value * 0.35, max_value, cx));
104        let filled_progress_bar = cx.new(|cx| ProgressBar::new("filled", max_value, max_value, cx));
105
106        Some(
107            div()
108                .flex()
109                .flex_col()
110                .gap_4()
111                .p_4()
112                .w(px(240.0))
113                .child(div().child("Progress Bar"))
114                .child(
115                    div()
116                        .flex()
117                        .flex_col()
118                        .gap_2()
119                        .child(
120                            div()
121                                .flex()
122                                .justify_between()
123                                .child(Label::new("0%"))
124                                .child(Label::new("Empty")),
125                        )
126                        .child(empty_progress_bar.clone()),
127                )
128                .child(
129                    div()
130                        .flex()
131                        .flex_col()
132                        .gap_2()
133                        .child(
134                            div()
135                                .flex()
136                                .justify_between()
137                                .child(Label::new("38%"))
138                                .child(Label::new("Partial")),
139                        )
140                        .child(partial_progress_bar.clone()),
141                )
142                .child(
143                    div()
144                        .flex()
145                        .flex_col()
146                        .gap_2()
147                        .child(
148                            div()
149                                .flex()
150                                .justify_between()
151                                .child(Label::new("100%"))
152                                .child(Label::new("Complete")),
153                        )
154                        .child(filled_progress_bar.clone()),
155                )
156                .into_any_element(),
157        )
158    }
159}