//! Display an interactive selector of a single value from a range of values. 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::{Appearance, Handle, HandleShape, 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_pure::widget::slider; /// # use iced_native::renderer::Null; /// # /// # type Slider<'a, T, Message> = slider::Slider<'a, T, Message, Null>; /// # /// #[derive(Clone)] /// pub enum Message { /// SliderChanged(f32), /// } /// /// let value = 50.0; /// /// Slider::new(0.0..=100.0, value, Message::SliderChanged); /// ``` /// /// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) #[allow(missing_debug_implementations)] pub struct Slider<'a, T, Message, Renderer> where Renderer: iced_native::Renderer, Renderer::Theme: StyleSheet, { range: RangeInclusive, step: T, value: T, on_change: Box Message + 'a>, on_release: Option, width: Length, height: u16, style: ::Style, } impl<'a, T, Message, Renderer> Slider<'a, T, Message, Renderer> where T: Copy + From + std::cmp::PartialOrd, Message: Clone, Renderer: iced_native::Renderer, Renderer::Theme: StyleSheet, { /// 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(range: RangeInclusive, 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: 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: impl Into<::Style>, ) -> Self { self.style = style.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 for Slider<'a, T, Message, Renderer> where T: Copy + Into + num_traits::FromPrimitive, Message: Clone, Renderer: iced_native::Renderer, Renderer::Theme: StyleSheet, { fn tag(&self) -> tree::Tag { tree::Tag::of::() } 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::(), &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, ) { slider::draw( renderer, layout, cursor_position, tree.state.downcast_ref::(), self.value, &self.range, theme, self.style, ) } 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::(), ) } } impl<'a, T, Message, Renderer> From> for Element<'a, Message, Renderer> where T: 'a + Copy + Into + num_traits::FromPrimitive, Message: 'a + Clone, Renderer: 'a + iced_native::Renderer, Renderer::Theme: StyleSheet, { fn from( slider: Slider<'a, T, Message, Renderer>, ) -> Element<'a, Message, Renderer> { Element::new(slider) } }