diff --git a/crates/egui/src/widgets/double_slider.rs b/crates/egui/src/widgets/double_slider.rs index f3e99857b93..224d07efbd6 100644 --- a/crates/egui/src/widgets/double_slider.rs +++ b/crates/egui/src/widgets/double_slider.rs @@ -1,4 +1,3 @@ -use crate::emath; use crate::emath::{Pos2, Rect, Vec2}; use crate::epaint::{CircleShape, Color32, PathShape, Shape, Stroke}; use crate::{Sense, Ui, Widget}; @@ -7,6 +6,21 @@ use std::ops::RangeInclusive; // offset for stroke highlight const OFFSET: f32 = 2.0; +/// Control two numbers with a double slider. +/// +/// The slider range defines the values you get when pulling the slider to the far edges. +/// +/// The range can include any numbers, and go from low-to-high or from high-to-low. +/// +/// +/// ``` +/// # egui::__run_test_ui(|ui| { +/// # let mut my_f32: f32 = 0.0; +/// # let mut my_other_f32: f32 = 0.0; +/// ui.add(egui::DoubleSlider::new(&mut my_f32,&mut my_other_f32, 0.0..=100.0)); +/// # }); +/// ``` +/// #[must_use = "You should put this widget in a ui with `ui.add(widget);`"] pub struct DoubleSlider<'a> { left_slider: &'a mut f32, @@ -39,64 +53,64 @@ impl<'a> DoubleSlider<'a> { } } - #[allow(dead_code)] /// Set the primary width for the slider. + #[inline] pub fn width(mut self, width: f32) -> Self { self.width = width; self } - #[allow(dead_code)] /// Set the separation distance for the two sliders. + #[inline] pub fn separation_distance(mut self, separation_distance: f32) -> Self { self.separation_distance = separation_distance; self } - #[allow(dead_code)] /// Set the primary color for the slider. + #[inline] pub fn color(mut self, color: Color32) -> Self { self.color = color; self } - #[allow(dead_code)] /// Set the stroke for the main line. + #[inline] pub fn stroke(mut self, stroke: Stroke) -> Self { self.stroke = stroke; self } - #[allow(dead_code)] /// Set the color fill for the slider cursor. + #[inline] pub fn cursor_fill(mut self, cursor_fill: Color32) -> Self { self.cursor_fill = cursor_fill; self } - #[allow(dead_code)] /// Set the auxiliary stroke. + #[inline] pub fn aux_stroke(mut self, aux_stroke: Stroke) -> Self { self.stroke = aux_stroke; self } - #[allow(dead_code)] /// Set the control point radius + #[inline] pub fn control_point_radius(mut self, control_point_radius: f32) -> Self { self.control_point_radius = control_point_radius; self } - fn to_coord(&self, num: f32) -> f32 { + fn val_to_x(&self, val: f32) -> f32 { (self.width - 2.0 * self.control_point_radius - 2.0 * OFFSET) / (self.range.end() - self.range.start()) - * (num - self.range.start()) + * (val - self.range.start()) + self.control_point_radius + OFFSET } - fn from_coord(&self, x: f32) -> f32 { + fn x_to_val(&self, x: f32) -> f32 { (self.range.end() - self.range.start()) / (self.width - 2.0 * self.control_point_radius - 2.0 * OFFSET) * x @@ -105,6 +119,7 @@ impl<'a> DoubleSlider<'a> { impl<'a> Widget for DoubleSlider<'a> { fn ui(self, ui: &mut Ui) -> crate::Response { + // calculate height let height = 2.0 * self.control_point_radius + 2.0 * OFFSET; let (response, painter) = @@ -114,6 +129,7 @@ impl<'a> Widget for DoubleSlider<'a> { let mut right_edge = response.rect.right_center(); right_edge.x -= self.control_point_radius; + // draw the line painter.add(PathShape::line( vec![left_edge, right_edge], Stroke::new(self.stroke.width, self.color), @@ -124,17 +140,20 @@ impl<'a> Widget for DoubleSlider<'a> { response.rect, ); + // handle lower bound let lower_bound = { + // get the control point let size = Vec2::splat(2.0 * self.control_point_radius); let point_in_screen = to_screen.transform_pos(Pos2 { - x: self.to_coord(*self.left_slider), + x: self.val_to_x(*self.left_slider), y: self.control_point_radius + OFFSET, }); let point_rect = Rect::from_center_size(point_in_screen, size); let point_id = response.id.with(0); let point_response = ui.interact(point_rect, point_id, Sense::drag()); - *self.left_slider += self.from_coord(point_response.drag_delta().x); + // handle logic + *self.left_slider += self.x_to_val(point_response.drag_delta().x); if *self.right_slider < *self.left_slider + self.separation_distance { *self.right_slider = *self.left_slider + self.separation_distance; } @@ -155,17 +174,20 @@ impl<'a> Widget for DoubleSlider<'a> { } }; + // handle upper bound let upper_bound = { + // get the control point let size = Vec2::splat(2.0 * self.control_point_radius); let point_in_screen = to_screen.transform_pos(Pos2 { - x: self.to_coord(*self.right_slider), + x: self.val_to_x(*self.right_slider), y: self.control_point_radius + OFFSET, }); let point_rect = Rect::from_center_size(point_in_screen, size); let point_id = response.id.with(1); let point_response = ui.interact(point_rect, point_id, Sense::drag()); - *self.right_slider += self.from_coord(point_response.drag_delta().x); + // handle logic + *self.right_slider += self.x_to_val(point_response.drag_delta().x); if *self.left_slider > *self.right_slider - self.separation_distance { *self.left_slider = *self.right_slider - self.separation_distance; } @@ -187,8 +209,8 @@ impl<'a> Widget for DoubleSlider<'a> { }; let points_in_screen: Vec = [ - self.to_coord(*self.left_slider), - self.to_coord(*self.right_slider), + self.val_to_x(*self.left_slider), + self.val_to_x(*self.right_slider), ] .iter() .map(|p| { @@ -200,7 +222,9 @@ impl<'a> Widget for DoubleSlider<'a> { }) .collect(); + // draw line between points painter.add(PathShape::line(points_in_screen, self.stroke)); + // draw control points painter.extend([Shape::Circle(lower_bound), Shape::Circle(upper_bound)]); response