diff options
Diffstat (limited to 'pure/src/widget/slider.rs')
-rw-r--r-- | pure/src/widget/slider.rs | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/pure/src/widget/slider.rs b/pure/src/widget/slider.rs new file mode 100644 index 00000000..1107bdc1 --- /dev/null +++ b/pure/src/widget/slider.rs @@ -0,0 +1,243 @@ +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +use crate::widget::tree::{self, Tree}; +use crate::{Element, Widget}; + +use iced_native::event::{self, Event}; +use iced_native::layout; +use iced_native::mouse; +use iced_native::renderer; +use iced_native::widget::slider; +use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell, Size}; + +use std::ops::RangeInclusive; + +pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; + +/// An horizontal bar and a handle that selects a single value from a range of +/// values. +/// +/// A [`Slider`] will try to fill the horizontal space of its container. +/// +/// The [`Slider`] range of numeric values is generic and its step size defaults +/// to 1 unit. +/// +/// # Example +/// ``` +/// # use iced_native::widget::slider::{self, Slider}; +/// # +/// #[derive(Clone)] +/// pub enum Message { +/// SliderChanged(f32), +/// } +/// +/// let state = &mut slider::State::new(); +/// let value = 50.0; +/// +/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged); +/// ``` +/// +///  +#[allow(missing_debug_implementations)] +pub struct Slider<'a, T, Message> { + range: RangeInclusive<T>, + step: T, + value: T, + on_change: Box<dyn Fn(T) -> Message + 'a>, + on_release: Option<Message>, + width: Length, + height: u16, + style_sheet: Box<dyn StyleSheet + 'a>, +} + +impl<'a, T, Message> Slider<'a, T, Message> +where + T: Copy + From<u8> + std::cmp::PartialOrd, + Message: Clone, +{ + /// The default height of a [`Slider`]. + pub const DEFAULT_HEIGHT: u16 = 22; + + /// Creates a new [`Slider`]. + /// + /// It expects: + /// * an inclusive range of possible values + /// * the current value of the [`Slider`] + /// * a function that will be called when the [`Slider`] is dragged. + /// It receives the new value of the [`Slider`] 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() + }; + + Slider { + value, + range, + step: T::from(1), + on_change: Box::new(on_change), + on_release: None, + width: Length::Fill, + height: Self::DEFAULT_HEIGHT, + style_sheet: Default::default(), + } + } + + /// Sets the release message of the [`Slider`]. + /// 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 [`Slider`]. + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Slider`]. + pub fn height(mut self, height: u16) -> Self { + self.height = height; + self + } + + /// Sets the style of the [`Slider`]. + pub fn style( + mut self, + style_sheet: impl Into<Box<dyn StyleSheet + 'a>>, + ) -> Self { + self.style_sheet = style_sheet.into(); + self + } + + /// Sets the step size of the [`Slider`]. + pub fn step(mut self, step: T) -> Self { + self.step = step; + self + } +} + +impl<'a, T, Message, Renderer> Widget<Message, Renderer> + for Slider<'a, T, Message> +where + T: Copy + Into<f64> + num_traits::FromPrimitive, + Message: Clone, + Renderer: iced_native::Renderer, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::<slider::State>() + } + + fn state(&self) -> tree::State { + tree::State::new(slider::State::new()) + } + + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = + limits.width(self.width).height(Length::Units(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 { + slider::update( + event, + layout, + cursor_position, + shell, + tree.state.downcast_mut::<slider::State>(), + &mut self.value, + &self.range, + self.step, + self.on_change.as_ref(), + &self.on_release, + ) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + _style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + _viewport: &Rectangle, + ) { + slider::draw( + renderer, + layout, + cursor_position, + tree.state.downcast_ref::<slider::State>(), + self.value, + &self.range, + self.style_sheet.as_ref(), + ) + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor_position: Point, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + slider::mouse_interaction( + layout, + cursor_position, + tree.state.downcast_ref::<slider::State>(), + ) + } +} + +impl<'a, T, Message, Renderer> From<Slider<'a, T, Message>> + for Element<'a, Message, Renderer> +where + T: 'a + Copy + Into<f64> + num_traits::FromPrimitive, + Message: 'a + Clone, + Renderer: 'a + iced_native::Renderer, +{ + fn from(slider: Slider<'a, T, Message>) -> Element<'a, Message, Renderer> { + Element::new(slider) + } +} |