diff options
Diffstat (limited to 'native/src/widget/vertical_slider.rs')
-rw-r--r-- | native/src/widget/vertical_slider.rs | 468 |
1 files changed, 0 insertions, 468 deletions
diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs deleted file mode 100644 index f1687e38..00000000 --- a/native/src/widget/vertical_slider.rs +++ /dev/null @@ -1,468 +0,0 @@ -//! Display an interactive selector of a single value from a range of values. -//! -//! A [`VerticalSlider`] has some local [`State`]. -use std::ops::RangeInclusive; - -pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; - -use crate::event::{self, Event}; -use crate::widget::tree::{self, Tree}; -use crate::{ - layout, mouse, renderer, touch, Background, Clipboard, Color, Element, - Layout, Length, Pixels, Point, Rectangle, Shell, Size, Widget, -}; - -/// An vertical bar and a handle that selects a single value from a range of -/// values. -/// -/// A [`VerticalSlider`] will try to fill the vertical space of its container. -/// -/// The [`VerticalSlider`] range of numeric values is generic and its step size defaults -/// to 1 unit. -/// -/// # Example -/// ``` -/// # use iced_native::widget::vertical_slider; -/// # use iced_native::renderer::Null; -/// # -/// # type VerticalSlider<'a, T, Message> = vertical_slider::VerticalSlider<'a, T, Message, Null>; -/// # -/// #[derive(Clone)] -/// pub enum Message { -/// SliderChanged(f32), -/// } -/// -/// let value = 50.0; -/// -/// VerticalSlider::new(0.0..=100.0, value, Message::SliderChanged); -/// ``` -#[allow(missing_debug_implementations)] -pub struct VerticalSlider<'a, T, Message, Renderer> -where - Renderer: crate::Renderer, - Renderer::Theme: StyleSheet, -{ - range: RangeInclusive<T>, - step: T, - value: T, - on_change: Box<dyn Fn(T) -> Message + 'a>, - on_release: Option<Message>, - width: f32, - height: Length, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, T, Message, Renderer> VerticalSlider<'a, T, Message, Renderer> -where - T: Copy + From<u8> + std::cmp::PartialOrd, - Message: Clone, - Renderer: crate::Renderer, - Renderer::Theme: StyleSheet, -{ - /// The default width of a [`VerticalSlider`]. - pub const DEFAULT_WIDTH: f32 = 22.0; - - /// Creates a new [`VerticalSlider`]. - /// - /// It expects: - /// * an inclusive range of possible values - /// * the current value of the [`VerticalSlider`] - /// * a function that will be called when the [`VerticalSlider`] is dragged. - /// It receives the new value of the [`VerticalSlider`] and must produce a - /// `Message`. - pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self - where - F: 'a + Fn(T) -> Message, - { - let value = if value >= *range.start() { - value - } else { - *range.start() - }; - - let value = if value <= *range.end() { - value - } else { - *range.end() - }; - - VerticalSlider { - value, - range, - step: T::from(1), - on_change: Box::new(on_change), - on_release: None, - width: Self::DEFAULT_WIDTH, - height: Length::Fill, - style: Default::default(), - } - } - - /// Sets the release message of the [`VerticalSlider`]. - /// This is called when the mouse is released from the slider. - /// - /// Typically, the user's interaction with the slider is finished when this message is produced. - /// This is useful if you need to spawn a long-running task from the slider's result, where - /// the default on_change message could create too many events. - pub fn on_release(mut self, on_release: Message) -> Self { - self.on_release = Some(on_release); - self - } - - /// Sets the width of the [`VerticalSlider`]. - pub fn width(mut self, width: impl Into<Pixels>) -> Self { - self.width = width.into().0; - self - } - - /// Sets the height of the [`VerticalSlider`]. - pub fn height(mut self, height: impl Into<Length>) -> Self { - self.height = height.into(); - self - } - - /// Sets the style of the [`VerticalSlider`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Sets the step size of the [`VerticalSlider`]. - pub fn step(mut self, step: T) -> Self { - self.step = step; - self - } -} - -impl<'a, T, Message, Renderer> Widget<Message, Renderer> - for VerticalSlider<'a, T, Message, Renderer> -where - T: Copy + Into<f64> + num_traits::FromPrimitive, - Message: Clone, - Renderer: crate::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<State>() - } - - fn state(&self) -> tree::State { - tree::State::new(State::new()) - } - - fn width(&self) -> Length { - Length::Shrink - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - update( - event, - layout, - cursor_position, - shell, - tree.state.downcast_mut::<State>(), - &mut self.value, - &self.range, - self.step, - self.on_change.as_ref(), - &self.on_release, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - draw( - renderer, - layout, - cursor_position, - tree.state.downcast_ref::<State>(), - self.value, - &self.range, - theme, - &self.style, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - mouse_interaction( - layout, - cursor_position, - tree.state.downcast_ref::<State>(), - ) - } -} - -impl<'a, T, Message, Renderer> From<VerticalSlider<'a, T, Message, Renderer>> - for Element<'a, Message, Renderer> -where - T: 'a + Copy + Into<f64> + num_traits::FromPrimitive, - Message: 'a + Clone, - Renderer: 'a + crate::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - slider: VerticalSlider<'a, T, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(slider) - } -} - -/// Processes an [`Event`] and updates the [`State`] of a [`VerticalSlider`] -/// accordingly. -pub fn update<Message, T>( - event: Event, - layout: Layout<'_>, - cursor_position: Point, - shell: &mut Shell<'_, Message>, - state: &mut State, - value: &mut T, - range: &RangeInclusive<T>, - step: 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 mut change = || { - let bounds = layout.bounds(); - let new_value = if cursor_position.y >= bounds.y + bounds.height { - *range.start() - } else if cursor_position.y <= bounds.y { - *range.end() - } else { - let step = step.into(); - let start = (*range.start()).into(); - let end = (*range.end()).into(); - - 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 let Some(value) = T::from_f64(value) { - value - } else { - return; - } - }; - - if ((*value).into() - new_value.into()).abs() > f64::EPSILON { - shell.publish((on_change)(new_value)); - - *value = new_value; - } - }; - - match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if layout.bounds().contains(cursor_position) { - 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); - } - state.is_dragging = false; - - return event::Status::Captured; - } - } - Event::Mouse(mouse::Event::CursorMoved { .. }) - | Event::Touch(touch::Event::FingerMoved { .. }) => { - if is_dragging { - change(); - - return event::Status::Captured; - } - } - _ => {} - } - - event::Status::Ignored -} - -/// Draws a [`VerticalSlider`]. -pub fn draw<T, R>( - renderer: &mut R, - layout: Layout<'_>, - cursor_position: Point, - state: &State, - value: T, - range: &RangeInclusive<T>, - style_sheet: &dyn StyleSheet<Style = <R::Theme as StyleSheet>::Style>, - style: &<R::Theme as StyleSheet>::Style, -) where - T: Into<f64> + Copy, - R: crate::Renderer, - R::Theme: StyleSheet, -{ - let bounds = layout.bounds(); - let is_mouse_over = bounds.contains(cursor_position); - - 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 rail_x = bounds.x + (bounds.width / 2.0).round(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - 1.0, - y: bounds.y, - width: 2.0, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - style.rail_colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x + 1.0, - y: bounds.y, - width: 2.0, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(style.rail_colors.1), - ); - - let (handle_width, handle_height, handle_border_radius) = match style - .handle - .shape - { - HandleShape::Circle { radius } => (radius * 2.0, radius * 2.0, radius), - HandleShape::Rectangle { - width, - border_radius, - } => (f32::from(width), bounds.width, border_radius), - }; - - 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 handle_offset = if range_start >= range_end { - 0.0 - } else { - (bounds.height - handle_width) * (value - range_end) - / (range_start - range_end) - }; - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - (handle_height / 2.0), - y: bounds.y + handle_offset.round(), - width: handle_height, - height: handle_width, - }, - border_radius: handle_border_radius.into(), - border_width: style.handle.border_width, - border_color: style.handle.border_color, - }, - style.handle.color, - ); -} - -/// Computes the current [`mouse::Interaction`] of a [`VerticalSlider`]. -pub fn mouse_interaction( - layout: Layout<'_>, - cursor_position: Point, - state: &State, -) -> mouse::Interaction { - let bounds = layout.bounds(); - let is_mouse_over = bounds.contains(cursor_position); - - if state.is_dragging { - mouse::Interaction::Grabbing - } else if is_mouse_over { - mouse::Interaction::Grab - } else { - mouse::Interaction::default() - } -} - -/// The local state of a [`VerticalSlider`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct State { - is_dragging: bool, -} - -impl State { - /// Creates a new [`State`]. - pub fn new() -> State { - State::default() - } -} |