summaryrefslogtreecommitdiffstats
path: root/pure/src
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-02-13 17:20:10 +0700
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-02-13 17:20:10 +0700
commit3f1a45ca47dc086a5c4e45867d3f9c63a4e7ba19 (patch)
tree81a5f754fbe4a2f9795f3f4098df734e8e59991a /pure/src
parent45455be45000c0d41d18eced1b62eab049c5e9c0 (diff)
downloadiced-3f1a45ca47dc086a5c4e45867d3f9c63a4e7ba19.tar.gz
iced-3f1a45ca47dc086a5c4e45867d3f9c63a4e7ba19.tar.bz2
iced-3f1a45ca47dc086a5c4e45867d3f9c63a4e7ba19.zip
Implement `Slider` in `iced_pure`
Diffstat (limited to 'pure/src')
-rw-r--r--pure/src/widget.rs19
-rw-r--r--pure/src/widget/slider.rs253
2 files changed, 270 insertions, 2 deletions
diff --git a/pure/src/widget.rs b/pure/src/widget.rs
index 93298c61..009741a8 100644
--- a/pure/src/widget.rs
+++ b/pure/src/widget.rs
@@ -7,6 +7,7 @@ mod container;
mod element;
mod row;
mod scrollable;
+mod slider;
mod text;
mod text_input;
mod tree;
@@ -19,6 +20,7 @@ pub use element::Element;
pub use image::Image;
pub use row::Row;
pub use scrollable::Scrollable;
+pub use slider::Slider;
pub use text::Text;
pub use text_input::TextInput;
pub use tree::Tree;
@@ -36,8 +38,6 @@ pub trait Widget<Message, Renderer> {
fn state(&self) -> Box<dyn Any>;
- fn diff(&self, tree: &mut Tree);
-
fn children_state(&self) -> Vec<Tree>;
fn width(&self) -> Length;
@@ -62,6 +62,8 @@ pub trait Widget<Message, Renderer> {
viewport: &Rectangle,
);
+ fn diff(&self, _tree: &mut Tree) {}
+
fn mouse_interaction(
&self,
_state: &Tree,
@@ -149,6 +151,19 @@ where
TextInput::new(placeholder, value, on_change)
}
+pub fn slider<'a, Message, Renderer, T>(
+ range: std::ops::RangeInclusive<T>,
+ value: T,
+ on_change: impl Fn(T) -> Message + 'a,
+) -> Slider<'a, T, Message>
+where
+ Message: Clone,
+ Renderer: iced_native::Renderer,
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+{
+ Slider::new(range, value, on_change)
+}
+
pub fn image<Handle>(handle: Handle) -> Image<Handle> {
Image::new(handle)
}
diff --git a/pure/src/widget/slider.rs b/pure/src/widget/slider.rs
new file mode 100644
index 00000000..f659c2ed
--- /dev/null
+++ b/pure/src/widget/slider.rs
@@ -0,0 +1,253 @@
+//! Display an interactive selector of a single value from a range of values.
+//!
+//! A [`Slider`] has some local [`State`].
+use crate::{Element, Tree, 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, Hasher, Layout, Length, Point, Rectangle, Shell, Size,
+};
+
+use std::any::{self, Any};
+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);
+/// ```
+///
+/// ![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> {
+ 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) -> any::TypeId {
+ any::TypeId::of::<slider::State>()
+ }
+
+ fn state(&self) -> Box<dyn Any> {
+ Box::new(slider::State::new())
+ }
+
+ fn children_state(&self) -> Vec<Tree> {
+ Vec::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>(),
+ )
+ }
+
+ fn hash_layout(&self, state: &mut Hasher) {
+ slider::hash_layout(state, self.width)
+ }
+}
+
+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)
+ }
+}