Progress bar (#519)
* add progress bar * update changelog * apply suggestions * disable animation by default and tweak colors * allow toggling the animation by clicking * Update egui/src/widgets/progress_bar.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * Update egui/src/widgets/progress_bar.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * Update egui/src/widgets/progress_bar.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * address review comments Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
52e3663958
commit
89cea7aca7
4 changed files with 158 additions and 0 deletions
|
@ -6,6 +6,7 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [
|
||||||
|
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
* [Progress bar](https://github.com/emilk/egui/pull/519)
|
||||||
|
|
||||||
|
|
||||||
## 0.13.1 - 2021-06-28 - Plot fixes
|
## 0.13.1 - 2021-06-28 - Plot fixes
|
||||||
|
|
|
@ -13,6 +13,7 @@ mod hyperlink;
|
||||||
mod image;
|
mod image;
|
||||||
mod label;
|
mod label;
|
||||||
pub mod plot;
|
pub mod plot;
|
||||||
|
mod progress_bar;
|
||||||
mod selected_label;
|
mod selected_label;
|
||||||
mod separator;
|
mod separator;
|
||||||
mod slider;
|
mod slider;
|
||||||
|
@ -20,6 +21,7 @@ pub(crate) mod text_edit;
|
||||||
|
|
||||||
pub use hyperlink::*;
|
pub use hyperlink::*;
|
||||||
pub use label::*;
|
pub use label::*;
|
||||||
|
pub use progress_bar::ProgressBar;
|
||||||
pub use selected_label::*;
|
pub use selected_label::*;
|
||||||
pub use separator::*;
|
pub use separator::*;
|
||||||
pub use {button::*, drag_value::DragValue, image::Image, slider::*, text_edit::*};
|
pub use {button::*, drag_value::DragValue, image::Image, slider::*, text_edit::*};
|
||||||
|
|
141
egui/src/widgets/progress_bar.rs
Normal file
141
egui/src/widgets/progress_bar.rs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
enum ProgressBarText {
|
||||||
|
Custom(String),
|
||||||
|
Percentage,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple progress bar.
|
||||||
|
pub struct ProgressBar {
|
||||||
|
progress: f32,
|
||||||
|
desired_width: Option<f32>,
|
||||||
|
text: Option<ProgressBarText>,
|
||||||
|
animate: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgressBar {
|
||||||
|
/// Progress in the `[0, 1]` range, where `1` means "completed".
|
||||||
|
pub fn new(progress: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
progress: progress.clamp(0.0, 1.0),
|
||||||
|
desired_width: None,
|
||||||
|
text: None,
|
||||||
|
animate: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The desired width of the bar. Will use all horizonal space if not set.
|
||||||
|
pub fn desired_width(mut self, desired_width: f32) -> Self {
|
||||||
|
self.desired_width = Some(desired_width);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A custom text to display on the progress bar.
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
pub fn text(mut self, text: impl ToString) -> Self {
|
||||||
|
self.text = Some(ProgressBarText::Custom(text.to_string()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show the progress in percent on the progress bar.
|
||||||
|
pub fn show_percentage(mut self) -> Self {
|
||||||
|
self.text = Some(ProgressBarText::Percentage);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether to display a loading animation when progress `< 1`.
|
||||||
|
/// Note that this require the UI to be redrawn.
|
||||||
|
/// Defaults to `false`.
|
||||||
|
pub fn animate(mut self, animate: bool) -> Self {
|
||||||
|
self.animate = animate;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for ProgressBar {
|
||||||
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
|
let ProgressBar {
|
||||||
|
progress,
|
||||||
|
desired_width,
|
||||||
|
text,
|
||||||
|
mut animate,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
animate &= progress < 1.0;
|
||||||
|
|
||||||
|
let desired_width = desired_width.unwrap_or(ui.available_size_before_wrap().x);
|
||||||
|
let height = ui.spacing().interact_size.y;
|
||||||
|
let (outer_rect, response) =
|
||||||
|
ui.allocate_exact_size(vec2(desired_width, height), Sense::hover());
|
||||||
|
let visuals = ui.style().visuals.clone();
|
||||||
|
let corner_radius = outer_rect.height() / 2.0;
|
||||||
|
ui.painter().rect(
|
||||||
|
outer_rect,
|
||||||
|
corner_radius,
|
||||||
|
visuals.extreme_bg_color,
|
||||||
|
Stroke::none(),
|
||||||
|
);
|
||||||
|
let inner_rect = Rect::from_min_size(
|
||||||
|
outer_rect.min,
|
||||||
|
vec2(
|
||||||
|
(outer_rect.width() * progress).at_least(outer_rect.height()),
|
||||||
|
outer_rect.height(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (dark, bright) = (0.7, 1.0);
|
||||||
|
let color_factor = if animate {
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
lerp(dark..=bright, ui.input().time.cos().abs())
|
||||||
|
} else {
|
||||||
|
bright
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.painter().rect(
|
||||||
|
inner_rect,
|
||||||
|
corner_radius,
|
||||||
|
Color32::from(Rgba::from(visuals.selection.bg_fill) * color_factor as f32),
|
||||||
|
Stroke::none(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if animate {
|
||||||
|
let n_points = 20;
|
||||||
|
let start_angle = ui.input().time as f64 * 360f64.to_radians();
|
||||||
|
let end_angle = start_angle + 240f64.to_radians() * ui.input().time.sin();
|
||||||
|
let circle_radius = corner_radius - 2.0;
|
||||||
|
let points: Vec<Pos2> = (0..n_points)
|
||||||
|
.map(|i| {
|
||||||
|
let angle = lerp(start_angle..=end_angle, i as f64 / n_points as f64);
|
||||||
|
let (sin, cos) = angle.sin_cos();
|
||||||
|
inner_rect.right_center()
|
||||||
|
+ circle_radius * vec2(cos as f32, sin as f32)
|
||||||
|
+ vec2(-corner_radius, 0.0)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
ui.painter().add(Shape::Path {
|
||||||
|
points,
|
||||||
|
closed: false,
|
||||||
|
fill: Color32::TRANSPARENT,
|
||||||
|
stroke: Stroke::new(2.0, visuals.faint_bg_color),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(text_kind) = text {
|
||||||
|
let text = match text_kind {
|
||||||
|
ProgressBarText::Custom(string) => string,
|
||||||
|
ProgressBarText::Percentage => format!("{}%", (progress * 100.0) as usize),
|
||||||
|
};
|
||||||
|
ui.painter().sub_region(outer_rect).text(
|
||||||
|
outer_rect.left_center() + vec2(ui.spacing().item_spacing.x, 0.0),
|
||||||
|
Align2::LEFT_CENTER,
|
||||||
|
text,
|
||||||
|
TextStyle::Button,
|
||||||
|
visuals
|
||||||
|
.override_text_color
|
||||||
|
.unwrap_or(visuals.selection.stroke.color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ pub struct WidgetGallery {
|
||||||
scalar: f32,
|
scalar: f32,
|
||||||
string: String,
|
string: String,
|
||||||
color: egui::Color32,
|
color: egui::Color32,
|
||||||
|
animate_progress_bar: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WidgetGallery {
|
impl Default for WidgetGallery {
|
||||||
|
@ -28,6 +29,7 @@ impl Default for WidgetGallery {
|
||||||
scalar: 42.0,
|
scalar: 42.0,
|
||||||
string: Default::default(),
|
string: Default::default(),
|
||||||
color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),
|
color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),
|
||||||
|
animate_progress_bar: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +97,7 @@ impl WidgetGallery {
|
||||||
scalar,
|
scalar,
|
||||||
string,
|
string,
|
||||||
color,
|
color,
|
||||||
|
animate_progress_bar,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.add(doc_link_label("Label", "label,heading"));
|
ui.add(doc_link_label("Label", "label,heading"));
|
||||||
|
@ -157,6 +160,17 @@ impl WidgetGallery {
|
||||||
ui.add(egui::Slider::new(scalar, 0.0..=360.0).suffix("°"));
|
ui.add(egui::Slider::new(scalar, 0.0..=360.0).suffix("°"));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.add(doc_link_label("ProgressBar", "ProgressBar"));
|
||||||
|
let progress = *scalar / 360.0;
|
||||||
|
let progress_bar = egui::ProgressBar::new(progress)
|
||||||
|
.show_percentage()
|
||||||
|
.animate(*animate_progress_bar);
|
||||||
|
*animate_progress_bar = ui
|
||||||
|
.add(progress_bar)
|
||||||
|
.on_hover_text("The progress bar can be animated!")
|
||||||
|
.hovered();
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
ui.add(doc_link_label("DragValue", "DragValue"));
|
ui.add(doc_link_label("DragValue", "DragValue"));
|
||||||
ui.add(egui::DragValue::new(scalar).speed(1.0));
|
ui.add(egui::DragValue::new(scalar).speed(1.0));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
Loading…
Reference in a new issue