Skip to content

Commit

Permalink
Add option to customize progress bar rounding (#2881)
Browse files Browse the repository at this point in the history
The minimum filled width is adapted to the radius and the animation is
disabled so it renders correctly.

---------

Co-authored-by: Emil Ernerfeldt <[email protected]>
  • Loading branch information
YgorSouza and emilk authored Jan 7, 2024
1 parent 3b19303 commit 4a6999b
Showing 1 changed file with 28 additions and 11 deletions.
39 changes: 28 additions & 11 deletions crates/egui/src/widgets/progress_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct ProgressBar {
text: Option<ProgressBarText>,
fill: Option<Color32>,
animate: bool,
rounding: Option<Rounding>,
}

impl ProgressBar {
Expand All @@ -28,6 +29,7 @@ impl ProgressBar {
text: None,
fill: None,
animate: false,
rounding: None,
}
}

Expand Down Expand Up @@ -69,11 +71,26 @@ impl ProgressBar {
/// Whether to display a loading animation when progress `< 1`.
/// Note that this will cause the UI to be redrawn.
/// Defaults to `false`.
///
/// If [`Self::rounding`] and [`Self::animate`] are used simultaneously, the animation is not
/// rendered, since it requires a perfect circle to render correctly. However, the UI is still
/// redrawn.
#[inline]
pub fn animate(mut self, animate: bool) -> Self {
self.animate = animate;
self
}

/// Set the rounding of the progress bar.
///
/// If [`Self::rounding`] and [`Self::animate`] are used simultaneously, the animation is not
/// rendered, since it requires a perfect circle to render correctly. However, the UI is still
/// redrawn.
#[inline]
pub fn rounding(mut self, rounding: impl Into<Rounding>) -> Self {
self.rounding = Some(rounding.into());
self
}
}

impl Widget for ProgressBar {
Expand All @@ -85,6 +102,7 @@ impl Widget for ProgressBar {
text,
fill,
animate,
rounding,
} = self;

let animate = animate && progress < 1.0;
Expand All @@ -101,16 +119,15 @@ impl Widget for ProgressBar {
}

let visuals = ui.style().visuals.clone();
let rounding = outer_rect.height() / 2.0;
let is_custom_rounding = rounding.is_some();
let corner_radius = outer_rect.height() / 2.0;
let rounding = rounding.unwrap_or_else(|| corner_radius.into());
ui.painter()
.rect(outer_rect, rounding, 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 min_width = 2.0 * rounding.sw.at_least(rounding.nw).at_most(corner_radius);
let filled_width = (outer_rect.width() * progress).at_least(min_width);
let inner_rect =
Rect::from_min_size(outer_rect.min, vec2(filled_width, outer_rect.height()));

let (dark, bright) = (0.7, 1.0);
let color_factor = if animate {
Expand All @@ -129,19 +146,19 @@ impl Widget for ProgressBar {
Stroke::NONE,
);

if animate {
if animate && !is_custom_rounding {
let n_points = 20;
let time = ui.input(|i| i.time);
let start_angle = time * std::f64::consts::TAU;
let end_angle = start_angle + 240f64.to_radians() * time.sin();
let circle_radius = rounding - 2.0;
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(-rounding, 0.0)
+ vec2(-corner_radius, 0.0)
})
.collect();
ui.painter()
Expand Down

0 comments on commit 4a6999b

Please sign in to comment.