Implement styling for Slider
				
					
				
			This commit is contained in:
		
							parent
							
								
									d0dc7cebf9
								
							
						
					
					
						commit
						b329003c8f
					
				| @ -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 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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 { | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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}; | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| pub mod button; | ||||
| pub mod container; | ||||
| pub mod scrollable; | ||||
| pub mod slider; | ||||
| pub mod text_input; | ||||
|  | ||||
							
								
								
									
										95
									
								
								style/src/slider.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								style/src/slider.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||
|     } | ||||
| } | ||||
| @ -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, | ||||
|         }; | ||||
| 
 | ||||
|         ( | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| pub mod button; | ||||
| pub mod container; | ||||
| pub mod scrollable; | ||||
| pub mod slider; | ||||
| pub mod text_input; | ||||
|  | ||||
							
								
								
									
										16
									
								
								wgpu/src/widget/slider.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								wgpu/src/widget/slider.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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>; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user