Add option to customize progress bar rounding (#2881)

The minimum filled width is adapted to the radius and the animation is
disabled so it renders correctly.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
YgorSouza 2024-01-07 16:43:13 +01:00 committed by GitHub
parent 3b19303e02
commit 4a6999bbad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 28 additions and 11 deletions

View File

@ -16,6 +16,7 @@ pub struct ProgressBar {
text: Option<ProgressBarText>,
fill: Option<Color32>,
animate: bool,
rounding: Option<Rounding>,
}
impl ProgressBar {
@ -28,6 +29,7 @@ impl ProgressBar {
text: None,
fill: None,
animate: false,
rounding: None,
}
}
@ -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 {
@ -85,6 +102,7 @@ impl Widget for ProgressBar {
text,
fill,
animate,
rounding,
} = self;
let animate = animate && progress < 1.0;
@ -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 {
@ -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()