Add progress bar component (#28518)

- Adds the progress bar component

Release Notes:

- N/A
This commit is contained in:
Nate Butler 2025-04-10 12:11:58 -06:00 committed by GitHub
parent b0b52f299c
commit 3abf95216c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 377 additions and 18 deletions

View file

@ -22,6 +22,7 @@ mod notification;
mod numeric_stepper;
mod popover;
mod popover_menu;
mod progress;
mod radio;
mod right_click_menu;
mod scrollbar;
@ -61,6 +62,7 @@ pub use notification::*;
pub use numeric_stepper::*;
pub use popover::*;
pub use popover_menu::*;
pub use progress::*;
pub use radio::*;
pub use right_click_menu::*;
pub use scrollbar::*;

View file

@ -267,7 +267,7 @@ impl RenderOnce for IconWithIndicator {
impl Component for Icon {
fn scope() -> ComponentScope {
ComponentScope::None
ComponentScope::Images
}
fn description() -> Option<&'static str> {

View file

@ -26,7 +26,7 @@ impl RenderOnce for DecoratedIcon {
impl Component for DecoratedIcon {
fn scope() -> ComponentScope {
ComponentScope::None
ComponentScope::Images
}
fn description() -> Option<&'static str> {

View file

@ -199,7 +199,7 @@ impl RenderOnce for Label {
impl Component for Label {
fn scope() -> ComponentScope {
ComponentScope::None
ComponentScope::Typography
}
fn description() -> Option<&'static str> {

View file

@ -0,0 +1,2 @@
mod progress_bar;
pub use progress_bar::*;

View file

@ -0,0 +1,159 @@
use documented::Documented;
use gpui::{Hsla, point};
use crate::components::Label;
use crate::prelude::*;
/// A progress bar is a horizontal bar that communicates the status of a process.
///
/// A progress bar should not be used to represent indeterminate progress.
#[derive(RegisterComponent, Documented)]
pub struct ProgressBar {
id: ElementId,
value: f32,
max_value: f32,
bg_color: Hsla,
fg_color: Hsla,
}
impl ProgressBar {
/// Create a new progress bar with the given value and maximum value.
pub fn new(
id: impl Into<ElementId>,
value: f32,
max_value: f32,
cx: &mut Context<Self>,
) -> Self {
Self {
id: id.into(),
value,
max_value,
bg_color: cx.theme().colors().background,
fg_color: cx.theme().status().info,
}
}
/// Set the current value of the progress bar.
pub fn value(&mut self, value: f32) -> &mut Self {
self.value = value;
self
}
/// Set the maximum value of the progress bar.
pub fn max_value(&mut self, max_value: f32) -> &mut Self {
self.max_value = max_value;
self
}
/// Set the background color of the progress bar.
pub fn bg_color(&mut self, color: Hsla) -> &mut Self {
self.bg_color = color;
self
}
/// Set the foreground color of the progress bar.
pub fn fg_color(&mut self, color: Hsla) -> &mut Self {
self.fg_color = color;
self
}
}
impl Render for ProgressBar {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let fill_width = (self.value / self.max_value).clamp(0.02, 1.0);
div()
.id(self.id.clone())
.w_full()
.h(px(8.0))
.rounded_full()
.py(px(2.0))
.px(px(4.0))
.bg(self.bg_color)
.shadow(smallvec::smallvec![gpui::BoxShadow {
color: gpui::black().opacity(0.08),
offset: point(px(0.), px(1.)),
blur_radius: px(0.),
spread_radius: px(0.),
}])
.child(
div()
.h_full()
.rounded_full()
.bg(self.fg_color)
.w(relative(fill_width)),
)
}
}
impl Component for ProgressBar {
fn scope() -> ComponentScope {
ComponentScope::Status
}
fn description() -> Option<&'static str> {
Some(Self::DOCS)
}
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let max_value = 180.0;
let empty_progress_bar = cx.new(|cx| ProgressBar::new("empty", 0.0, max_value, cx));
let partial_progress_bar =
cx.new(|cx| ProgressBar::new("partial", max_value * 0.35, max_value, cx));
let filled_progress_bar = cx.new(|cx| ProgressBar::new("filled", max_value, max_value, cx));
Some(
div()
.flex()
.flex_col()
.gap_4()
.p_4()
.w(px(240.0))
.child(div().child("Progress Bar"))
.child(
div()
.flex()
.flex_col()
.gap_2()
.child(
div()
.flex()
.justify_between()
.child(Label::new("0%"))
.child(Label::new("Empty")),
)
.child(empty_progress_bar.clone()),
)
.child(
div()
.flex()
.flex_col()
.gap_2()
.child(
div()
.flex()
.justify_between()
.child(Label::new("38%"))
.child(Label::new("Partial")),
)
.child(partial_progress_bar.clone()),
)
.child(
div()
.flex()
.flex_col()
.gap_2()
.child(
div()
.flex()
.justify_between()
.child(Label::new("100%"))
.child(Label::new("Complete")),
)
.child(filled_progress_bar.clone()),
)
.into_any_element(),
)
}
}

View file

@ -235,7 +235,7 @@ impl Headline {
impl Component for Headline {
fn scope() -> ComponentScope {
ComponentScope::None
ComponentScope::Typography
}
fn description() -> Option<&'static str> {