diff --git a/AUTHORS b/AUTHORS index 71553959d2..433b42ce09 100644 --- a/AUTHORS +++ b/AUTHORS @@ -16,3 +16,4 @@ Robert Wittams Jaap Aarts Maximilian Köstler Bruno Dupuis +Christopher Noel Hesse diff --git a/CHANGELOG.md b/CHANGELOG.md index 769b79667e..1380617845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ You can find its changes [documented below](#070---2021-01-01). - x11: Set WM_CLASS property ([#1868] by [@psychon]) - Expose `RawWindowHandle` for `WindowHandle` under the `raw-win-handle` feature ([#1828] by [@djeedai]) - `Slider` widget now warns if max < min and swaps the values ([#1882] by [@Maan2003]) +- Widget/Slider: Add stepping functionality ([#1875] by [@raymanfx]) ### Changed diff --git a/druid/examples/widget_gallery.rs b/druid/examples/widget_gallery.rs index f4140468d0..f77e16f97e 100644 --- a/druid/examples/widget_gallery.rs +++ b/druid/examples/widget_gallery.rs @@ -152,10 +152,15 @@ fn ui_builder() -> impl Widget { )) .with_child(label_widget( Flex::column() - .with_child(Slider::new().lens(AppData::progressbar)) + .with_child( + Slider::new() + .with_range(0.05, 0.95) + .with_step(0.10) + .lens(AppData::progressbar), + ) .with_spacer(4.0) .with_child(Label::new(|data: &AppData, _: &_| { - format!("{:3.0}%", data.progressbar * 100.0) + format!("{:3.2}%", data.progressbar * 100.) })), "Slider", )) diff --git a/druid/src/widget/slider.rs b/druid/src/widget/slider.rs index 071c72f14c..5897046696 100644 --- a/druid/src/widget/slider.rs +++ b/druid/src/widget/slider.rs @@ -31,6 +31,7 @@ const KNOB_STROKE_WIDTH: f64 = 2.0; pub struct Slider { min: f64, max: f64, + step: Option, knob_pos: Point, knob_hovered: bool, x_offset: f64, @@ -42,6 +43,7 @@ impl Slider { Slider { min: 0., max: 1., + step: None, knob_pos: Default::default(), knob_hovered: Default::default(), x_offset: Default::default(), @@ -57,6 +59,24 @@ impl Slider { self } + /// Builder-style method to set the stepping. + /// + /// The default step size is `0.0` (smooth). + pub fn with_step(mut self, step: f64) -> Self { + if step < 0.0 { + warn!("bad stepping (must be positive): {}", step); + return self; + } + self.step = if step > 0.0 { + Some(step) + } else { + // A stepping value of 0.0 would yield an infinite amount of steps. + // Enforce no stepping instead. + None + }; + self + } + /// check self.min <= self.max, if not swaps the values. fn check_range(&mut self) { if self.max < self.min { @@ -79,7 +99,24 @@ impl Slider { let scalar = ((mouse_x + self.x_offset - knob_width / 2.) / (slider_width - knob_width)) .max(0.0) .min(1.0); - self.min + scalar * (self.max - self.min) + let mut value = self.min + scalar * (self.max - self.min); + if let Some(step) = self.step { + let max_step_value = ((self.max - self.min) / step).floor() * step + self.min; + if value > max_step_value { + // edge case: make sure max is reachable + let left_dist = value - max_step_value; + let right_dist = self.max - value; + value = if left_dist < right_dist { + max_step_value + } else { + self.max + }; + } else { + // snap to discrete intervals + value = (((value - self.min) / step).round() * step + self.min).min(self.max); + } + } + value } fn normalize(&self, data: f64) -> f64 {