From 57f9fc4ed2cc4fa34d3511bfa35ab186592beb1e Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 26 Jun 2021 23:24:59 +0200 Subject: [PATCH 1/9] add progress bar --- egui_demo_lib/src/apps/demo/mod.rs | 1 + egui_demo_lib/src/apps/demo/progress_bar.rs | 132 ++++++++++++++++++ egui_demo_lib/src/apps/demo/widget_gallery.rs | 11 ++ 3 files changed, 144 insertions(+) create mode 100644 egui_demo_lib/src/apps/demo/progress_bar.rs diff --git a/egui_demo_lib/src/apps/demo/mod.rs b/egui_demo_lib/src/apps/demo/mod.rs index 9a89160de42..d636cebcf60 100644 --- a/egui_demo_lib/src/apps/demo/mod.rs +++ b/egui_demo_lib/src/apps/demo/mod.rs @@ -15,6 +15,7 @@ pub mod multi_touch; pub mod painting; pub mod password; pub mod plot_demo; +pub mod progress_bar; pub mod scrolling; pub mod sliders; pub mod tests; diff --git a/egui_demo_lib/src/apps/demo/progress_bar.rs b/egui_demo_lib/src/apps/demo/progress_bar.rs new file mode 100644 index 00000000000..f0cb976d7bb --- /dev/null +++ b/egui_demo_lib/src/apps/demo/progress_bar.rs @@ -0,0 +1,132 @@ +use egui::*; + +/// A simple progress bar. +pub struct ProgressBar { + progress: f32, + desired_width: Option, + text: String, + animate: bool, +} + +impl ProgressBar { + pub fn new(progress: f32) -> Self { + Self { + progress: progress.clamp(0.0, 1.0), + desired_width: None, + text: "".to_string(), + animate: true, + } + } + + /// The desired width of the bar. Will use all horizonal space if not set. + #[allow(dead_code)] + 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 = text.to_string(); + self + } + + /// Whether to animate the bar. Note that this require the UI to be redrawn. + /// Defaults to `false`. + #[allow(dead_code)] + 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) = if visuals.dark_mode { + (0.2, 0.3) + } else { + (0.8, 0.9) + }; + let color_factor = if animate { + ui.ctx().request_repaint(); + lerp(dark..=bright, ui.input().time.cos().abs()) + } else { + (bright + dark) / 2.0 + }; + + ui.painter().rect( + inner_rect, + corner_radius, + Color32::from_gray((color_factor * 255.0) as u8), + 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 = (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 !text.is_empty() { + ui.painter().text( + outer_rect.left_center() + vec2(ui.spacing().item_spacing.x, 0.0), + Align2::LEFT_CENTER, + text, + TextStyle::Button, + visuals.text_color(), + ); + } + + response + } +} + +pub fn url_to_file_source_code() -> String { + format!("https://github.com/emilk/egui/blob/master/{}", file!()) +} diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index e19b6bc4fad..0a3a7343ea0 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -157,6 +157,17 @@ impl WidgetGallery { ui.add(egui::Slider::new(scalar, 0.0..=360.0).suffix("°")); ui.end_row(); + ui.hyperlink_to( + "Progress bar:", + super::progress_bar::url_to_file_source_code(), + ); + let progress = *scalar / 360.0; + ui.add( + super::progress_bar::ProgressBar::new(progress) + .text(format!("{}%", (progress * 100.0) as usize)), + ); + ui.end_row(); + ui.add(doc_link_label("DragValue", "DragValue")); ui.add(egui::DragValue::new(scalar).speed(1.0)); ui.end_row(); From f3188562b7f4cf891a6c091b715efbea17dff6b6 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sat, 26 Jun 2021 23:27:14 +0200 Subject: [PATCH 2/9] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42deab2a655..4297dd807c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [ ## Unreleased +* [Progress bar](https://github.com/emilk/egui/pull/519) ## 0.13.0 - 2021-06-24 - Better panels, plots and new visual style From 5dba3c1687e395caa6dd53051958f2ad3ac1c4c8 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 27 Jun 2021 05:13:52 +0200 Subject: [PATCH 3/9] apply suggestions --- egui/src/widgets/mod.rs | 2 ++ .../demo => egui/src/widgets}/progress_bar.rs | 20 ++++++------------- egui_demo_lib/src/apps/demo/mod.rs | 1 - egui_demo_lib/src/apps/demo/widget_gallery.rs | 10 ++-------- 4 files changed, 10 insertions(+), 23 deletions(-) rename {egui_demo_lib/src/apps/demo => egui/src/widgets}/progress_bar.rs (89%) diff --git a/egui/src/widgets/mod.rs b/egui/src/widgets/mod.rs index 2330019a348..532920db148 100644 --- a/egui/src/widgets/mod.rs +++ b/egui/src/widgets/mod.rs @@ -13,6 +13,7 @@ mod hyperlink; mod image; mod label; pub mod plot; +mod progress_bar; mod selected_label; mod separator; mod slider; @@ -20,6 +21,7 @@ pub(crate) mod text_edit; pub use hyperlink::*; pub use label::*; +pub use progress_bar::ProgressBar; pub use selected_label::*; pub use separator::*; pub use {button::*, drag_value::DragValue, image::Image, slider::*, text_edit::*}; diff --git a/egui_demo_lib/src/apps/demo/progress_bar.rs b/egui/src/widgets/progress_bar.rs similarity index 89% rename from egui_demo_lib/src/apps/demo/progress_bar.rs rename to egui/src/widgets/progress_bar.rs index f0cb976d7bb..dbe8c9b4051 100644 --- a/egui_demo_lib/src/apps/demo/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -1,4 +1,4 @@ -use egui::*; +use crate::*; /// A simple progress bar. pub struct ProgressBar { @@ -19,7 +19,6 @@ impl ProgressBar { } /// The desired width of the bar. Will use all horizonal space if not set. - #[allow(dead_code)] pub fn desired_width(mut self, desired_width: f32) -> Self { self.desired_width = Some(desired_width); self @@ -34,7 +33,6 @@ impl ProgressBar { /// Whether to animate the bar. Note that this require the UI to be redrawn. /// Defaults to `false`. - #[allow(dead_code)] pub fn animate(mut self, animate: bool) -> Self { self.animate = animate; self @@ -72,11 +70,7 @@ impl Widget for ProgressBar { ), ); - let (dark, bright) = if visuals.dark_mode { - (0.2, 0.3) - } else { - (0.8, 0.9) - }; + let (dark, bright) = (0.8, 1.0); let color_factor = if animate { ui.ctx().request_repaint(); lerp(dark..=bright, ui.input().time.cos().abs()) @@ -87,7 +81,7 @@ impl Widget for ProgressBar { ui.painter().rect( inner_rect, corner_radius, - Color32::from_gray((color_factor * 255.0) as u8), + Color32::from(Rgba::from(visuals.selection.bg_fill) * color_factor as f32), Stroke::none(), ); @@ -119,14 +113,12 @@ impl Widget for ProgressBar { Align2::LEFT_CENTER, text, TextStyle::Button, - visuals.text_color(), + visuals + .override_text_color + .unwrap_or(visuals.selection.stroke.color), ); } response } } - -pub fn url_to_file_source_code() -> String { - format!("https://github.com/emilk/egui/blob/master/{}", file!()) -} diff --git a/egui_demo_lib/src/apps/demo/mod.rs b/egui_demo_lib/src/apps/demo/mod.rs index d636cebcf60..9a89160de42 100644 --- a/egui_demo_lib/src/apps/demo/mod.rs +++ b/egui_demo_lib/src/apps/demo/mod.rs @@ -15,7 +15,6 @@ pub mod multi_touch; pub mod painting; pub mod password; pub mod plot_demo; -pub mod progress_bar; pub mod scrolling; pub mod sliders; pub mod tests; diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index 0a3a7343ea0..fddc80c8562 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -157,15 +157,9 @@ impl WidgetGallery { ui.add(egui::Slider::new(scalar, 0.0..=360.0).suffix("°")); ui.end_row(); - ui.hyperlink_to( - "Progress bar:", - super::progress_bar::url_to_file_source_code(), - ); + ui.add(doc_link_label("ProgressBar", "ProgressBar")); let progress = *scalar / 360.0; - ui.add( - super::progress_bar::ProgressBar::new(progress) - .text(format!("{}%", (progress * 100.0) as usize)), - ); + ui.add(egui::ProgressBar::new(progress).text(format!("{}%", (progress * 100.0) as usize))); ui.end_row(); ui.add(doc_link_label("DragValue", "DragValue")); From c14c8d91a6b19d0a112bcf21832f8bba453b64f5 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 27 Jun 2021 05:18:26 +0200 Subject: [PATCH 4/9] disable animation by default and tweak colors --- egui/src/widgets/progress_bar.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/egui/src/widgets/progress_bar.rs b/egui/src/widgets/progress_bar.rs index dbe8c9b4051..b460579b1f2 100644 --- a/egui/src/widgets/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -14,7 +14,7 @@ impl ProgressBar { progress: progress.clamp(0.0, 1.0), desired_width: None, text: "".to_string(), - animate: true, + animate: false, } } @@ -31,7 +31,7 @@ impl ProgressBar { self } - /// Whether to animate the bar. Note that this require the UI to be redrawn. + /// Whether to display a loading animation. Note that this require the UI to be redrawn. /// Defaults to `false`. pub fn animate(mut self, animate: bool) -> Self { self.animate = animate; @@ -70,7 +70,7 @@ impl Widget for ProgressBar { ), ); - let (dark, bright) = (0.8, 1.0); + let (dark, bright) = (0.7, 1.0); let color_factor = if animate { ui.ctx().request_repaint(); lerp(dark..=bright, ui.input().time.cos().abs()) From 49ba4001a3f20dcd441b3e5d46782c75cdb4c5b7 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Sun, 27 Jun 2021 05:26:05 +0200 Subject: [PATCH 5/9] allow toggling the animation by clicking --- egui/src/widgets/progress_bar.rs | 2 +- egui_demo_lib/src/apps/demo/widget_gallery.rs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/egui/src/widgets/progress_bar.rs b/egui/src/widgets/progress_bar.rs index b460579b1f2..478f7645358 100644 --- a/egui/src/widgets/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -53,7 +53,7 @@ impl Widget for ProgressBar { 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()); + ui.allocate_exact_size(vec2(desired_width, height), Sense::click()); let visuals = ui.style().visuals.clone(); let corner_radius = outer_rect.height() / 2.0; ui.painter().rect( diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index fddc80c8562..acce453e185 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -16,6 +16,7 @@ pub struct WidgetGallery { scalar: f32, string: String, color: egui::Color32, + animate_progress_bar: bool, } impl Default for WidgetGallery { @@ -28,6 +29,7 @@ impl Default for WidgetGallery { scalar: 42.0, string: Default::default(), color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5), + animate_progress_bar: false, } } } @@ -95,6 +97,7 @@ impl WidgetGallery { scalar, string, color, + animate_progress_bar, } = self; ui.add(doc_link_label("Label", "label,heading")); @@ -159,7 +162,15 @@ impl WidgetGallery { ui.add(doc_link_label("ProgressBar", "ProgressBar")); let progress = *scalar / 360.0; - ui.add(egui::ProgressBar::new(progress).text(format!("{}%", (progress * 100.0) as usize))); + let progress_bar = egui::ProgressBar::new(progress) + .text(format!("{}%", (progress * 100.0) as usize)) + .animate(*animate_progress_bar); + let progress_bar_response = ui + .add(progress_bar) + .on_hover_text("Click to enable the animation!"); + if progress_bar_response.clicked() { + *animate_progress_bar = !*animate_progress_bar; + } ui.end_row(); ui.add(doc_link_label("DragValue", "DragValue")); From 24e6fbe4c874b2c070d06b0abcf74f8829e0b8aa Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 28 Jun 2021 17:16:03 +0200 Subject: [PATCH 6/9] Update egui/src/widgets/progress_bar.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/progress_bar.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/egui/src/widgets/progress_bar.rs b/egui/src/widgets/progress_bar.rs index 478f7645358..2efe5e935f6 100644 --- a/egui/src/widgets/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -9,6 +9,7 @@ pub struct ProgressBar { } 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), From dafe2ef5514a94b289352172b16fcf3bcc7ccb6b Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 28 Jun 2021 17:16:10 +0200 Subject: [PATCH 7/9] Update egui/src/widgets/progress_bar.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/progress_bar.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/egui/src/widgets/progress_bar.rs b/egui/src/widgets/progress_bar.rs index 2efe5e935f6..8502c029d85 100644 --- a/egui/src/widgets/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -32,7 +32,8 @@ impl ProgressBar { self } - /// Whether to display a loading animation. Note that this require the UI to be redrawn. + /// 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; From f426fa31000557ad4f63532a5346e182f935cc62 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 28 Jun 2021 17:16:16 +0200 Subject: [PATCH 8/9] Update egui/src/widgets/progress_bar.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/progress_bar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/progress_bar.rs b/egui/src/widgets/progress_bar.rs index 8502c029d85..a46ad5cc16b 100644 --- a/egui/src/widgets/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -55,7 +55,7 @@ impl Widget for ProgressBar { 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::click()); + 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( From 1d0d86f98c8bbac2a578cb7dbf254292b1e177f2 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Mon, 28 Jun 2021 17:33:30 +0200 Subject: [PATCH 9/9] address review comments --- egui/src/widgets/progress_bar.rs | 27 ++++++++++++++----- egui_demo_lib/src/apps/demo/widget_gallery.rs | 10 +++---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/egui/src/widgets/progress_bar.rs b/egui/src/widgets/progress_bar.rs index a46ad5cc16b..01e1cc953b7 100644 --- a/egui/src/widgets/progress_bar.rs +++ b/egui/src/widgets/progress_bar.rs @@ -1,10 +1,15 @@ use crate::*; +enum ProgressBarText { + Custom(String), + Percentage, +} + /// A simple progress bar. pub struct ProgressBar { progress: f32, desired_width: Option, - text: String, + text: Option, animate: bool, } @@ -14,7 +19,7 @@ impl ProgressBar { Self { progress: progress.clamp(0.0, 1.0), desired_width: None, - text: "".to_string(), + text: None, animate: false, } } @@ -28,7 +33,13 @@ impl ProgressBar { /// 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 = text.to_string(); + 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 } @@ -77,7 +88,7 @@ impl Widget for ProgressBar { ui.ctx().request_repaint(); lerp(dark..=bright, ui.input().time.cos().abs()) } else { - (bright + dark) / 2.0 + bright }; ui.painter().rect( @@ -109,8 +120,12 @@ impl Widget for ProgressBar { }); } - if !text.is_empty() { - ui.painter().text( + 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, diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index acce453e185..93139010d9e 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -163,14 +163,12 @@ impl WidgetGallery { ui.add(doc_link_label("ProgressBar", "ProgressBar")); let progress = *scalar / 360.0; let progress_bar = egui::ProgressBar::new(progress) - .text(format!("{}%", (progress * 100.0) as usize)) + .show_percentage() .animate(*animate_progress_bar); - let progress_bar_response = ui + *animate_progress_bar = ui .add(progress_bar) - .on_hover_text("Click to enable the animation!"); - if progress_bar_response.clicked() { - *animate_progress_bar = !*animate_progress_bar; - } + .on_hover_text("The progress bar can be animated!") + .hovered(); ui.end_row(); ui.add(doc_link_label("DragValue", "DragValue"));