diff options
-rw-r--r-- | examples/styling.rs | 75 | ||||
-rw-r--r-- | native/src/renderer/null.rs | 25 | ||||
-rw-r--r-- | native/src/widget/slider.rs | 32 | ||||
-rw-r--r-- | src/native.rs | 11 | ||||
-rw-r--r-- | style/src/lib.rs | 1 | ||||
-rw-r--r-- | style/src/slider.rs | 95 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/slider.rs | 57 | ||||
-rw-r--r-- | wgpu/src/widget.rs | 1 | ||||
-rw-r--r-- | wgpu/src/widget/slider.rs | 16 |
9 files changed, 266 insertions, 47 deletions
diff --git a/examples/styling.rs b/examples/styling.rs index 61c8bcba..b0cdbcf0 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -1,6 +1,6 @@ use iced::{ - button, scrollable, text_input, Button, Column, Container, Element, Length, - Radio, Row, Sandbox, Scrollable, Settings, Text, TextInput, + button, scrollable, slider, text_input, Button, Column, Container, Element, + Length, Radio, Row, Sandbox, Scrollable, Settings, Slider, Text, TextInput, }; pub fn main() { @@ -14,6 +14,8 @@ struct Styling { input: text_input::State, input_value: String, button: button::State, + slider: slider::State, + slider_value: f32, } #[derive(Debug, Clone)] @@ -21,6 +23,7 @@ enum Message { ThemeChanged(style::Theme), InputChanged(String), ButtonPressed, + SliderChanged(f32), } impl Sandbox for Styling { @@ -39,6 +42,7 @@ impl Sandbox for Styling { Message::ThemeChanged(theme) => self.theme = theme, Message::InputChanged(value) => self.input_value = value, Message::ButtonPressed => (), + Message::SliderChanged(value) => self.slider_value = value, } } @@ -70,12 +74,21 @@ impl Sandbox for Styling { .on_press(Message::ButtonPressed) .style(self.theme); + let slider = Slider::new( + &mut self.slider, + 0.0..=100.0, + self.slider_value, + Message::SliderChanged, + ) + .style(self.theme); + let content = Column::new() .spacing(20) .padding(20) .max_width(600) .push(choose_theme) - .push(Row::new().spacing(10).push(text_input).push(button)); + .push(Row::new().spacing(10).push(text_input).push(button)) + .push(slider); let scrollable = Scrollable::new(&mut self.scroll) .style(self.theme) @@ -91,7 +104,7 @@ impl Sandbox for Styling { } mod style { - use iced::{button, container, scrollable, text_input}; + use iced::{button, container, scrollable, slider, text_input}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Theme { @@ -145,6 +158,15 @@ mod style { } } + impl From<Theme> for Box<dyn slider::StyleSheet> { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Slider.into(), + } + } + } + mod light { use iced::{button, Background, Color, Vector}; @@ -175,7 +197,8 @@ mod style { mod dark { use iced::{ - button, container, scrollable, text_input, Background, Color, + button, container, scrollable, slider, text_input, Background, + Color, }; pub struct Container; @@ -291,5 +314,47 @@ mod style { } } } + + pub struct Slider; + + impl slider::StyleSheet for Slider { + fn active(&self) -> slider::Style { + let blue = Color::from_rgb8(0x72, 0x89, 0xDA); + + slider::Style { + rail_colors: (blue, Color { a: 0.1, ..blue }), + handle: slider::Handle { + shape: slider::HandleShape::Circle { radius: 9 }, + color: blue, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + } + } + + fn hovered(&self) -> slider::Style { + let active = self.active(); + + slider::Style { + handle: slider::Handle { + color: Color::from_rgb(0.90, 0.90, 0.90), + ..active.handle + }, + ..active + } + } + + fn dragging(&self) -> slider::Style { + let active = self.active(); + + slider::Style { + handle: slider::Handle { + color: Color::from_rgb(0.85, 0.85, 0.85), + ..active.handle + }, + ..active + } + } + } } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 0184ac00..72f06a87 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,7 +1,7 @@ use crate::{ - button, checkbox, column, radio, row, scrollable, text, text_input, Color, - Element, Font, HorizontalAlignment, Layout, Point, Rectangle, Renderer, - Size, VerticalAlignment, + button, checkbox, column, radio, row, scrollable, slider, text, text_input, + Color, Element, Font, HorizontalAlignment, Layout, Point, Rectangle, + Renderer, Size, VerticalAlignment, }; /// A renderer that does nothing. @@ -180,3 +180,22 @@ impl checkbox::Renderer for Null { ) { } } + +impl slider::Renderer for Null { + type Style = (); + + fn height(&self) -> u32 { + 30 + } + + fn draw( + &mut self, + _bounds: Rectangle, + _cursor_position: Point, + _range: std::ops::RangeInclusive<f32>, + _value: f32, + _is_dragging: bool, + _style_sheet: &Self::Style, + ) -> Self::Output { + } +} diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index ea66a347..c35a933e 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -21,8 +21,9 @@ use std::{hash::Hash, ops::RangeInclusive}; /// /// # Example /// ``` -/// # use iced_native::{slider, Slider}; +/// # use iced_native::{slider, renderer::Null}; /// # +/// # pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Null>; /// pub enum Message { /// SliderChanged(f32), /// } @@ -35,15 +36,16 @@ use std::{hash::Hash, ops::RangeInclusive}; /// ///  #[allow(missing_debug_implementations)] -pub struct Slider<'a, Message> { +pub struct Slider<'a, Message, Renderer: self::Renderer> { state: &'a mut State, range: RangeInclusive<f32>, value: f32, on_change: Box<dyn Fn(f32) -> Message>, width: Length, + style: Renderer::Style, } -impl<'a, Message> Slider<'a, Message> { +impl<'a, Message, Renderer: self::Renderer> Slider<'a, Message, Renderer> { /// Creates a new [`Slider`]. /// /// It expects: @@ -71,6 +73,7 @@ impl<'a, Message> Slider<'a, Message> { range, on_change: Box::new(on_change), width: Length::Fill, + style: Renderer::Style::default(), } } @@ -81,6 +84,14 @@ impl<'a, Message> Slider<'a, Message> { self.width = width; self } + + /// Sets the style of the [`Slider`]. + /// + /// [`Slider`]: struct.Slider.html + pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self { + self.style = style.into(); + self + } } /// The local state of a [`Slider`]. @@ -100,7 +111,8 @@ impl State { } } -impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message> +impl<'a, Message, Renderer> Widget<Message, Renderer> + for Slider<'a, Message, Renderer> where Renderer: self::Renderer, { @@ -188,6 +200,7 @@ where self.range.clone(), self.value, self.state.is_dragging, + &self.style, ) } @@ -204,6 +217,8 @@ where /// [`Slider`]: struct.Slider.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + type Style: Default; + /// Returns the height of the [`Slider`]. /// /// [`Slider`]: struct.Slider.html @@ -228,16 +243,19 @@ pub trait Renderer: crate::Renderer { range: RangeInclusive<f32>, value: f32, is_dragging: bool, + style: &Self::Style, ) -> Self::Output; } -impl<'a, Message, Renderer> From<Slider<'a, Message>> +impl<'a, Message, Renderer> From<Slider<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: 'static + self::Renderer, Message: 'static, { - fn from(slider: Slider<'a, Message>) -> Element<'a, Message, Renderer> { + fn from( + slider: Slider<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { Element::new(slider) } } diff --git a/src/native.rs b/src/native.rs index 1061a730..c2584fdd 100644 --- a/src/native.rs +++ b/src/native.rs @@ -24,17 +24,6 @@ pub mod widget { //! [`text_input::State`]: text_input/struct.State.html pub use iced_wgpu::widget::*; - pub mod slider { - //! Display an interactive selector of a single value from a range of - //! values. - //! - //! A [`Slider`] has some local [`State`]. - //! - //! [`Slider`]: struct.Slider.html - //! [`State`]: struct.State.html - pub use iced_winit::slider::{Slider, State}; - } - pub mod image { //! Display images in your user interface. pub use iced_winit::image::{Handle, Image}; diff --git a/style/src/lib.rs b/style/src/lib.rs index fb90b2b5..4b8d339e 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,4 +1,5 @@ pub mod button; pub mod container; pub mod scrollable; +pub mod slider; pub mod text_input; diff --git a/style/src/slider.rs b/style/src/slider.rs new file mode 100644 index 00000000..776e180c --- /dev/null +++ b/style/src/slider.rs @@ -0,0 +1,95 @@ +//! Display an interactive selector of a single value from a range of values. +use iced_core::Color; + +/// The appearance of a slider. +#[derive(Debug, Clone, Copy)] +pub struct Style { + pub rail_colors: (Color, Color), + pub handle: Handle, +} + +/// The appearance of the handle of a slider. +#[derive(Debug, Clone, Copy)] +pub struct Handle { + pub shape: HandleShape, + pub color: Color, + pub border_width: u16, + pub border_color: Color, +} + +/// The shape of the handle of a slider. +#[derive(Debug, Clone, Copy)] +pub enum HandleShape { + Circle { radius: u16 }, + Rectangle { width: u16, border_radius: u16 }, +} + +/// A set of rules that dictate the style of a slider. +pub trait StyleSheet { + /// Produces the style of an active slider. + fn active(&self) -> Style; + + /// Produces the style of an hovered slider. + fn hovered(&self) -> Style; + + /// Produces the style of a slider that is being dragged. + fn dragging(&self) -> Style; +} + +struct Default; + +impl StyleSheet for Default { + fn active(&self) -> Style { + Style { + rail_colors: ([0.6, 0.6, 0.6, 0.5].into(), Color::WHITE), + handle: Handle { + shape: HandleShape::Rectangle { + width: 8, + border_radius: 4, + }, + color: Color::from_rgb(0.95, 0.95, 0.95), + border_color: Color::from_rgb(0.6, 0.6, 0.6), + border_width: 1, + }, + } + } + + fn hovered(&self) -> Style { + let active = self.active(); + + Style { + handle: Handle { + color: Color::from_rgb(0.90, 0.90, 0.90), + ..active.handle + }, + ..active + } + } + + fn dragging(&self) -> Style { + let active = self.active(); + + Style { + handle: Handle { + color: Color::from_rgb(0.85, 0.85, 0.85), + ..active.handle + }, + ..active + } + } +} + +impl std::default::Default for Box<dyn StyleSheet> { + fn default() -> Self { + Box::new(Default) + } +} + +impl<T> From<T> for Box<dyn StyleSheet> +where + T: 'static + StyleSheet, +{ + fn from(style: T) -> Self { + Box::new(style) + } +} diff --git a/wgpu/src/renderer/widget/slider.rs b/wgpu/src/renderer/widget/slider.rs index 386decb5..c8ebd0da 100644 --- a/wgpu/src/renderer/widget/slider.rs +++ b/wgpu/src/renderer/widget/slider.rs @@ -1,10 +1,14 @@ -use crate::{Primitive, Renderer}; +use crate::{ + slider::{HandleShape, StyleSheet}, + Primitive, Renderer, +}; use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle}; -const HANDLE_WIDTH: f32 = 8.0; const HANDLE_HEIGHT: f32 = 22.0; impl slider::Renderer for Renderer { + type Style = Box<dyn StyleSheet>; + fn height(&self) -> u32 { 30 } @@ -16,9 +20,18 @@ impl slider::Renderer for Renderer { range: std::ops::RangeInclusive<f32>, value: f32, is_dragging: bool, + style_sheet: &Self::Style, ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); + let style = if is_dragging { + style_sheet.dragging() + } else if is_mouse_over { + style_sheet.hovered() + } else { + style_sheet.active() + }; + let rail_y = bounds.y + (bounds.height / 2.0).round(); let (rail_top, rail_bottom) = ( @@ -29,7 +42,7 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Background::Color([0.6, 0.6, 0.6, 0.5].into()), + background: Background::Color(style.rail_colors.0), border_radius: 0, border_width: 0, border_color: Color::TRANSPARENT, @@ -41,7 +54,7 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Background::Color(Color::WHITE), + background: Background::Color(style.rail_colors.1), border_radius: 0, border_width: 0, border_color: Color::TRANSPARENT, @@ -50,29 +63,31 @@ impl slider::Renderer for Renderer { let (range_start, range_end) = range.into_inner(); - let handle_offset = (bounds.width - HANDLE_WIDTH) + let (handle_width, handle_height, handle_border_radius) = + match style.handle.shape { + HandleShape::Circle { radius } => { + (f32::from(radius * 2), f32::from(radius * 2), radius) + } + HandleShape::Rectangle { + width, + border_radius, + } => (f32::from(width), HANDLE_HEIGHT, border_radius), + }; + + let handle_offset = (bounds.width - handle_width) * ((value - range_start) / (range_end - range_start).max(1.0)); let handle = Primitive::Quad { bounds: Rectangle { x: bounds.x + handle_offset.round(), - y: rail_y - HANDLE_HEIGHT / 2.0, - width: HANDLE_WIDTH, - height: HANDLE_HEIGHT, + y: rail_y - handle_height / 2.0, + width: handle_width, + height: handle_height, }, - background: Background::Color( - if is_dragging { - [0.85, 0.85, 0.85] - } else if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 4, - border_width: 1, - border_color: Color::from_rgb(0.6, 0.6, 0.6), + background: Background::Color(style.handle.color), + border_radius: handle_border_radius, + border_width: style.handle.border_width, + border_color: style.handle.border_color, }; ( diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index fb90b2b5..4b8d339e 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,4 +1,5 @@ pub mod button; pub mod container; pub mod scrollable; +pub mod slider; pub mod text_input; diff --git a/wgpu/src/widget/slider.rs b/wgpu/src/widget/slider.rs new file mode 100644 index 00000000..4e47978f --- /dev/null +++ b/wgpu/src/widget/slider.rs @@ -0,0 +1,16 @@ +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +//! +//! [`Slider`]: struct.Slider.html +//! [`State`]: struct.State.html +use crate::Renderer; + +pub use iced_native::slider::State; +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. +/// +/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`. +pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Renderer>; |