diff options
Diffstat (limited to 'widget')
| -rw-r--r-- | widget/src/slider.rs | 548 | ||||
| -rw-r--r-- | widget/src/vertical_slider.rs | 558 | 
2 files changed, 483 insertions, 623 deletions
| diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 65bc1772..ce02a0a6 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -17,7 +17,7 @@ use crate::core::{  use std::ops::RangeInclusive;  pub use iced_style::slider::{ -    Appearance, Handle, HandleShape, Rail, StyleSheet, +    Appearance, Handle, HandleShape, Rail, Status, StyleSheet,  };  /// An horizontal bar and a handle that selects a single value from a range of @@ -58,7 +58,7 @@ where      on_release: Option<Message>,      width: Length,      height: f32, -    style: Theme::Style, +    style: fn(&Theme, Status) -> Appearance,  }  impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme> @@ -104,7 +104,7 @@ where              on_release: None,              width: Length::Fill,              height: Self::DEFAULT_HEIGHT, -            style: Default::default(), +            style: Theme::default(),          }      } @@ -140,8 +140,8 @@ where      }      /// Sets the style of the [`Slider`]. -    pub fn style(mut self, style: impl Into<Theme::Style>) -> Self { -        self.style = style.into(); +    pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { +        self.style = style;          self      } @@ -173,7 +173,7 @@ where      }      fn state(&self) -> tree::State { -        tree::State::new(State::new()) +        tree::State::new(State::default())      }      fn size(&self) -> Size<Length> { @@ -203,355 +203,283 @@ where          shell: &mut Shell<'_, Message>,          _viewport: &Rectangle,      ) -> event::Status { -        update( -            event, -            layout, -            cursor, -            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, -        ) -    } +        let state = tree.state.downcast_mut::<State>(); -    fn draw( -        &self, -        tree: &Tree, -        renderer: &mut Renderer, -        theme: &Theme, -        _style: &renderer::Style, -        layout: Layout<'_>, -        cursor: mouse::Cursor, -        _viewport: &Rectangle, -    ) { -        draw( -            renderer, -            layout, -            cursor, -            tree.state.downcast_ref::<State>(), -            self.value, -            &self.range, -            theme, -            &self.style, -        ); -    } +        let is_dragging = state.is_dragging; +        let current_value = self.value; -    fn mouse_interaction( -        &self, -        tree: &Tree, -        layout: Layout<'_>, -        cursor: mouse::Cursor, -        _viewport: &Rectangle, -        _renderer: &Renderer, -    ) -> mouse::Interaction { -        mouse_interaction(layout, cursor, tree.state.downcast_ref::<State>()) -    } -} +        let locate = |cursor_position: Point| -> Option<T> { +            let bounds = layout.bounds(); +            let new_value = if cursor_position.x <= bounds.x { +                Some(*self.range.start()) +            } else if cursor_position.x >= bounds.x + bounds.width { +                Some(*self.range.end()) +            } else { +                let step = if state.keyboard_modifiers.shift() { +                    self.shift_step.unwrap_or(self.step) +                } else { +                    self.step +                } +                .into(); -impl<'a, T, Message, Theme, Renderer> From<Slider<'a, T, Message, Theme>> -    for Element<'a, Message, Theme, Renderer> -where -    T: Copy + Into<f64> + num_traits::FromPrimitive + 'a, -    Message: Clone + 'a, -    Theme: StyleSheet + 'a, -    Renderer: crate::core::Renderer + 'a, -{ -    fn from( -        slider: Slider<'a, T, Message, Theme>, -    ) -> Element<'a, Message, Theme, Renderer> { -        Element::new(slider) -    } -} +                let start = (*self.range.start()).into(); +                let end = (*self.range.end()).into(); -/// Processes an [`Event`] and updates the [`State`] of a [`Slider`] -/// accordingly. -pub fn update<Message, T>( -    event: Event, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    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 -where -    T: Copy + Into<f64> + num_traits::FromPrimitive, -    Message: Clone, -{ -    let is_dragging = state.is_dragging; -    let current_value = *value; +                let percent = f64::from(cursor_position.x - bounds.x) +                    / f64::from(bounds.width); -    let locate = |cursor_position: Point| -> Option<T> { -        let bounds = layout.bounds(); -        let new_value = if cursor_position.x <= bounds.x { -            Some(*range.start()) -        } else if cursor_position.x >= bounds.x + bounds.width { -            Some(*range.end()) -        } else { +                let steps = (percent * (end - start) / step).round(); +                let value = steps * step + start; + +                T::from_f64(value) +            }; + +            new_value +        }; + +        let increment = |value: T| -> Option<T> {              let step = if state.keyboard_modifiers.shift() { -                shift_step.unwrap_or(step) +                self.shift_step.unwrap_or(self.step)              } else { -                step +                self.step              }              .into(); -            let start = (*range.start()).into(); -            let end = (*range.end()).into(); +            let steps = (value.into() / step).round(); +            let new_value = step * (steps + 1.0); -            let percent = f64::from(cursor_position.x - bounds.x) -                / f64::from(bounds.width); - -            let steps = (percent * (end - start) / step).round(); -            let value = steps * step + start; +            if new_value > (*self.range.end()).into() { +                return Some(*self.range.end()); +            } -            T::from_f64(value) +            T::from_f64(new_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()); -        } +        let decrement = |value: T| -> Option<T> { +            let step = if state.keyboard_modifiers.shift() { +                self.shift_step.unwrap_or(self.step) +            } else { +                self.step +            } +            .into(); -        T::from_f64(new_value) -    }; +            let steps = (value.into() / step).round(); +            let new_value = step * (steps - 1.0); -    let decrement = |value: T| -> Option<T> { -        let step = if state.keyboard_modifiers.shift() { -            shift_step.unwrap_or(step) -        } else { -            step -        } -        .into(); +            if new_value < (*self.range.start()).into() { +                return Some(*self.range.start()); +            } -        let steps = (value.into() / step).round(); -        let new_value = step * (steps - 1.0); +            T::from_f64(new_value) +        }; -        if new_value < (*range.start()).into() { -            return Some(*range.start()); -        } +        let change = |new_value: T| { +            if (self.value.into() - new_value.into()).abs() > f64::EPSILON { +                shell.publish((self.on_change)(new_value)); -        T::from_f64(new_value) -    }; +                self.value = new_value; +            } +        }; -    let change = |new_value: T| { -        if ((*value).into() - new_value.into()).abs() > f64::EPSILON { -            shell.publish((on_change)(new_value)); +        match event { +            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) +            | Event::Touch(touch::Event::FingerPressed { .. }) => { +                if let Some(cursor_position) = +                    cursor.position_over(layout.bounds()) +                { +                    if state.keyboard_modifiers.command() { +                        let _ = self.default.map(change); +                        state.is_dragging = false; +                    } else { +                        let _ = locate(cursor_position).map(change); +                        state.is_dragging = true; +                    } -            *value = new_value; -        } -    }; - -    match event { -        Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) -        | Event::Touch(touch::Event::FingerPressed { .. }) => { -            if let Some(cursor_position) = cursor.position_over(layout.bounds()) -            { -                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;                  } - -                return event::Status::Captured;              } -        } -        Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) -        | Event::Touch(touch::Event::FingerLifted { .. }) -        | Event::Touch(touch::Event::FingerLost { .. }) => { -            if is_dragging { -                if let Some(on_release) = on_release.clone() { -                    shell.publish(on_release); -                } -                state.is_dragging = false; +            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) +            | Event::Touch(touch::Event::FingerLifted { .. }) +            | Event::Touch(touch::Event::FingerLost { .. }) => { +                if is_dragging { +                    if let Some(on_release) = self.on_release.clone() { +                        shell.publish(on_release); +                    } +                    state.is_dragging = false; -                return event::Status::Captured; +                    return event::Status::Captured; +                }              } -        } -        Event::Mouse(mouse::Event::CursorMoved { .. }) -        | Event::Touch(touch::Event::FingerMoved { .. }) => { -            if is_dragging { -                let _ = cursor.position().and_then(locate).map(change); +            Event::Mouse(mouse::Event::CursorMoved { .. }) +            | Event::Touch(touch::Event::FingerMoved { .. }) => { +                if is_dragging { +                    let _ = cursor.position().and_then(locate).map(change); -                return event::Status::Captured; +                    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); +            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); +                        } +                        _ => (),                      } -                    Key::Named(key::Named::ArrowDown) => { -                        let _ = decrement(current_value).map(change); -                    } -                    _ => (), -                } -                return event::Status::Captured; +                    return event::Status::Captured; +                }              } +            Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { +                state.keyboard_modifiers = modifiers; +            } +            _ => {}          } -        Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { -            state.keyboard_modifiers = modifiers; -        } -        _ => {} + +        event::Status::Ignored      } -    event::Status::Ignored -} +    fn draw( +        &self, +        tree: &Tree, +        renderer: &mut Renderer, +        theme: &Theme, +        _style: &renderer::Style, +        layout: Layout<'_>, +        cursor: mouse::Cursor, +        _viewport: &Rectangle, +    ) { +        let state = tree.state.downcast_ref::<State>(); +        let bounds = layout.bounds(); +        let is_mouse_over = cursor.is_over(bounds); -/// Draws a [`Slider`]. -pub fn draw<T, Theme, Renderer>( -    renderer: &mut Renderer, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    state: &State, -    value: T, -    range: &RangeInclusive<T>, -    theme: &Theme, -    style: &Theme::Style, -) where -    T: Into<f64> + Copy, -    Theme: StyleSheet, -    Renderer: crate::core::Renderer, -{ -    let bounds = layout.bounds(); -    let is_mouse_over = cursor.is_over(bounds); - -    let style = if state.is_dragging { -        theme.dragging(style) -    } else if is_mouse_over { -        theme.hovered(style) -    } else { -        theme.active(style) -    }; - -    let (handle_width, handle_height, handle_border_radius) = -        match style.handle.shape { -            HandleShape::Circle { radius } => { -                (radius * 2.0, radius * 2.0, radius.into()) -            } -            HandleShape::Rectangle { -                width, -                border_radius, -            } => (f32::from(width), bounds.height, border_radius), +        let style = (self.style)( +            theme, +            if state.is_dragging { +                Status::Dragging +            } else if is_mouse_over { +                Status::Hovered +            } else { +                Status::Active +            }, +        ); + +        let (handle_width, handle_height, handle_border_radius) = +            match style.handle.shape { +                HandleShape::Circle { radius } => { +                    (radius * 2.0, radius * 2.0, radius.into()) +                } +                HandleShape::Rectangle { +                    width, +                    border_radius, +                } => (f32::from(width), bounds.height, border_radius), +            }; + +        let value = self.value.into() as f32; +        let (range_start, range_end) = { +            let (start, end) = self.range.clone().into_inner(); + +            (start.into() as f32, end.into() as f32)          }; -    let value = value.into() as f32; -    let (range_start, range_end) = { -        let (start, end) = range.clone().into_inner(); - -        (start.into() as f32, end.into() as f32) -    }; - -    let offset = if range_start >= range_end { -        0.0 -    } else { -        (bounds.width - handle_width) * (value - range_start) -            / (range_end - range_start) -    }; - -    let rail_y = bounds.y + bounds.height / 2.0; - -    renderer.fill_quad( -        renderer::Quad { -            bounds: Rectangle { -                x: bounds.x, -                y: rail_y - style.rail.width / 2.0, -                width: offset + handle_width / 2.0, -                height: style.rail.width, -            }, -            border: Border::with_radius(style.rail.border_radius), -            ..renderer::Quad::default() -        }, -        style.rail.colors.0, -    ); - -    renderer.fill_quad( -        renderer::Quad { -            bounds: Rectangle { -                x: bounds.x + offset + handle_width / 2.0, -                y: rail_y - style.rail.width / 2.0, -                width: bounds.width - offset - handle_width / 2.0, -                height: style.rail.width, +        let offset = if range_start >= range_end { +            0.0 +        } else { +            (bounds.width - handle_width) * (value - range_start) +                / (range_end - range_start) +        }; + +        let rail_y = bounds.y + bounds.height / 2.0; + +        renderer.fill_quad( +            renderer::Quad { +                bounds: Rectangle { +                    x: bounds.x, +                    y: rail_y - style.rail.width / 2.0, +                    width: offset + handle_width / 2.0, +                    height: style.rail.width, +                }, +                border: Border::with_radius(style.rail.border_radius), +                ..renderer::Quad::default()              }, -            border: Border::with_radius(style.rail.border_radius), -            ..renderer::Quad::default() -        }, -        style.rail.colors.1, -    ); - -    renderer.fill_quad( -        renderer::Quad { -            bounds: Rectangle { -                x: bounds.x + offset, -                y: rail_y - handle_height / 2.0, -                width: handle_width, -                height: handle_height, +            style.rail.colors.0, +        ); + +        renderer.fill_quad( +            renderer::Quad { +                bounds: Rectangle { +                    x: bounds.x + offset + handle_width / 2.0, +                    y: rail_y - style.rail.width / 2.0, +                    width: bounds.width - offset - handle_width / 2.0, +                    height: style.rail.width, +                }, +                border: Border::with_radius(style.rail.border_radius), +                ..renderer::Quad::default()              }, -            border: Border { -                radius: handle_border_radius, -                width: style.handle.border_width, -                color: style.handle.border_color, +            style.rail.colors.1, +        ); + +        renderer.fill_quad( +            renderer::Quad { +                bounds: Rectangle { +                    x: bounds.x + offset, +                    y: rail_y - handle_height / 2.0, +                    width: handle_width, +                    height: handle_height, +                }, +                border: Border { +                    radius: handle_border_radius, +                    width: style.handle.border_width, +                    color: style.handle.border_color, +                }, +                ..renderer::Quad::default()              }, -            ..renderer::Quad::default() -        }, -        style.handle.color, -    ); +            style.handle.color, +        ); +    } + +    fn mouse_interaction( +        &self, +        tree: &Tree, +        layout: Layout<'_>, +        cursor: mouse::Cursor, +        _viewport: &Rectangle, +        _renderer: &Renderer, +    ) -> mouse::Interaction { +        let state = tree.state.downcast_ref::<State>(); +        let bounds = layout.bounds(); +        let is_mouse_over = cursor.is_over(bounds); + +        if state.is_dragging { +            mouse::Interaction::Grabbing +        } else if is_mouse_over { +            mouse::Interaction::Grab +        } else { +            mouse::Interaction::default() +        } +    }  } -/// Computes the current [`mouse::Interaction`] of a [`Slider`]. -pub fn mouse_interaction( -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    state: &State, -) -> mouse::Interaction { -    let bounds = layout.bounds(); -    let is_mouse_over = cursor.is_over(bounds); - -    if state.is_dragging { -        mouse::Interaction::Grabbing -    } else if is_mouse_over { -        mouse::Interaction::Grab -    } else { -        mouse::Interaction::default() +impl<'a, T, Message, Theme, Renderer> From<Slider<'a, T, Message, Theme>> +    for Element<'a, Message, Theme, Renderer> +where +    T: Copy + Into<f64> + num_traits::FromPrimitive + 'a, +    Message: Clone + 'a, +    Theme: StyleSheet + 'a, +    Renderer: crate::core::Renderer + 'a, +{ +    fn from( +        slider: Slider<'a, T, Message, Theme>, +    ) -> Element<'a, Message, Theme, Renderer> { +        Element::new(slider)      }  } -/// The local state of a [`Slider`].  #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct State { +struct State {      is_dragging: bool,      keyboard_modifiers: keyboard::Modifiers,  } - -impl State { -    /// Creates a new [`State`]. -    pub fn new() -> State { -        State::default() -    } -} diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index 8f7c88da..b6903001 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -3,7 +3,9 @@  //! A [`VerticalSlider`] has some local [`State`].  use std::ops::RangeInclusive; -pub use crate::style::slider::{Appearance, Handle, HandleShape, StyleSheet}; +pub use crate::style::slider::{ +    Appearance, Handle, HandleShape, Status, StyleSheet, +};  use crate::core;  use crate::core::event::{self, Event}; @@ -55,7 +57,7 @@ where      on_release: Option<Message>,      width: f32,      height: Length, -    style: Theme::Style, +    style: fn(&Theme, Status) -> Appearance,  }  impl<'a, T, Message, Theme> VerticalSlider<'a, T, Message, Theme> @@ -101,7 +103,7 @@ where              on_release: None,              width: Self::DEFAULT_WIDTH,              height: Length::Fill, -            style: Default::default(), +            style: Theme::default(),          }      } @@ -137,7 +139,10 @@ where      }      /// Sets the style of the [`VerticalSlider`]. -    pub fn style(mut self, style: impl Into<Theme::Style>) -> Self { +    pub fn style( +        mut self, +        style: impl Into<fn(&Theme, Status) -> Appearance>, +    ) -> Self {          self.style = style.into();          self      } @@ -170,7 +175,7 @@ where      }      fn state(&self) -> tree::State { -        tree::State::new(State::new()) +        tree::State::new(State::default())      }      fn size(&self) -> Size<Length> { @@ -200,360 +205,287 @@ where          shell: &mut Shell<'_, Message>,          _viewport: &Rectangle,      ) -> event::Status { -        update( -            event, -            layout, -            cursor, -            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, -        ) -    } +        let state = tree.state.downcast_mut::<State>(); +        let is_dragging = state.is_dragging; +        let current_value = self.value; -    fn draw( -        &self, -        tree: &Tree, -        renderer: &mut Renderer, -        theme: &Theme, -        _style: &renderer::Style, -        layout: Layout<'_>, -        cursor: mouse::Cursor, -        _viewport: &Rectangle, -    ) { -        draw( -            renderer, -            layout, -            cursor, -            tree.state.downcast_ref::<State>(), -            self.value, -            &self.range, -            theme, -            &self.style, -        ); -    } +        let locate = |cursor_position: Point| -> Option<T> { +            let bounds = layout.bounds(); -    fn mouse_interaction( -        &self, -        tree: &Tree, -        layout: Layout<'_>, -        cursor: mouse::Cursor, -        _viewport: &Rectangle, -        _renderer: &Renderer, -    ) -> mouse::Interaction { -        mouse_interaction(layout, cursor, tree.state.downcast_ref::<State>()) -    } -} +            let new_value = if cursor_position.y >= bounds.y + bounds.height { +                Some(*self.range.start()) +            } else if cursor_position.y <= bounds.y { +                Some(*self.range.end()) +            } else { +                let step = if state.keyboard_modifiers.shift() { +                    self.shift_step.unwrap_or(self.step) +                } else { +                    self.step +                } +                .into(); -impl<'a, T, Message, Theme, Renderer> -    From<VerticalSlider<'a, T, Message, Theme>> -    for Element<'a, Message, Theme, Renderer> -where -    T: Copy + Into<f64> + num_traits::FromPrimitive + 'a, -    Message: Clone + 'a, -    Theme: StyleSheet + 'a, -    Renderer: core::Renderer + 'a, -{ -    fn from( -        slider: VerticalSlider<'a, T, Message, Theme>, -    ) -> Element<'a, Message, Theme, Renderer> { -        Element::new(slider) -    } -} +                let start = (*self.range.start()).into(); +                let end = (*self.range.end()).into(); -/// Processes an [`Event`] and updates the [`State`] of a [`VerticalSlider`] -/// accordingly. -pub fn update<Message, T>( -    event: Event, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    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 -where -    T: Copy + Into<f64> + num_traits::FromPrimitive, -    Message: Clone, -{ -    let is_dragging = state.is_dragging; -    let current_value = *value; +                let percent = 1.0 +                    - f64::from(cursor_position.y - bounds.y) +                        / f64::from(bounds.height); -    let locate = |cursor_position: Point| -> Option<T> { -        let bounds = layout.bounds(); +                let steps = (percent * (end - start) / step).round(); +                let value = steps * step + start; -        let new_value = if cursor_position.y >= bounds.y + bounds.height { -            Some(*range.start()) -        } else if cursor_position.y <= bounds.y { -            Some(*range.end()) -        } else { +                T::from_f64(value) +            }; + +            new_value +        }; + +        let increment = |value: T| -> Option<T> {              let step = if state.keyboard_modifiers.shift() { -                shift_step.unwrap_or(step) +                self.shift_step.unwrap_or(self.step)              } else { -                step +                self.step              }              .into(); -            let start = (*range.start()).into(); -            let end = (*range.end()).into(); +            let steps = (value.into() / step).round(); +            let new_value = step * (steps + 1.0); -            let percent = 1.0 -                - f64::from(cursor_position.y - bounds.y) -                    / f64::from(bounds.height); - -            let steps = (percent * (end - start) / step).round(); -            let value = steps * step + start; +            if new_value > (*self.range.end()).into() { +                return Some(*self.range.end()); +            } -            T::from_f64(value) +            T::from_f64(new_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() { +                self.shift_step.unwrap_or(self.step) +            } else { +                self.step +            } +            .into(); -    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); -        let steps = (value.into() / step).round(); -        let new_value = step * (steps - 1.0); +            if new_value < (*self.range.start()).into() { +                return Some(*self.range.start()); +            } -        if new_value < (*range.start()).into() { -            return Some(*range.start()); -        } +            T::from_f64(new_value) +        }; -        T::from_f64(new_value) -    }; +        let change = |new_value: T| { +            if (self.value.into() - new_value.into()).abs() > f64::EPSILON { +                shell.publish((self.on_change)(new_value)); -    let change = |new_value: T| { -        if ((*value).into() - new_value.into()).abs() > f64::EPSILON { -            shell.publish((on_change)(new_value)); +                self.value = new_value; +            } +        }; -            *value = new_value; -        } -    }; - -    match event { -        Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) -        | Event::Touch(touch::Event::FingerPressed { .. }) => { -            if let Some(cursor_position) = cursor.position_over(layout.bounds()) -            { -                if state.keyboard_modifiers.control() -                    || state.keyboard_modifiers.command() +        match event { +            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) +            | Event::Touch(touch::Event::FingerPressed { .. }) => { +                if let Some(cursor_position) = +                    cursor.position_over(layout.bounds())                  { -                    let _ = default.map(change); -                    state.is_dragging = false; -                } else { -                    let _ = locate(cursor_position).map(change); -                    state.is_dragging = true; -                } +                    if state.keyboard_modifiers.control() +                        || state.keyboard_modifiers.command() +                    { +                        let _ = self.default.map(change); +                        state.is_dragging = false; +                    } else { +                        let _ = locate(cursor_position).map(change); +                        state.is_dragging = true; +                    } -                return event::Status::Captured; -            } -        } -        Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) -        | Event::Touch(touch::Event::FingerLifted { .. }) -        | Event::Touch(touch::Event::FingerLost { .. }) => { -            if is_dragging { -                if let Some(on_release) = on_release.clone() { -                    shell.publish(on_release); +                    return event::Status::Captured;                  } -                state.is_dragging = false; +            } +            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) +            | Event::Touch(touch::Event::FingerLifted { .. }) +            | Event::Touch(touch::Event::FingerLost { .. }) => { +                if is_dragging { +                    if let Some(on_release) = self.on_release.clone() { +                        shell.publish(on_release); +                    } +                    state.is_dragging = false; -                return event::Status::Captured; +                    return event::Status::Captured; +                }              } -        } -        Event::Mouse(mouse::Event::CursorMoved { .. }) -        | Event::Touch(touch::Event::FingerMoved { .. }) => { -            if is_dragging { -                let _ = cursor.position().and_then(locate).map(change); +            Event::Mouse(mouse::Event::CursorMoved { .. }) +            | Event::Touch(touch::Event::FingerMoved { .. }) => { +                if is_dragging { +                    let _ = cursor.position().and_then(locate).map(change); -                return event::Status::Captured; +                    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); +            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; +                    return event::Status::Captured; +                }              } +            Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { +                state.keyboard_modifiers = modifiers; +            } +            _ => {}          } -        Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { -            state.keyboard_modifiers = modifiers; -        } -        _ => {} + +        event::Status::Ignored      } -    event::Status::Ignored -} +    fn draw( +        &self, +        tree: &Tree, +        renderer: &mut Renderer, +        theme: &Theme, +        _style: &renderer::Style, +        layout: Layout<'_>, +        cursor: mouse::Cursor, +        _viewport: &Rectangle, +    ) { +        let state = tree.state.downcast_ref::<State>(); +        let bounds = layout.bounds(); +        let is_mouse_over = cursor.is_over(bounds); -/// Draws a [`VerticalSlider`]. -pub fn draw<T, Theme, Renderer>( -    renderer: &mut Renderer, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    state: &State, -    value: T, -    range: &RangeInclusive<T>, -    style_sheet: &Theme, -    style: &Theme::Style, -) where -    T: Into<f64> + Copy, -    Theme: StyleSheet, -    Renderer: core::Renderer, -{ -    let bounds = layout.bounds(); -    let is_mouse_over = cursor.is_over(bounds); - -    let style = if state.is_dragging { -        style_sheet.dragging(style) -    } else if is_mouse_over { -        style_sheet.hovered(style) -    } else { -        style_sheet.active(style) -    }; - -    let (handle_width, handle_height, handle_border_radius) = -        match style.handle.shape { -            HandleShape::Circle { radius } => { -                (radius * 2.0, radius * 2.0, radius.into()) -            } -            HandleShape::Rectangle { -                width, -                border_radius, -            } => (f32::from(width), bounds.width, border_radius), +        let style = (self.style)( +            theme, +            if state.is_dragging { +                Status::Dragging +            } else if is_mouse_over { +                Status::Hovered +            } else { +                Status::Active +            }, +        ); + +        let (handle_width, handle_height, handle_border_radius) = +            match style.handle.shape { +                HandleShape::Circle { radius } => { +                    (radius * 2.0, radius * 2.0, radius.into()) +                } +                HandleShape::Rectangle { +                    width, +                    border_radius, +                } => (f32::from(width), bounds.width, border_radius), +            }; + +        let value = self.value.into() as f32; +        let (range_start, range_end) = { +            let (start, end) = self.range.clone().into_inner(); + +            (start.into() as f32, end.into() as f32)          }; -    let value = value.into() as f32; -    let (range_start, range_end) = { -        let (start, end) = range.clone().into_inner(); - -        (start.into() as f32, end.into() as f32) -    }; - -    let offset = if range_start >= range_end { -        0.0 -    } else { -        (bounds.height - handle_width) * (value - range_end) -            / (range_start - range_end) -    }; - -    let rail_x = bounds.x + bounds.width / 2.0; - -    renderer.fill_quad( -        renderer::Quad { -            bounds: Rectangle { -                x: rail_x - style.rail.width / 2.0, -                y: bounds.y, -                width: style.rail.width, -                height: offset + handle_width / 2.0, -            }, -            border: Border::with_radius(style.rail.border_radius), -            ..renderer::Quad::default() -        }, -        style.rail.colors.1, -    ); - -    renderer.fill_quad( -        renderer::Quad { -            bounds: Rectangle { -                x: rail_x - style.rail.width / 2.0, -                y: bounds.y + offset + handle_width / 2.0, -                width: style.rail.width, -                height: bounds.height - offset - handle_width / 2.0, +        let offset = if range_start >= range_end { +            0.0 +        } else { +            (bounds.height - handle_width) * (value - range_end) +                / (range_start - range_end) +        }; + +        let rail_x = bounds.x + bounds.width / 2.0; + +        renderer.fill_quad( +            renderer::Quad { +                bounds: Rectangle { +                    x: rail_x - style.rail.width / 2.0, +                    y: bounds.y, +                    width: style.rail.width, +                    height: offset + handle_width / 2.0, +                }, +                border: Border::with_radius(style.rail.border_radius), +                ..renderer::Quad::default()              }, -            border: Border::with_radius(style.rail.border_radius), -            ..renderer::Quad::default() -        }, -        style.rail.colors.0, -    ); - -    renderer.fill_quad( -        renderer::Quad { -            bounds: Rectangle { -                x: rail_x - handle_height / 2.0, -                y: bounds.y + offset, -                width: handle_height, -                height: handle_width, +            style.rail.colors.1, +        ); + +        renderer.fill_quad( +            renderer::Quad { +                bounds: Rectangle { +                    x: rail_x - style.rail.width / 2.0, +                    y: bounds.y + offset + handle_width / 2.0, +                    width: style.rail.width, +                    height: bounds.height - offset - handle_width / 2.0, +                }, +                border: Border::with_radius(style.rail.border_radius), +                ..renderer::Quad::default()              }, -            border: Border { -                radius: handle_border_radius, -                width: style.handle.border_width, -                color: style.handle.border_color, +            style.rail.colors.0, +        ); + +        renderer.fill_quad( +            renderer::Quad { +                bounds: Rectangle { +                    x: rail_x - handle_height / 2.0, +                    y: bounds.y + offset, +                    width: handle_height, +                    height: handle_width, +                }, +                border: Border { +                    radius: handle_border_radius, +                    width: style.handle.border_width, +                    color: style.handle.border_color, +                }, +                ..renderer::Quad::default()              }, -            ..renderer::Quad::default() -        }, -        style.handle.color, -    ); +            style.handle.color, +        ); +    } + +    fn mouse_interaction( +        &self, +        tree: &Tree, +        layout: Layout<'_>, +        cursor: mouse::Cursor, +        _viewport: &Rectangle, +        _renderer: &Renderer, +    ) -> mouse::Interaction { +        let state = tree.state.downcast_ref::<State>(); +        let bounds = layout.bounds(); +        let is_mouse_over = cursor.is_over(bounds); + +        if state.is_dragging { +            mouse::Interaction::Grabbing +        } else if is_mouse_over { +            mouse::Interaction::Grab +        } else { +            mouse::Interaction::default() +        } +    }  } -/// Computes the current [`mouse::Interaction`] of a [`VerticalSlider`]. -pub fn mouse_interaction( -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    state: &State, -) -> mouse::Interaction { -    let bounds = layout.bounds(); -    let is_mouse_over = cursor.is_over(bounds); - -    if state.is_dragging { -        mouse::Interaction::Grabbing -    } else if is_mouse_over { -        mouse::Interaction::Grab -    } else { -        mouse::Interaction::default() +impl<'a, T, Message, Theme, Renderer> +    From<VerticalSlider<'a, T, Message, Theme>> +    for Element<'a, Message, Theme, Renderer> +where +    T: Copy + Into<f64> + num_traits::FromPrimitive + 'a, +    Message: Clone + 'a, +    Theme: StyleSheet + 'a, +    Renderer: core::Renderer + 'a, +{ +    fn from( +        slider: VerticalSlider<'a, T, Message, Theme>, +    ) -> Element<'a, Message, Theme, Renderer> { +        Element::new(slider)      }  } -/// The local state of a [`VerticalSlider`].  #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct State { +struct State {      is_dragging: bool,      keyboard_modifiers: keyboard::Modifiers,  } - -impl State { -    /// Creates a new [`State`]. -    pub fn new() -> State { -        State::default() -    } -} | 
