feat(progress_bar): linear progress markers
This commit is contained in:
parent
6f863f1905
commit
466e25d9f8
2 changed files with 143 additions and 30 deletions
|
|
@ -240,7 +240,9 @@ impl cosmic::Application for App {
|
||||||
widget::progress_bar::linear::Linear::new()
|
widget::progress_bar::linear::Linear::new()
|
||||||
.girth(10.0)
|
.girth(10.0)
|
||||||
.progress(self.progress)
|
.progress(self.progress)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill)
|
||||||
|
.markers([0.25, 0.5, 0.75])
|
||||||
|
.segment_spacing(2),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
widget::progress_bar::circular::Circular::new()
|
widget::progress_bar::circular::Circular::new()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use super::animation::{Animation, Progress};
|
||||||
use super::style::StyleSheet;
|
use super::style::StyleSheet;
|
||||||
use iced::advanced::widget::tree::{self, Tree};
|
use iced::advanced::widget::tree::{self, Tree};
|
||||||
use iced::advanced::{self, Clipboard, Layout, Shell, Widget, layout, renderer};
|
use iced::advanced::{self, Clipboard, Layout, Shell, Widget, layout, renderer};
|
||||||
use iced::{Background, Element, Event, Length, Rectangle, Size, mouse, window};
|
use iced::{Background, Element, Event, Length, Pixels, Rectangle, Size, mouse, window};
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -21,6 +21,8 @@ where
|
||||||
cycle_duration: Duration,
|
cycle_duration: Duration,
|
||||||
period: Duration,
|
period: Duration,
|
||||||
progress: Option<f32>,
|
progress: Option<f32>,
|
||||||
|
markers: Vec<f32>,
|
||||||
|
segment_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Theme> Linear<Theme>
|
impl<Theme> Linear<Theme>
|
||||||
|
|
@ -36,6 +38,8 @@ where
|
||||||
cycle_duration: Duration::from_millis(1500),
|
cycle_duration: Duration::from_millis(1500),
|
||||||
period: Duration::from_secs(2),
|
period: Duration::from_secs(2),
|
||||||
progress: None,
|
progress: None,
|
||||||
|
markers: Vec::new(),
|
||||||
|
segment_spacing: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +79,26 @@ where
|
||||||
self.progress = Some(progress.clamp(0.0, 1.0));
|
self.progress = Some(progress.clamp(0.0, 1.0));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the markers of a determinate progress bar, which divide the bar into segments.
|
||||||
|
/// Each value is a progress fraction between `0.0` and `1.0 at which a visual gap is inserted.
|
||||||
|
pub fn markers(mut self, markers: impl Into<Vec<f32>>) -> Self {
|
||||||
|
let mut markers = markers.into();
|
||||||
|
for bp in &mut markers {
|
||||||
|
*bp = bp.clamp(0.0, 1.0);
|
||||||
|
}
|
||||||
|
markers.sort_by(f32::total_cmp);
|
||||||
|
markers.dedup();
|
||||||
|
|
||||||
|
self.markers = markers;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the spacing between segments at each marker.
|
||||||
|
pub fn segment_spacing(mut self, spacing: impl Into<Pixels>) -> Self {
|
||||||
|
self.segment_spacing = spacing.into().0;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Theme> Default for Linear<Theme>
|
impl<Theme> Default for Linear<Theme>
|
||||||
|
|
@ -165,26 +189,17 @@ where
|
||||||
let custom_style = theme.appearance(&self.style, self.progress.is_some(), false);
|
let custom_style = theme.appearance(&self.style, self.progress.is_some(), false);
|
||||||
let state = tree.state.downcast_ref::<State>();
|
let state = tree.state.downcast_ref::<State>();
|
||||||
|
|
||||||
renderer.fill_quad(
|
let border_width = if custom_style.border_color.is_some() {
|
||||||
renderer::Quad {
|
1.0
|
||||||
bounds,
|
} else {
|
||||||
border: iced::Border {
|
0.0
|
||||||
width: if custom_style.border_color.is_some() {
|
};
|
||||||
1.0
|
let border_color = custom_style.border_color.unwrap_or(custom_style.bar_color);
|
||||||
} else {
|
let radius = custom_style.border_radius;
|
||||||
0.0
|
|
||||||
},
|
|
||||||
color: custom_style.border_color.unwrap_or(custom_style.bar_color),
|
|
||||||
radius: custom_style.border_radius.into(),
|
|
||||||
},
|
|
||||||
snap: true,
|
|
||||||
..renderer::Quad::default()
|
|
||||||
},
|
|
||||||
Background::Color(custom_style.track_color),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut draw_segment = |x: f32, width: f32| {
|
let mut draw_quad = |x: f32, width: f32, color: iced::Color, border: iced::Border| {
|
||||||
if width > 0.001 {
|
// don't draw if width is less than 0.1 pixels
|
||||||
|
if width * bounds.width > 0.1 {
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
|
|
@ -193,22 +208,102 @@ where
|
||||||
width: width * bounds.width,
|
width: width * bounds.width,
|
||||||
height: bounds.height,
|
height: bounds.height,
|
||||||
},
|
},
|
||||||
border: iced::Border {
|
border,
|
||||||
width: 0.0,
|
|
||||||
color: iced::Color::TRANSPARENT,
|
|
||||||
radius: custom_style.border_radius.into(),
|
|
||||||
},
|
|
||||||
snap: true,
|
snap: true,
|
||||||
..renderer::Quad::default()
|
..renderer::Quad::default()
|
||||||
},
|
},
|
||||||
Background::Color(custom_style.bar_color),
|
Background::Color(color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.progress.is_some() {
|
if self.progress.is_some() {
|
||||||
draw_segment(0.0, state.progress.current);
|
let spacing = self.segment_spacing.max(1.0);
|
||||||
|
let radius_inner = radius.min(spacing);
|
||||||
|
|
||||||
|
let gap = if self.markers.is_empty() {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
spacing / bounds.width
|
||||||
|
};
|
||||||
|
let drawable = 1.0 - gap * self.markers.len() as f32;
|
||||||
|
let num_segments = self.markers.len() + 1;
|
||||||
|
|
||||||
|
let segment_bounds = |i: usize| {
|
||||||
|
let seg_lo = if i == 0 { 0.0 } else { self.markers[i - 1] };
|
||||||
|
let seg_hi = if i == num_segments - 1 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
self.markers[i]
|
||||||
|
};
|
||||||
|
(seg_lo, seg_hi)
|
||||||
|
};
|
||||||
|
let get_radius = |i: usize| {
|
||||||
|
let r_left = if i == 0 { radius } else { radius_inner };
|
||||||
|
let r_right = if i == num_segments - 1 {
|
||||||
|
radius
|
||||||
|
} else {
|
||||||
|
radius_inner
|
||||||
|
};
|
||||||
|
[r_left, r_right, r_right, r_left].into()
|
||||||
|
};
|
||||||
|
|
||||||
|
// draw track segments
|
||||||
|
for i in 0..num_segments {
|
||||||
|
let (seg_lo, seg_hi) = segment_bounds(i);
|
||||||
|
let x_start = seg_lo * drawable + i as f32 * gap;
|
||||||
|
let x_width = (seg_hi - seg_lo) * drawable;
|
||||||
|
|
||||||
|
draw_quad(
|
||||||
|
x_start,
|
||||||
|
x_width,
|
||||||
|
custom_style.track_color,
|
||||||
|
iced::Border {
|
||||||
|
width: border_width,
|
||||||
|
color: border_color,
|
||||||
|
radius: get_radius(i),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw bar segments
|
||||||
|
let current_p = state.progress.current;
|
||||||
|
for i in 0..num_segments {
|
||||||
|
let (seg_lo, seg_hi) = segment_bounds(i);
|
||||||
|
|
||||||
|
// don't iterate over non-filled segments
|
||||||
|
if current_p < seg_lo {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let x_start = seg_lo * drawable + i as f32 * gap;
|
||||||
|
let x_width = (seg_hi - seg_lo) * drawable;
|
||||||
|
let fill = ((current_p - seg_lo) / (seg_hi - seg_lo)).clamp(0.0, 1.0);
|
||||||
|
|
||||||
|
draw_quad(
|
||||||
|
x_start,
|
||||||
|
x_width * fill,
|
||||||
|
custom_style.bar_color,
|
||||||
|
iced::Border {
|
||||||
|
radius: get_radius(i),
|
||||||
|
..iced::Border::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// draw track
|
||||||
|
draw_quad(
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
custom_style.track_color,
|
||||||
|
iced::Border {
|
||||||
|
width: border_width,
|
||||||
|
color: border_color,
|
||||||
|
radius: radius.into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// draw bar
|
||||||
let (bar_start, bar_end) =
|
let (bar_start, bar_end) =
|
||||||
state
|
state
|
||||||
.animation
|
.animation
|
||||||
|
|
@ -218,8 +313,24 @@ where
|
||||||
let right_width = (1.0 - start).min(length);
|
let right_width = (1.0 - start).min(length);
|
||||||
let left_width = length - right_width;
|
let left_width = length - right_width;
|
||||||
|
|
||||||
draw_segment(start, right_width);
|
draw_quad(
|
||||||
draw_segment(0.0, left_width);
|
start,
|
||||||
|
right_width,
|
||||||
|
custom_style.bar_color,
|
||||||
|
iced::Border {
|
||||||
|
radius: radius.into(),
|
||||||
|
..iced::Border::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
draw_quad(
|
||||||
|
0.0,
|
||||||
|
left_width,
|
||||||
|
custom_style.bar_color,
|
||||||
|
iced::Border {
|
||||||
|
radius: radius.into(),
|
||||||
|
..iced::Border::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue