summaryrefslogtreecommitdiffstats
path: root/widget/src/slider.rs
diff options
context:
space:
mode:
Diffstat (limited to 'widget/src/slider.rs')
-rw-r--r--widget/src/slider.rs119
1 files changed, 106 insertions, 13 deletions
diff --git a/widget/src/slider.rs b/widget/src/slider.rs
index 5c3b6384..65bc1772 100644
--- a/widget/src/slider.rs
+++ b/widget/src/slider.rs
@@ -2,6 +2,8 @@
//!
//! A [`Slider`] has some local [`State`].
use crate::core::event::{self, Event};
+use crate::core::keyboard;
+use crate::core::keyboard::key::{self, Key};
use crate::core::layout;
use crate::core::mouse;
use crate::core::renderer;
@@ -49,7 +51,9 @@ where
{
range: RangeInclusive<T>,
step: T,
+ shift_step: Option<T>,
value: T,
+ default: Option<T>,
on_change: Box<dyn Fn(T) -> Message + 'a>,
on_release: Option<Message>,
width: Length,
@@ -59,7 +63,7 @@ where
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
where
- T: Copy + From<u8> + std::cmp::PartialOrd,
+ T: Copy + From<u8> + PartialOrd,
Message: Clone,
Theme: StyleSheet,
{
@@ -92,8 +96,10 @@ where
Slider {
value,
+ default: None,
range,
step: T::from(1),
+ shift_step: None,
on_change: Box::new(on_change),
on_release: None,
width: Length::Fill,
@@ -102,6 +108,14 @@ where
}
}
+ /// Sets the optional default value for the [`Slider`].
+ ///
+ /// If set, the [`Slider`] will reset to this value when ctrl-clicked or command-clicked.
+ pub fn default(mut self, default: impl Into<T>) -> Self {
+ self.default = Some(default.into());
+ self
+ }
+
/// Sets the release message of the [`Slider`].
/// This is called when the mouse is released from the slider.
///
@@ -136,6 +150,14 @@ where
self.step = step.into();
self
}
+
+ /// Sets the optional "shift" step for the [`Slider`].
+ ///
+ /// If set, this value is used as the step while the shift key is pressed.
+ pub fn shift_step(mut self, shift_step: impl Into<T>) -> Self {
+ self.shift_step = Some(shift_step.into());
+ self
+ }
}
impl<'a, T, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
@@ -188,8 +210,10 @@ where
shell,
tree.state.downcast_mut::<State>(),
&mut self.value,
+ self.default,
&self.range,
self.step,
+ self.shift_step,
self.on_change.as_ref(),
&self.on_release,
)
@@ -253,8 +277,10 @@ pub fn update<Message, T>(
shell: &mut Shell<'_, Message>,
state: &mut State,
value: &mut T,
+ default: Option<T>,
range: &RangeInclusive<T>,
step: T,
+ shift_step: Option<T>,
on_change: &dyn Fn(T) -> Message,
on_release: &Option<Message>,
) -> event::Status
@@ -263,15 +289,22 @@ where
Message: Clone,
{
let is_dragging = state.is_dragging;
+ let current_value = *value;
- let mut change = |cursor_position: Point| {
+ let locate = |cursor_position: Point| -> Option<T> {
let bounds = layout.bounds();
let new_value = if cursor_position.x <= bounds.x {
- *range.start()
+ Some(*range.start())
} else if cursor_position.x >= bounds.x + bounds.width {
- *range.end()
+ Some(*range.end())
} else {
- let step = step.into();
+ let step = if state.keyboard_modifiers.shift() {
+ shift_step.unwrap_or(step)
+ } else {
+ step
+ }
+ .into();
+
let start = (*range.start()).into();
let end = (*range.end()).into();
@@ -281,13 +314,49 @@ where
let steps = (percent * (end - start) / step).round();
let value = steps * step + start;
- if let Some(value) = T::from_f64(value) {
- value
- } else {
- return;
- }
+ T::from_f64(value)
};
+ new_value
+ };
+
+ let increment = |value: T| -> Option<T> {
+ let step = if state.keyboard_modifiers.shift() {
+ shift_step.unwrap_or(step)
+ } else {
+ step
+ }
+ .into();
+
+ let steps = (value.into() / step).round();
+ let new_value = step * (steps + 1.0);
+
+ if new_value > (*range.end()).into() {
+ return Some(*range.end());
+ }
+
+ T::from_f64(new_value)
+ };
+
+ let decrement = |value: T| -> Option<T> {
+ let step = if state.keyboard_modifiers.shift() {
+ shift_step.unwrap_or(step)
+ } else {
+ step
+ }
+ .into();
+
+ let steps = (value.into() / step).round();
+ let new_value = step * (steps - 1.0);
+
+ if new_value < (*range.start()).into() {
+ return Some(*range.start());
+ }
+
+ T::from_f64(new_value)
+ };
+
+ let change = |new_value: T| {
if ((*value).into() - new_value.into()).abs() > f64::EPSILON {
shell.publish((on_change)(new_value));
@@ -300,8 +369,13 @@ where
| Event::Touch(touch::Event::FingerPressed { .. }) => {
if let Some(cursor_position) = cursor.position_over(layout.bounds())
{
- change(cursor_position);
- state.is_dragging = true;
+ if state.keyboard_modifiers.command() {
+ let _ = default.map(change);
+ state.is_dragging = false;
+ } else {
+ let _ = locate(cursor_position).map(change);
+ state.is_dragging = true;
+ }
return event::Status::Captured;
}
@@ -321,11 +395,29 @@ where
Event::Mouse(mouse::Event::CursorMoved { .. })
| Event::Touch(touch::Event::FingerMoved { .. }) => {
if is_dragging {
- let _ = cursor.position().map(change);
+ let _ = cursor.position().and_then(locate).map(change);
+
+ return event::Status::Captured;
+ }
+ }
+ Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
+ if cursor.position_over(layout.bounds()).is_some() {
+ match key {
+ Key::Named(key::Named::ArrowUp) => {
+ let _ = increment(current_value).map(change);
+ }
+ Key::Named(key::Named::ArrowDown) => {
+ let _ = decrement(current_value).map(change);
+ }
+ _ => (),
+ }
return event::Status::Captured;
}
}
+ Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
+ state.keyboard_modifiers = modifiers;
+ }
_ => {}
}
@@ -454,6 +546,7 @@ pub fn mouse_interaction(
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct State {
is_dragging: bool,
+ keyboard_modifiers: keyboard::Modifiers,
}
impl State {