gpui_component/
progress.rs

1use crate::{ActiveTheme, StyledExt};
2use gpui::{
3    div, prelude::FluentBuilder, px, relative, App, Hsla, IntoElement, ParentElement, RenderOnce,
4    StyleRefinement, Styled, Window,
5};
6
7/// A Progress bar element.
8#[derive(IntoElement)]
9pub struct Progress {
10    style: StyleRefinement,
11    color: Option<Hsla>,
12    value: f32,
13}
14
15impl Progress {
16    /// Create a new Progress bar.
17    pub fn new() -> Self {
18        Progress {
19            value: Default::default(),
20            color: None,
21            style: StyleRefinement::default().h(px(8.)).rounded(px(4.)),
22        }
23    }
24
25    /// Set the color of the progress bar.
26    pub fn bg(mut self, color: impl Into<Hsla>) -> Self {
27        self.color = Some(color.into());
28        self
29    }
30
31    /// Set the percentage value of the progress bar.
32    ///
33    /// The value should be between 0.0 and 100.0.
34    pub fn value(mut self, value: f32) -> Self {
35        self.value = value.clamp(0., 100.);
36        self
37    }
38}
39
40impl Styled for Progress {
41    fn style(&mut self) -> &mut StyleRefinement {
42        &mut self.style
43    }
44}
45
46impl RenderOnce for Progress {
47    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
48        let radius = self.style.corner_radii.clone();
49        let mut inner_style = StyleRefinement::default();
50        inner_style.corner_radii = radius;
51
52        let color = self.color.unwrap_or(cx.theme().progress_bar);
53
54        let relative_w = relative(match self.value {
55            v if v < 0. => 0.,
56            v if v > 100. => 1.,
57            v => v / 100.,
58        });
59
60        div()
61            .w_full()
62            .relative()
63            .rounded_full()
64            .refine_style(&self.style)
65            .bg(color.opacity(0.2))
66            .child(
67                div()
68                    .absolute()
69                    .top_0()
70                    .left_0()
71                    .h_full()
72                    .w(relative_w)
73                    .bg(color)
74                    .map(|this| match self.value {
75                        v if v >= 100. => this.refine_style(&inner_style),
76                        _ => this.refine_style(&inner_style).rounded_r_none(),
77                    }),
78            )
79    }
80}