Progress
A linear progress bar component that visually represents the completion percentage of a task. The progress bar features smooth animations, customizable colors, and automatic styling that adapts to the current theme.
Import
rust
use gpui_component::progress::Progress;
Usage
Basic Progress Bar
rust
Progress::new()
.value(50.0) // 50% complete
Different Progress Values
rust
// Starting state (0%)
Progress::new()
.value(0.0)
// Partially complete (25%)
Progress::new()
.value(25.0)
// Nearly complete (75%)
Progress::new()
.value(75.0)
// Complete (100%)
Progress::new()
.value(100.0)
Indeterminate State
rust
// For unknown progress duration
Progress::new()
.value(-1.0) // Any negative value shows as 0%
// Or explicitly set to 0 for starting state
Progress::new()
.value(0.0)
Dynamic Progress Updates
rust
struct MyComponent {
progress_value: f32,
}
impl MyComponent {
fn update_progress(&mut self, new_value: f32) {
self.progress_value = new_value.clamp(0.0, 100.0);
}
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_3()
.child(
h_flex()
.gap_2()
.child(Button::new("decrease").label("-").on_click(
cx.listener(|this, _, _, _| {
this.update_progress(this.progress_value - 10.0);
})
))
.child(Button::new("increase").label("+").on_click(
cx.listener(|this, _, _, _| {
this.update_progress(this.progress_value + 10.0);
})
))
)
.child(Progress::new().value(self.progress_value))
.child(format!("{}%", self.progress_value as i32))
}
}
File Upload Progress
rust
struct FileUpload {
bytes_uploaded: u64,
total_bytes: u64,
}
impl FileUpload {
fn progress_percentage(&self) -> f32 {
if self.total_bytes == 0 {
0.0
} else {
(self.bytes_uploaded as f32 / self.total_bytes as f32) * 100.0
}
}
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_2()
.child("Uploading file...")
.child(Progress::new().value(self.progress_percentage()))
.child(format!(
"{} / {} bytes",
self.bytes_uploaded,
self.total_bytes
))
}
}
Loading States
rust
struct LoadingComponent {
is_loading: bool,
progress: f32,
}
impl LoadingComponent {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_3()
.when(self.is_loading, |this| {
this.child("Loading...")
.child(Progress::new().value(self.progress))
})
.when(!self.is_loading, |this| {
this.child("Task completed!")
.child(Progress::new().value(100.0))
})
}
}
Multi-Step Process
rust
enum ProcessStep {
Initializing,
Processing,
Finalizing,
Complete,
}
struct MultiStepProcess {
current_step: ProcessStep,
step_progress: f32,
}
impl MultiStepProcess {
fn overall_progress(&self) -> f32 {
let base_progress = match self.current_step {
ProcessStep::Initializing => 0.0,
ProcessStep::Processing => 33.33,
ProcessStep::Finalizing => 66.66,
ProcessStep::Complete => 100.0,
};
if matches!(self.current_step, ProcessStep::Complete) {
100.0
} else {
base_progress + (self.step_progress / 3.0)
}
}
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_3()
.child(match self.current_step {
ProcessStep::Initializing => "Initializing...",
ProcessStep::Processing => "Processing data...",
ProcessStep::Finalizing => "Finalizing...",
ProcessStep::Complete => "Complete!",
})
.child(Progress::new().value(self.overall_progress()))
.child(format!("{:.1}% complete", self.overall_progress()))
}
}
API Reference
Progress
Method | Description |
---|---|
new() | Create a new progress bar with default settings |
value(f32) | Set the progress value (0.0 to 100.0) |
Progress Properties
Property | Type | Default | Description |
---|---|---|---|
value | f32 | 0.0 | Progress percentage (0-100) |
height | f32 | 8.0 | Height of the progress bar in pixels |
Examples
Task Progress with Status
rust
struct TaskProgress {
completed_tasks: usize,
total_tasks: usize,
}
impl TaskProgress {
fn progress_value(&self) -> f32 {
if self.total_tasks == 0 {
0.0
} else {
(self.completed_tasks as f32 / self.total_tasks as f32) * 100.0
}
}
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_2()
.child(
h_flex()
.justify_between()
.child("Task Progress")
.child(format!("{}/{}", self.completed_tasks, self.total_tasks))
)
.child(Progress::new().value(self.progress_value()))
.when(self.completed_tasks == self.total_tasks, |this| {
this.child("All tasks completed!")
})
}
}
Download Progress with Speed
rust
struct DownloadProgress {
downloaded: u64,
total_size: u64,
speed_mbps: f32,
}
impl DownloadProgress {
fn eta_seconds(&self) -> u64 {
if self.speed_mbps == 0.0 {
0
} else {
let remaining_mb = (self.total_size - self.downloaded) as f32 / 1_000_000.0;
(remaining_mb / self.speed_mbps) as u64
}
}
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let progress = (self.downloaded as f32 / self.total_size as f32) * 100.0;
v_flex()
.gap_2()
.child(
h_flex()
.justify_between()
.child("Downloading...")
.child(format!("{:.1}%", progress))
)
.child(Progress::new().value(progress))
.child(
h_flex()
.justify_between()
.child(format!("{:.1} MB/s", self.speed_mbps))
.child(format!("ETA: {}s", self.eta_seconds()))
)
}
}
Installation Progress
rust
struct InstallationProgress {
current_package: String,
package_index: usize,
total_packages: usize,
package_progress: f32,
}
impl InstallationProgress {
fn overall_progress(&self) -> f32 {
if self.total_packages == 0 {
0.0
} else {
let completed_packages = self.package_index as f32;
let current_package_contribution = self.package_progress / 100.0;
let total_progress = (completed_packages + current_package_contribution)
/ self.total_packages as f32;
total_progress * 100.0
}
}
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_3()
.child("Installing packages...")
.child(
v_flex()
.gap_2()
.child(
h_flex()
.justify_between()
.child(format!("Overall Progress"))
.child(format!("{}/{}", self.package_index + 1, self.total_packages))
)
.child(Progress::new().value(self.overall_progress()))
)
.child(
v_flex()
.gap_2()
.child(format!("Installing: {}", self.current_package))
.child(Progress::new().value(self.package_progress))
)
}
}
Styling and Theming
The Progress component automatically adapts to the current theme:
Theme Colors
rust
// The progress bar uses theme colors automatically
// Background: theme.progress_bar with 20% opacity
// Fill: theme.progress_bar at full opacity
// These colors adapt to light/dark theme automatically
Progress::new().value(75.0) // Uses theme colors
Visual Properties
- Height: 8px by default
- Border Radius: Matches theme radius (up to half the height)
- Background: Semi-transparent theme progress bar color (20% opacity)
- Fill: Full opacity theme progress bar color
- Animation: Smooth transitions when value changes
- Corners: Rounded on completion, left-rounded during progress
Accessibility
- Progress value is semantically meaningful for screen readers
- Visual progress is complemented by text indicators
- High contrast between background and fill colors
- Smooth transitions provide visual feedback
- Progress state can be announced programmatically
Behavior Notes
- Values less than 0 are clamped to 0%
- Values greater than 100 are clamped to 100%
- Progress bar fills from left to right
- Border radius adjusts based on completion state:
- Partial progress: Left side rounded only
- Complete progress: Both sides rounded
- Background color is always a semi-transparent version of the fill color
- Height and radius adapt to theme settings automatically
Best Practices
- Always provide text indicators alongside the visual progress bar
- Use meaningful labels to describe what is progressing
- Update progress regularly but not too frequently to avoid performance issues
- Consider showing ETA or completion time for long-running tasks
- Provide cancel/pause options for lengthy operations
- Show final status when progress reaches 100%
- Handle error states gracefully with appropriate messaging