From ce309db37b8eb9860ae1f1be1710fb39e7a9edea Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 Mar 2024 03:57:03 +0100 Subject: Try new approach to theming for `Slider` --- style/src/lib.rs | 2 +- style/src/slider.rs | 29 ++- style/src/theme.rs | 108 +++----- widget/src/slider.rs | 548 ++++++++++++++++++----------------------- widget/src/vertical_slider.rs | 558 +++++++++++++++++++----------------------- 5 files changed, 532 insertions(+), 713 deletions(-) diff --git a/style/src/lib.rs b/style/src/lib.rs index 3c2865eb..17ba09c4 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -10,7 +10,7 @@ #![forbid(unsafe_code, rust_2018_idioms)] #![deny( unused_results, - missing_docs, + // missing_docs, unused_results, rustdoc::broken_intra_doc_links )] diff --git a/style/src/slider.rs b/style/src/slider.rs index bf1c7329..0c19e47d 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -1,6 +1,6 @@ //! Change the apperance of a slider. use crate::core::border; -use crate::core::Color; +use crate::core::{Color, Pixels}; /// The appearance of a slider. #[derive(Debug, Clone, Copy)] @@ -11,6 +11,17 @@ pub struct Appearance { pub handle: Handle, } +impl Appearance { + /// Changes the [`HandleShape`] of the [`Appearance`] to a circle + /// with the given radius. + pub fn with_circular_handle(mut self, radius: impl Into) -> Self { + self.handle.shape = HandleShape::Circle { + radius: radius.into().0, + }; + self + } +} + /// The appearance of a slider rail #[derive(Debug, Clone, Copy)] pub struct Rail { @@ -54,15 +65,11 @@ pub enum HandleShape { /// A set of rules that dictate the style of a slider. pub trait StyleSheet { - /// The supported style of the [`StyleSheet`]. - type Style: Default; - - /// Produces the style of an active slider. - fn active(&self, style: &Self::Style) -> Appearance; - - /// Produces the style of an hovered slider. - fn hovered(&self, style: &Self::Style) -> Appearance; + fn default() -> fn(&Self, Status) -> Appearance; +} - /// Produces the style of a slider that is being dragged. - fn dragging(&self, style: &Self::Style) -> Appearance; +pub enum Status { + Active, + Hovered, + Dragging, } diff --git a/style/src/theme.rs b/style/src/theme.rs index 0b56e101..656d6bf9 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -608,88 +608,40 @@ impl container::Appearance> container::StyleSheet for T { } } -/// The style of a slider. -#[derive(Default)] -pub enum Slider { - /// The default style. - #[default] - Default, - /// A custom style. - Custom(Box>), -} - impl slider::StyleSheet for Theme { - type Style = Slider; - - fn active(&self, style: &Self::Style) -> slider::Appearance { - match style { - Slider::Default => { - let palette = self.extended_palette(); - - let handle = slider::Handle { - shape: slider::HandleShape::Rectangle { - width: 8, - border_radius: 4.0.into(), - }, - color: Color::WHITE, - border_color: Color::WHITE, - border_width: 1.0, - }; - - slider::Appearance { - rail: slider::Rail { - colors: ( - palette.primary.base.color, - palette.secondary.base.color, - ), - width: 4.0, - border_radius: 2.0.into(), - }, - handle: slider::Handle { - color: palette.background.base.color, - border_color: palette.primary.base.color, - ..handle - }, - } - } - Slider::Custom(custom) => custom.active(self), - } - } - - fn hovered(&self, style: &Self::Style) -> slider::Appearance { - match style { - Slider::Default => { - let active = self.active(style); - let palette = self.extended_palette(); - - slider::Appearance { - handle: slider::Handle { - color: palette.primary.weak.color, - ..active.handle - }, - ..active - } - } - Slider::Custom(custom) => custom.hovered(self), - } + fn default() -> fn(&Self, slider::Status) -> slider::Appearance { + slider } +} - fn dragging(&self, style: &Self::Style) -> slider::Appearance { - match style { - Slider::Default => { - let active = self.active(style); - let palette = self.extended_palette(); +pub fn slider(theme: &Theme, status: slider::Status) -> slider::Appearance { + let palette = theme.extended_palette(); - slider::Appearance { - handle: slider::Handle { - color: palette.primary.base.color, - ..active.handle - }, - ..active - } - } - Slider::Custom(custom) => custom.dragging(self), - } + let handle = slider::Handle { + shape: slider::HandleShape::Rectangle { + width: 8, + border_radius: 4.0.into(), + }, + color: Color::WHITE, + border_color: Color::WHITE, + border_width: 1.0, + }; + + slider::Appearance { + rail: slider::Rail { + colors: (palette.primary.base.color, palette.secondary.base.color), + width: 4.0, + border_radius: 2.0.into(), + }, + handle: slider::Handle { + color: match status { + slider::Status::Active => palette.background.base.color, + slider::Status::Hovered => palette.primary.weak.color, + slider::Status::Dragging => palette.primary.base.color, + }, + border_color: palette.primary.base.color, + ..handle + }, } } diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 65bc1772..ce02a0a6 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -17,7 +17,7 @@ use crate::core::{ use std::ops::RangeInclusive; pub use iced_style::slider::{ - Appearance, Handle, HandleShape, Rail, StyleSheet, + Appearance, Handle, HandleShape, Rail, Status, StyleSheet, }; /// An horizontal bar and a handle that selects a single value from a range of @@ -58,7 +58,7 @@ where on_release: Option, width: Length, height: f32, - style: Theme::Style, + style: fn(&Theme, Status) -> Appearance, } impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme> @@ -104,7 +104,7 @@ where on_release: None, width: Length::Fill, height: Self::DEFAULT_HEIGHT, - style: Default::default(), + style: Theme::default(), } } @@ -140,8 +140,8 @@ where } /// Sets the style of the [`Slider`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { + self.style = style; self } @@ -173,7 +173,7 @@ where } fn state(&self) -> tree::State { - tree::State::new(State::new()) + tree::State::new(State::default()) } fn size(&self) -> Size { @@ -203,355 +203,283 @@ where shell: &mut Shell<'_, Message>, _viewport: &Rectangle, ) -> event::Status { - update( - event, - layout, - cursor, - shell, - tree.state.downcast_mut::(), - &mut self.value, - self.default, - &self.range, - self.step, - self.shift_step, - self.on_change.as_ref(), - &self.on_release, - ) - } + let state = tree.state.downcast_mut::(); - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - _viewport: &Rectangle, - ) { - draw( - renderer, - layout, - cursor, - tree.state.downcast_ref::(), - self.value, - &self.range, - theme, - &self.style, - ); - } + let is_dragging = state.is_dragging; + let current_value = self.value; - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor: mouse::Cursor, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - mouse_interaction(layout, cursor, tree.state.downcast_ref::()) - } -} + let locate = |cursor_position: Point| -> Option { + let bounds = layout.bounds(); + let new_value = if cursor_position.x <= bounds.x { + Some(*self.range.start()) + } else if cursor_position.x >= bounds.x + bounds.width { + Some(*self.range.end()) + } else { + let step = if state.keyboard_modifiers.shift() { + self.shift_step.unwrap_or(self.step) + } else { + self.step + } + .into(); -impl<'a, T, Message, Theme, Renderer> From> - for Element<'a, Message, Theme, Renderer> -where - T: Copy + Into + num_traits::FromPrimitive + 'a, - Message: Clone + 'a, - Theme: StyleSheet + 'a, - Renderer: crate::core::Renderer + 'a, -{ - fn from( - slider: Slider<'a, T, Message, Theme>, - ) -> Element<'a, Message, Theme, Renderer> { - Element::new(slider) - } -} + let start = (*self.range.start()).into(); + let end = (*self.range.end()).into(); -/// Processes an [`Event`] and updates the [`State`] of a [`Slider`] -/// accordingly. -pub fn update( - event: Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - shell: &mut Shell<'_, Message>, - state: &mut State, - value: &mut T, - default: Option, - range: &RangeInclusive, - step: T, - shift_step: Option, - on_change: &dyn Fn(T) -> Message, - on_release: &Option, -) -> event::Status -where - T: Copy + Into + num_traits::FromPrimitive, - Message: Clone, -{ - let is_dragging = state.is_dragging; - let current_value = *value; + let percent = f64::from(cursor_position.x - bounds.x) + / f64::from(bounds.width); - let locate = |cursor_position: Point| -> Option { - let bounds = layout.bounds(); - let new_value = if cursor_position.x <= bounds.x { - Some(*range.start()) - } else if cursor_position.x >= bounds.x + bounds.width { - Some(*range.end()) - } else { + let steps = (percent * (end - start) / step).round(); + let value = steps * step + start; + + T::from_f64(value) + }; + + new_value + }; + + let increment = |value: T| -> Option { let step = if state.keyboard_modifiers.shift() { - shift_step.unwrap_or(step) + self.shift_step.unwrap_or(self.step) } else { - step + self.step } .into(); - let start = (*range.start()).into(); - let end = (*range.end()).into(); + let steps = (value.into() / step).round(); + let new_value = step * (steps + 1.0); - let percent = f64::from(cursor_position.x - bounds.x) - / f64::from(bounds.width); - - let steps = (percent * (end - start) / step).round(); - let value = steps * step + start; + if new_value > (*self.range.end()).into() { + return Some(*self.range.end()); + } - T::from_f64(value) + T::from_f64(new_value) }; - new_value - }; - - let increment = |value: T| -> Option { - let step = if state.keyboard_modifiers.shift() { - shift_step.unwrap_or(step) - } else { - step - } - .into(); - - let steps = (value.into() / step).round(); - let new_value = step * (steps + 1.0); - - if new_value > (*range.end()).into() { - return Some(*range.end()); - } + let decrement = |value: T| -> Option { + let step = if state.keyboard_modifiers.shift() { + self.shift_step.unwrap_or(self.step) + } else { + self.step + } + .into(); - T::from_f64(new_value) - }; + let steps = (value.into() / step).round(); + let new_value = step * (steps - 1.0); - let decrement = |value: T| -> Option { - let step = if state.keyboard_modifiers.shift() { - shift_step.unwrap_or(step) - } else { - step - } - .into(); + if new_value < (*self.range.start()).into() { + return Some(*self.range.start()); + } - let steps = (value.into() / step).round(); - let new_value = step * (steps - 1.0); + T::from_f64(new_value) + }; - if new_value < (*range.start()).into() { - return Some(*range.start()); - } + let change = |new_value: T| { + if (self.value.into() - new_value.into()).abs() > f64::EPSILON { + shell.publish((self.on_change)(new_value)); - T::from_f64(new_value) - }; + self.value = new_value; + } + }; - let change = |new_value: T| { - if ((*value).into() - new_value.into()).abs() > f64::EPSILON { - shell.publish((on_change)(new_value)); + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if let Some(cursor_position) = + cursor.position_over(layout.bounds()) + { + if state.keyboard_modifiers.command() { + let _ = self.default.map(change); + state.is_dragging = false; + } else { + let _ = locate(cursor_position).map(change); + state.is_dragging = true; + } - *value = new_value; - } - }; - - match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if let Some(cursor_position) = cursor.position_over(layout.bounds()) - { - if state.keyboard_modifiers.command() { - let _ = default.map(change); - state.is_dragging = false; - } else { - let _ = locate(cursor_position).map(change); - state.is_dragging = true; + return event::Status::Captured; } - - return event::Status::Captured; } - } - Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerLifted { .. }) - | Event::Touch(touch::Event::FingerLost { .. }) => { - if is_dragging { - if let Some(on_release) = on_release.clone() { - shell.publish(on_release); - } - state.is_dragging = false; + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) + | Event::Touch(touch::Event::FingerLost { .. }) => { + if is_dragging { + if let Some(on_release) = self.on_release.clone() { + shell.publish(on_release); + } + state.is_dragging = false; - return event::Status::Captured; + return event::Status::Captured; + } } - } - Event::Mouse(mouse::Event::CursorMoved { .. }) - | Event::Touch(touch::Event::FingerMoved { .. }) => { - if is_dragging { - let _ = cursor.position().and_then(locate).map(change); + Event::Mouse(mouse::Event::CursorMoved { .. }) + | Event::Touch(touch::Event::FingerMoved { .. }) => { + if is_dragging { + let _ = cursor.position().and_then(locate).map(change); - return event::Status::Captured; + return event::Status::Captured; + } } - } - Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { - if cursor.position_over(layout.bounds()).is_some() { - match key { - Key::Named(key::Named::ArrowUp) => { - let _ = increment(current_value).map(change); + Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { + if cursor.position_over(layout.bounds()).is_some() { + match key { + Key::Named(key::Named::ArrowUp) => { + let _ = increment(current_value).map(change); + } + Key::Named(key::Named::ArrowDown) => { + let _ = decrement(current_value).map(change); + } + _ => (), } - Key::Named(key::Named::ArrowDown) => { - let _ = decrement(current_value).map(change); - } - _ => (), - } - return event::Status::Captured; + return event::Status::Captured; + } } + Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { + state.keyboard_modifiers = modifiers; + } + _ => {} } - Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { - state.keyboard_modifiers = modifiers; - } - _ => {} + + event::Status::Ignored } - event::Status::Ignored -} + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + _style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + ) { + let state = tree.state.downcast_ref::(); + let bounds = layout.bounds(); + let is_mouse_over = cursor.is_over(bounds); -/// Draws a [`Slider`]. -pub fn draw( - renderer: &mut Renderer, - layout: Layout<'_>, - cursor: mouse::Cursor, - state: &State, - value: T, - range: &RangeInclusive, - theme: &Theme, - style: &Theme::Style, -) where - T: Into + Copy, - Theme: StyleSheet, - Renderer: crate::core::Renderer, -{ - let bounds = layout.bounds(); - let is_mouse_over = cursor.is_over(bounds); - - let style = if state.is_dragging { - theme.dragging(style) - } else if is_mouse_over { - theme.hovered(style) - } else { - theme.active(style) - }; - - let (handle_width, handle_height, handle_border_radius) = - match style.handle.shape { - HandleShape::Circle { radius } => { - (radius * 2.0, radius * 2.0, radius.into()) - } - HandleShape::Rectangle { - width, - border_radius, - } => (f32::from(width), bounds.height, border_radius), + let style = (self.style)( + theme, + if state.is_dragging { + Status::Dragging + } else if is_mouse_over { + Status::Hovered + } else { + Status::Active + }, + ); + + let (handle_width, handle_height, handle_border_radius) = + match style.handle.shape { + HandleShape::Circle { radius } => { + (radius * 2.0, radius * 2.0, radius.into()) + } + HandleShape::Rectangle { + width, + border_radius, + } => (f32::from(width), bounds.height, border_radius), + }; + + let value = self.value.into() as f32; + let (range_start, range_end) = { + let (start, end) = self.range.clone().into_inner(); + + (start.into() as f32, end.into() as f32) }; - let value = value.into() as f32; - let (range_start, range_end) = { - let (start, end) = range.clone().into_inner(); - - (start.into() as f32, end.into() as f32) - }; - - let offset = if range_start >= range_end { - 0.0 - } else { - (bounds.width - handle_width) * (value - range_start) - / (range_end - range_start) - }; - - let rail_y = bounds.y + bounds.height / 2.0; - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y - style.rail.width / 2.0, - width: offset + handle_width / 2.0, - height: style.rail.width, - }, - border: Border::with_radius(style.rail.border_radius), - ..renderer::Quad::default() - }, - style.rail.colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x + offset + handle_width / 2.0, - y: rail_y - style.rail.width / 2.0, - width: bounds.width - offset - handle_width / 2.0, - height: style.rail.width, + let offset = if range_start >= range_end { + 0.0 + } else { + (bounds.width - handle_width) * (value - range_start) + / (range_end - range_start) + }; + + let rail_y = bounds.y + bounds.height / 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: rail_y - style.rail.width / 2.0, + width: offset + handle_width / 2.0, + height: style.rail.width, + }, + border: Border::with_radius(style.rail.border_radius), + ..renderer::Quad::default() }, - border: Border::with_radius(style.rail.border_radius), - ..renderer::Quad::default() - }, - style.rail.colors.1, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x + offset, - y: rail_y - handle_height / 2.0, - width: handle_width, - height: handle_height, + style.rail.colors.0, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + offset + handle_width / 2.0, + y: rail_y - style.rail.width / 2.0, + width: bounds.width - offset - handle_width / 2.0, + height: style.rail.width, + }, + border: Border::with_radius(style.rail.border_radius), + ..renderer::Quad::default() }, - border: Border { - radius: handle_border_radius, - width: style.handle.border_width, - color: style.handle.border_color, + style.rail.colors.1, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + offset, + y: rail_y - handle_height / 2.0, + width: handle_width, + height: handle_height, + }, + border: Border { + radius: handle_border_radius, + width: style.handle.border_width, + color: style.handle.border_color, + }, + ..renderer::Quad::default() }, - ..renderer::Quad::default() - }, - style.handle.color, - ); + style.handle.color, + ); + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + let state = tree.state.downcast_ref::(); + let bounds = layout.bounds(); + let is_mouse_over = cursor.is_over(bounds); + + if state.is_dragging { + mouse::Interaction::Grabbing + } else if is_mouse_over { + mouse::Interaction::Grab + } else { + mouse::Interaction::default() + } + } } -/// Computes the current [`mouse::Interaction`] of a [`Slider`]. -pub fn mouse_interaction( - layout: Layout<'_>, - cursor: mouse::Cursor, - state: &State, -) -> mouse::Interaction { - let bounds = layout.bounds(); - let is_mouse_over = cursor.is_over(bounds); - - if state.is_dragging { - mouse::Interaction::Grabbing - } else if is_mouse_over { - mouse::Interaction::Grab - } else { - mouse::Interaction::default() +impl<'a, T, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + T: Copy + Into + num_traits::FromPrimitive + 'a, + Message: Clone + 'a, + Theme: StyleSheet + 'a, + Renderer: crate::core::Renderer + 'a, +{ + fn from( + slider: Slider<'a, T, Message, Theme>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(slider) } } -/// The local state of a [`Slider`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct State { +struct State { is_dragging: bool, keyboard_modifiers: keyboard::Modifiers, } - -impl State { - /// Creates a new [`State`]. - pub fn new() -> State { - State::default() - } -} diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index 8f7c88da..b6903001 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -3,7 +3,9 @@ //! A [`VerticalSlider`] has some local [`State`]. use std::ops::RangeInclusive; -pub use crate::style::slider::{Appearance, Handle, HandleShape, StyleSheet}; +pub use crate::style::slider::{ + Appearance, Handle, HandleShape, Status, StyleSheet, +}; use crate::core; use crate::core::event::{self, Event}; @@ -55,7 +57,7 @@ where on_release: Option, width: f32, height: Length, - style: Theme::Style, + style: fn(&Theme, Status) -> Appearance, } impl<'a, T, Message, Theme> VerticalSlider<'a, T, Message, Theme> @@ -101,7 +103,7 @@ where on_release: None, width: Self::DEFAULT_WIDTH, height: Length::Fill, - style: Default::default(), + style: Theme::default(), } } @@ -137,7 +139,10 @@ where } /// Sets the style of the [`VerticalSlider`]. - pub fn style(mut self, style: impl Into) -> Self { + pub fn style( + mut self, + style: impl Into Appearance>, + ) -> Self { self.style = style.into(); self } @@ -170,7 +175,7 @@ where } fn state(&self) -> tree::State { - tree::State::new(State::new()) + tree::State::new(State::default()) } fn size(&self) -> Size { @@ -200,360 +205,287 @@ where shell: &mut Shell<'_, Message>, _viewport: &Rectangle, ) -> event::Status { - update( - event, - layout, - cursor, - shell, - tree.state.downcast_mut::(), - &mut self.value, - self.default, - &self.range, - self.step, - self.shift_step, - self.on_change.as_ref(), - &self.on_release, - ) - } + let state = tree.state.downcast_mut::(); + let is_dragging = state.is_dragging; + let current_value = self.value; - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - _viewport: &Rectangle, - ) { - draw( - renderer, - layout, - cursor, - tree.state.downcast_ref::(), - self.value, - &self.range, - theme, - &self.style, - ); - } + let locate = |cursor_position: Point| -> Option { + let bounds = layout.bounds(); - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor: mouse::Cursor, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - mouse_interaction(layout, cursor, tree.state.downcast_ref::()) - } -} + let new_value = if cursor_position.y >= bounds.y + bounds.height { + Some(*self.range.start()) + } else if cursor_position.y <= bounds.y { + Some(*self.range.end()) + } else { + let step = if state.keyboard_modifiers.shift() { + self.shift_step.unwrap_or(self.step) + } else { + self.step + } + .into(); -impl<'a, T, Message, Theme, Renderer> - From> - for Element<'a, Message, Theme, Renderer> -where - T: Copy + Into + num_traits::FromPrimitive + 'a, - Message: Clone + 'a, - Theme: StyleSheet + 'a, - Renderer: core::Renderer + 'a, -{ - fn from( - slider: VerticalSlider<'a, T, Message, Theme>, - ) -> Element<'a, Message, Theme, Renderer> { - Element::new(slider) - } -} + let start = (*self.range.start()).into(); + let end = (*self.range.end()).into(); -/// Processes an [`Event`] and updates the [`State`] of a [`VerticalSlider`] -/// accordingly. -pub fn update( - event: Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - shell: &mut Shell<'_, Message>, - state: &mut State, - value: &mut T, - default: Option, - range: &RangeInclusive, - step: T, - shift_step: Option, - on_change: &dyn Fn(T) -> Message, - on_release: &Option, -) -> event::Status -where - T: Copy + Into + num_traits::FromPrimitive, - Message: Clone, -{ - let is_dragging = state.is_dragging; - let current_value = *value; + let percent = 1.0 + - f64::from(cursor_position.y - bounds.y) + / f64::from(bounds.height); - let locate = |cursor_position: Point| -> Option { - let bounds = layout.bounds(); + let steps = (percent * (end - start) / step).round(); + let value = steps * step + start; - let new_value = if cursor_position.y >= bounds.y + bounds.height { - Some(*range.start()) - } else if cursor_position.y <= bounds.y { - Some(*range.end()) - } else { + T::from_f64(value) + }; + + new_value + }; + + let increment = |value: T| -> Option { let step = if state.keyboard_modifiers.shift() { - shift_step.unwrap_or(step) + self.shift_step.unwrap_or(self.step) } else { - step + self.step } .into(); - let start = (*range.start()).into(); - let end = (*range.end()).into(); + let steps = (value.into() / step).round(); + let new_value = step * (steps + 1.0); - let percent = 1.0 - - f64::from(cursor_position.y - bounds.y) - / f64::from(bounds.height); - - let steps = (percent * (end - start) / step).round(); - let value = steps * step + start; + if new_value > (*self.range.end()).into() { + return Some(*self.range.end()); + } - T::from_f64(value) + T::from_f64(new_value) }; - new_value - }; - - let increment = |value: T| -> Option { - let step = if state.keyboard_modifiers.shift() { - shift_step.unwrap_or(step) - } else { - step - } - .into(); - - let steps = (value.into() / step).round(); - let new_value = step * (steps + 1.0); - - if new_value > (*range.end()).into() { - return Some(*range.end()); - } - - T::from_f64(new_value) - }; + let decrement = |value: T| -> Option { + let step = if state.keyboard_modifiers.shift() { + self.shift_step.unwrap_or(self.step) + } else { + self.step + } + .into(); - let decrement = |value: T| -> Option { - let step = if state.keyboard_modifiers.shift() { - shift_step.unwrap_or(step) - } else { - step - } - .into(); + let steps = (value.into() / step).round(); + let new_value = step * (steps - 1.0); - let steps = (value.into() / step).round(); - let new_value = step * (steps - 1.0); + if new_value < (*self.range.start()).into() { + return Some(*self.range.start()); + } - if new_value < (*range.start()).into() { - return Some(*range.start()); - } + T::from_f64(new_value) + }; - T::from_f64(new_value) - }; + let change = |new_value: T| { + if (self.value.into() - new_value.into()).abs() > f64::EPSILON { + shell.publish((self.on_change)(new_value)); - let change = |new_value: T| { - if ((*value).into() - new_value.into()).abs() > f64::EPSILON { - shell.publish((on_change)(new_value)); + self.value = new_value; + } + }; - *value = new_value; - } - }; - - match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if let Some(cursor_position) = cursor.position_over(layout.bounds()) - { - if state.keyboard_modifiers.control() - || state.keyboard_modifiers.command() + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if let Some(cursor_position) = + cursor.position_over(layout.bounds()) { - let _ = default.map(change); - state.is_dragging = false; - } else { - let _ = locate(cursor_position).map(change); - state.is_dragging = true; - } + if state.keyboard_modifiers.control() + || state.keyboard_modifiers.command() + { + let _ = self.default.map(change); + state.is_dragging = false; + } else { + let _ = locate(cursor_position).map(change); + state.is_dragging = true; + } - return event::Status::Captured; - } - } - Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerLifted { .. }) - | Event::Touch(touch::Event::FingerLost { .. }) => { - if is_dragging { - if let Some(on_release) = on_release.clone() { - shell.publish(on_release); + return event::Status::Captured; } - state.is_dragging = false; + } + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) + | Event::Touch(touch::Event::FingerLost { .. }) => { + if is_dragging { + if let Some(on_release) = self.on_release.clone() { + shell.publish(on_release); + } + state.is_dragging = false; - return event::Status::Captured; + return event::Status::Captured; + } } - } - Event::Mouse(mouse::Event::CursorMoved { .. }) - | Event::Touch(touch::Event::FingerMoved { .. }) => { - if is_dragging { - let _ = cursor.position().and_then(locate).map(change); + Event::Mouse(mouse::Event::CursorMoved { .. }) + | Event::Touch(touch::Event::FingerMoved { .. }) => { + if is_dragging { + let _ = cursor.position().and_then(locate).map(change); - return event::Status::Captured; + return event::Status::Captured; + } } - } - Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { - if cursor.position_over(layout.bounds()).is_some() { - match key { - Key::Named(key::Named::ArrowUp) => { - let _ = increment(current_value).map(change); - } - Key::Named(key::Named::ArrowDown) => { - let _ = decrement(current_value).map(change); + Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { + if cursor.position_over(layout.bounds()).is_some() { + match key { + Key::Named(key::Named::ArrowUp) => { + let _ = increment(current_value).map(change); + } + Key::Named(key::Named::ArrowDown) => { + let _ = decrement(current_value).map(change); + } + _ => (), } - _ => (), - } - return event::Status::Captured; + return event::Status::Captured; + } } + Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { + state.keyboard_modifiers = modifiers; + } + _ => {} } - Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { - state.keyboard_modifiers = modifiers; - } - _ => {} + + event::Status::Ignored } - event::Status::Ignored -} + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + _style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + ) { + let state = tree.state.downcast_ref::(); + let bounds = layout.bounds(); + let is_mouse_over = cursor.is_over(bounds); -/// Draws a [`VerticalSlider`]. -pub fn draw( - renderer: &mut Renderer, - layout: Layout<'_>, - cursor: mouse::Cursor, - state: &State, - value: T, - range: &RangeInclusive, - style_sheet: &Theme, - style: &Theme::Style, -) where - T: Into + Copy, - Theme: StyleSheet, - Renderer: core::Renderer, -{ - let bounds = layout.bounds(); - let is_mouse_over = cursor.is_over(bounds); - - let style = if state.is_dragging { - style_sheet.dragging(style) - } else if is_mouse_over { - style_sheet.hovered(style) - } else { - style_sheet.active(style) - }; - - let (handle_width, handle_height, handle_border_radius) = - match style.handle.shape { - HandleShape::Circle { radius } => { - (radius * 2.0, radius * 2.0, radius.into()) - } - HandleShape::Rectangle { - width, - border_radius, - } => (f32::from(width), bounds.width, border_radius), + let style = (self.style)( + theme, + if state.is_dragging { + Status::Dragging + } else if is_mouse_over { + Status::Hovered + } else { + Status::Active + }, + ); + + let (handle_width, handle_height, handle_border_radius) = + match style.handle.shape { + HandleShape::Circle { radius } => { + (radius * 2.0, radius * 2.0, radius.into()) + } + HandleShape::Rectangle { + width, + border_radius, + } => (f32::from(width), bounds.width, border_radius), + }; + + let value = self.value.into() as f32; + let (range_start, range_end) = { + let (start, end) = self.range.clone().into_inner(); + + (start.into() as f32, end.into() as f32) }; - let value = value.into() as f32; - let (range_start, range_end) = { - let (start, end) = range.clone().into_inner(); - - (start.into() as f32, end.into() as f32) - }; - - let offset = if range_start >= range_end { - 0.0 - } else { - (bounds.height - handle_width) * (value - range_end) - / (range_start - range_end) - }; - - let rail_x = bounds.x + bounds.width / 2.0; - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - style.rail.width / 2.0, - y: bounds.y, - width: style.rail.width, - height: offset + handle_width / 2.0, - }, - border: Border::with_radius(style.rail.border_radius), - ..renderer::Quad::default() - }, - style.rail.colors.1, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - style.rail.width / 2.0, - y: bounds.y + offset + handle_width / 2.0, - width: style.rail.width, - height: bounds.height - offset - handle_width / 2.0, + let offset = if range_start >= range_end { + 0.0 + } else { + (bounds.height - handle_width) * (value - range_end) + / (range_start - range_end) + }; + + let rail_x = bounds.x + bounds.width / 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: rail_x - style.rail.width / 2.0, + y: bounds.y, + width: style.rail.width, + height: offset + handle_width / 2.0, + }, + border: Border::with_radius(style.rail.border_radius), + ..renderer::Quad::default() }, - border: Border::with_radius(style.rail.border_radius), - ..renderer::Quad::default() - }, - style.rail.colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - handle_height / 2.0, - y: bounds.y + offset, - width: handle_height, - height: handle_width, + style.rail.colors.1, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: rail_x - style.rail.width / 2.0, + y: bounds.y + offset + handle_width / 2.0, + width: style.rail.width, + height: bounds.height - offset - handle_width / 2.0, + }, + border: Border::with_radius(style.rail.border_radius), + ..renderer::Quad::default() }, - border: Border { - radius: handle_border_radius, - width: style.handle.border_width, - color: style.handle.border_color, + style.rail.colors.0, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: rail_x - handle_height / 2.0, + y: bounds.y + offset, + width: handle_height, + height: handle_width, + }, + border: Border { + radius: handle_border_radius, + width: style.handle.border_width, + color: style.handle.border_color, + }, + ..renderer::Quad::default() }, - ..renderer::Quad::default() - }, - style.handle.color, - ); + style.handle.color, + ); + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + let state = tree.state.downcast_ref::(); + let bounds = layout.bounds(); + let is_mouse_over = cursor.is_over(bounds); + + if state.is_dragging { + mouse::Interaction::Grabbing + } else if is_mouse_over { + mouse::Interaction::Grab + } else { + mouse::Interaction::default() + } + } } -/// Computes the current [`mouse::Interaction`] of a [`VerticalSlider`]. -pub fn mouse_interaction( - layout: Layout<'_>, - cursor: mouse::Cursor, - state: &State, -) -> mouse::Interaction { - let bounds = layout.bounds(); - let is_mouse_over = cursor.is_over(bounds); - - if state.is_dragging { - mouse::Interaction::Grabbing - } else if is_mouse_over { - mouse::Interaction::Grab - } else { - mouse::Interaction::default() +impl<'a, T, Message, Theme, Renderer> + From> + for Element<'a, Message, Theme, Renderer> +where + T: Copy + Into + num_traits::FromPrimitive + 'a, + Message: Clone + 'a, + Theme: StyleSheet + 'a, + Renderer: core::Renderer + 'a, +{ + fn from( + slider: VerticalSlider<'a, T, Message, Theme>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(slider) } } -/// The local state of a [`VerticalSlider`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct State { +struct State { is_dragging: bool, keyboard_modifiers: keyboard::Modifiers, } - -impl State { - /// Creates a new [`State`]. - pub fn new() -> State { - State::default() - } -} -- cgit From 4130ae4be95ce850263fbc55f490b68a95361d58 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 Mar 2024 19:31:26 +0100 Subject: Simplify theming for `Text` widget --- core/src/widget/text.rs | 65 ++++++++++++++++------ examples/integration/src/controls.rs | 103 ++++++++++++++++------------------- examples/lazy/src/main.rs | 3 +- examples/pane_grid/src/main.rs | 2 +- examples/pokedex/src/main.rs | 6 +- examples/todos/src/main.rs | 6 +- examples/tour/src/main.rs | 2 +- examples/visible_bounds/src/main.rs | 14 +++-- examples/websocket/src/main.rs | 4 +- style/src/theme.rs | 27 +-------- widget/src/checkbox.rs | 2 +- widget/src/tooltip.rs | 4 +- 12 files changed, 116 insertions(+), 122 deletions(-) diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 0796c4e4..217ad8b3 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -29,7 +29,7 @@ where vertical_alignment: alignment::Vertical, font: Option, shaping: Shaping, - style: Theme::Style, + style: Style, } impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> @@ -49,7 +49,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: Shaping::Basic, - style: Default::default(), + style: Style::Themed(Theme::default()), } } @@ -74,8 +74,20 @@ where } /// Sets the style of the [`Text`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self { + self.style = Style::Themed(style); + self + } + + /// Sets the [`Color`] of the [`Text`]. + pub fn color(mut self, color: impl Into) -> Self { + self.style = Style::Colored(Some(color.into())); + self + } + + /// Sets the [`Color`] of the [`Text`]. + pub fn color_maybe(mut self, color: Option>) -> Self { + self.style = Style::Colored(color.map(Into::into)); self } @@ -175,14 +187,12 @@ where ) { let state = tree.state.downcast_ref::>(); - draw( - renderer, - style, - layout, - state, - theme.appearance(self.style.clone()), - viewport, - ); + let appearance = match self.style { + Style::Themed(f) => f(theme), + Style::Colored(color) => Appearance { color }, + }; + + draw(renderer, style, layout, state, appearance, viewport); } } @@ -298,7 +308,7 @@ where horizontal_alignment: self.horizontal_alignment, vertical_alignment: self.vertical_alignment, font: self.font, - style: self.style.clone(), + style: self.style, shaping: self.shaping, } } @@ -327,11 +337,18 @@ where /// The style sheet of some text. pub trait StyleSheet { - /// The supported style of the [`StyleSheet`]. - type Style: Default + Clone; + /// Returns the default styling strategy for [`Text`]. + fn default() -> fn(&Self) -> Appearance { + |_| Appearance::default() + } +} - /// Produces the [`Appearance`] of some text. - fn appearance(&self, style: Self::Style) -> Appearance; +impl StyleSheet for Color { + fn default() -> fn(&Self) -> Appearance { + |color| Appearance { + color: Some(*color), + } + } } /// The apperance of some text. @@ -342,3 +359,17 @@ pub struct Appearance { /// The default, `None`, means using the inherited color. pub color: Option, } + +#[derive(Debug)] +enum Style { + Themed(fn(&Theme) -> Appearance), + Colored(Option), +} + +impl Clone for Style { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Style {} diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs index c9bab828..473a7138 100644 --- a/examples/integration/src/controls.rs +++ b/examples/integration/src/controls.rs @@ -1,25 +1,26 @@ use iced_wgpu::Renderer; -use iced_widget::{slider, text_input, Column, Row, Text}; -use iced_winit::core::{Alignment, Color, Element, Length}; +use iced_widget::{column, container, row, slider, text, text_input}; +use iced_winit::core::alignment; +use iced_winit::core::{Color, Element, Length}; use iced_winit::runtime::{Command, Program}; use iced_winit::style::Theme; pub struct Controls { background_color: Color, - text: String, + input: String, } #[derive(Debug, Clone)] pub enum Message { BackgroundColorChanged(Color), - TextChanged(String), + InputChanged(String), } impl Controls { pub fn new() -> Controls { Controls { background_color: Color::BLACK, - text: String::default(), + input: String::default(), } } @@ -38,8 +39,8 @@ impl Program for Controls { Message::BackgroundColorChanged(color) => { self.background_color = color; } - Message::TextChanged(text) => { - self.text = text; + Message::InputChanged(input) => { + self.input = input; } } @@ -48,60 +49,48 @@ impl Program for Controls { fn view(&self) -> Element { let background_color = self.background_color; - let text = &self.text; - let sliders = Row::new() - .width(500) - .spacing(20) - .push( - slider(0.0..=1.0, background_color.r, move |r| { - Message::BackgroundColorChanged(Color { - r, - ..background_color - }) + let sliders = row![ + slider(0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color }) - .step(0.01), - ) - .push( - slider(0.0..=1.0, background_color.g, move |g| { - Message::BackgroundColorChanged(Color { - g, - ..background_color - }) + }) + .step(0.01), + slider(0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color }) - .step(0.01), - ) - .push( - slider(0.0..=1.0, background_color.b, move |b| { - Message::BackgroundColorChanged(Color { - b, - ..background_color - }) + }) + .step(0.01), + slider(0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color }) - .step(0.01), - ); + }) + .step(0.01), + ] + .width(500) + .spacing(20); - Row::new() - .height(Length::Fill) - .align_items(Alignment::End) - .push( - Column::new().align_items(Alignment::End).push( - Column::new() - .padding(10) - .spacing(10) - .push(Text::new("Background color").style(Color::WHITE)) - .push(sliders) - .push( - Text::new(format!("{background_color:?}")) - .size(14) - .style(Color::WHITE), - ) - .push( - text_input("Placeholder", text) - .on_input(Message::TextChanged), - ), - ), - ) - .into() + container( + column![ + text("Background color").color(Color::WHITE), + text(format!("{background_color:?}")) + .size(14) + .color(Color::WHITE), + text_input("Placeholder", &self.input) + .on_input(Message::InputChanged), + sliders, + ] + .spacing(10), + ) + .padding(10) + .height(Length::Fill) + .align_y(alignment::Vertical::Bottom) + .into() } } diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 9d8c0e35..37b5d52c 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -184,8 +184,7 @@ impl Sandbox for App { .style(theme::Button::Destructive); row![ - text(&item.name) - .style(theme::Text::Color(item.color.into())), + text(&item.name).color(item.color), horizontal_space(), pick_list(Color::ALL, Some(item.color), move |color| { Message::ItemColorChanged(item.clone(), color) diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 39719420..c4bedccc 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -162,7 +162,7 @@ impl Application for Example { let title = row![ pin_button, "Pane", - text(pane.id.to_string()).style(if is_focused { + text(pane.id.to_string()).color(if is_focused { PANE_ID_COLOR_FOCUSED } else { PANE_ID_COLOR_UNFOCUSED diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 8b71a269..193f85f2 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -1,8 +1,6 @@ use iced::futures; use iced::widget::{self, column, container, image, row, text}; -use iced::{ - Alignment, Application, Color, Command, Element, Length, Settings, Theme, -}; +use iced::{Alignment, Application, Command, Element, Length, Settings, Theme}; pub fn main() -> iced::Result { Pokedex::run(Settings::default()) @@ -116,7 +114,7 @@ impl Pokemon { text(&self.name).size(30).width(Length::Fill), text(format!("#{}", self.number)) .size(20) - .style(Color::from([0.5, 0.5, 0.5])), + .color([0.5, 0.5, 0.5]), ] .align_items(Alignment::Center) .spacing(20), diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index eae127f7..b1aeb4a7 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -8,7 +8,7 @@ use iced::widget::{ }; use iced::window; use iced::{Application, Element}; -use iced::{Color, Command, Length, Settings, Size, Subscription}; +use iced::{Command, Length, Settings, Size, Subscription}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -209,7 +209,7 @@ impl Application for Todos { let title = text("todos") .width(Length::Fill) .size(100) - .style(Color::from([0.5, 0.5, 0.5])) + .color([0.5, 0.5, 0.5]) .horizontal_alignment(alignment::Horizontal::Center); let input = text_input("What needs to be done?", input_value) @@ -467,7 +467,7 @@ fn empty_message(message: &str) -> Element<'_, Message> { .width(Length::Fill) .size(25) .horizontal_alignment(alignment::Horizontal::Center) - .style(Color::from([0.7, 0.7, 0.7])), + .color([0.7, 0.7, 0.7]), ) .height(200) .center_y() diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 1e2f1ef8..52e1bbb7 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -474,7 +474,7 @@ impl<'a> Step { let color_section = column![ "And its color:", - text(format!("{color:?}")).style(color), + text(format!("{color:?}")).color(color), color_sliders, ] .padding(20) diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs index bef5d296..10cdc783 100644 --- a/examples/visible_bounds/src/main.rs +++ b/examples/visible_bounds/src/main.rs @@ -82,7 +82,10 @@ impl Application for Example { row![ text(label), horizontal_space(), - text(value).font(Font::MONOSPACE).size(14).style(color), + text(value) + .font(Font::MONOSPACE) + .size(14) + .color_maybe(color), ] .height(40) .align_items(Alignment::Center) @@ -102,13 +105,12 @@ impl Application for Example { }) .unwrap_or_default() { - Color { + Some(Color { g: 1.0, ..Color::BLACK - } - .into() + }) } else { - theme::Text::Default + None }, ) }; @@ -120,7 +122,7 @@ impl Application for Example { Some(Point { x, y }) => format!("({x}, {y})"), None => "unknown".to_string(), }, - theme::Text::Default, + None, ), view_bounds("Outer container", self.outer_bounds), view_bounds("Inner container", self.inner_bounds), diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 38a6db1e..47c1898a 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -6,7 +6,7 @@ use iced::widget::{ button, column, container, row, scrollable, text, text_input, }; use iced::{ - Application, Color, Command, Element, Length, Settings, Subscription, Theme, + color, Application, Command, Element, Length, Settings, Subscription, Theme, }; use once_cell::sync::Lazy; @@ -99,7 +99,7 @@ impl Application for WebSocket { let message_log: Element<_> = if self.messages.is_empty() { container( text("Your messages will appear here...") - .style(Color::from_rgb8(0x88, 0x88, 0x88)), + .color(color!(0x888888)), ) .width(Length::Fill) .height(Length::Fill) diff --git a/style/src/theme.rs b/style/src/theme.rs index 656d6bf9..43e7cafd 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1203,32 +1203,7 @@ impl scrollable::StyleSheet for Theme { } } -/// The style of text. -#[derive(Clone, Copy, Default)] -pub enum Text { - /// The default style. - #[default] - Default, - /// Colored text. - Color(Color), -} - -impl From for Text { - fn from(color: Color) -> Self { - Text::Color(color) - } -} - -impl text::StyleSheet for Theme { - type Style = Text; - - fn appearance(&self, style: Self::Style) -> text::Appearance { - match style { - Text::Default => text::Appearance::default(), - Text::Color(c) => text::Appearance { color: Some(c) }, - } - } -} +impl text::StyleSheet for Theme {} /// The style of a text input. #[derive(Default)] diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 0ff4d58b..3a192fba 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -39,7 +39,7 @@ pub struct Checkbox< Theme = crate::Theme, Renderer = crate::Renderer, > where - Theme: StyleSheet + crate::text::StyleSheet, + Theme: StyleSheet, Renderer: text::Renderer, { is_checked: bool, diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index d8a1e131..51969aec 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -20,7 +20,7 @@ pub struct Tooltip< Theme = crate::Theme, Renderer = crate::Renderer, > where - Theme: container::StyleSheet + crate::text::StyleSheet, + Theme: container::StyleSheet, Renderer: text::Renderer, { content: Element<'a, Message, Theme, Renderer>, @@ -34,7 +34,7 @@ pub struct Tooltip< impl<'a, Message, Theme, Renderer> Tooltip<'a, Message, Theme, Renderer> where - Theme: container::StyleSheet + crate::text::StyleSheet, + Theme: container::StyleSheet, Renderer: text::Renderer, { /// The default padding of a [`Tooltip`] drawn by this renderer. -- cgit From db92e1c942154bee474fee5e2c187f8a52a1bb96 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 Mar 2024 19:32:20 +0100 Subject: Enhnace `Themer` to allow derivation from current `Theme` --- widget/src/helpers.rs | 13 +++-- widget/src/themer.rs | 133 ++++++++++++++++++++++++++++---------------------- 2 files changed, 80 insertions(+), 66 deletions(-) diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index ed385ea5..e6322926 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -15,7 +15,6 @@ use crate::rule::{self, Rule}; use crate::runtime::Command; use crate::scrollable::{self, Scrollable}; use crate::slider::{self, Slider}; -use crate::style::application; use crate::text::{self, Text}; use crate::text_editor::{self, TextEditor}; use crate::text_input::{self, TextInput}; @@ -440,13 +439,13 @@ where } /// A widget that applies any `Theme` to its contents. -pub fn themer<'a, Message, Theme, Renderer>( - theme: Theme, - content: impl Into>, -) -> Themer<'a, Message, Theme, Renderer> +pub fn themer<'a, Message, OldTheme, NewTheme, F, Renderer>( + to_theme: F, + content: impl Into>, +) -> Themer<'a, Message, OldTheme, NewTheme, F, Renderer> where + F: Fn(&OldTheme) -> NewTheme, Renderer: core::Renderer, - Theme: application::StyleSheet, { - Themer::new(theme, content) + Themer::new(to_theme, content) } diff --git a/widget/src/themer.rs b/widget/src/themer.rs index 3a5fd823..a7eabd2c 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -7,58 +7,68 @@ use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::widget::Operation; use crate::core::{ - Background, Clipboard, Element, Layout, Length, Point, Rectangle, Shell, - Size, Vector, Widget, + Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, + Shell, Size, Vector, Widget, }; -use crate::style::application; + +use std::marker::PhantomData; /// A widget that applies any `Theme` to its contents. /// /// This widget can be useful to leverage multiple `Theme` /// types in an application. #[allow(missing_debug_implementations)] -pub struct Themer<'a, Message, Theme, Renderer> +pub struct Themer<'a, Message, Theme, NewTheme, F, Renderer = crate::Renderer> where + F: Fn(&Theme) -> NewTheme, Renderer: crate::core::Renderer, - Theme: application::StyleSheet, { - content: Element<'a, Message, Theme, Renderer>, - theme: Theme, - style: Theme::Style, - show_background: bool, + content: Element<'a, Message, NewTheme, Renderer>, + to_theme: F, + text_color: Option Color>, + background: Option Background>, + old_theme: PhantomData, } -impl<'a, Message, Theme, Renderer> Themer<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, NewTheme, F, Renderer> + Themer<'a, Message, Theme, NewTheme, F, Renderer> where + F: Fn(&Theme) -> NewTheme, Renderer: crate::core::Renderer, - Theme: application::StyleSheet, { /// Creates an empty [`Themer`] that applies the given `Theme` /// to the provided `content`. - pub fn new(theme: Theme, content: T) -> Self + pub fn new(to_theme: F, content: T) -> Self where - T: Into>, + T: Into>, { Self { content: content.into(), - theme, - style: Theme::Style::default(), - show_background: false, + to_theme, + text_color: None, + background: None, + old_theme: PhantomData, } } - /// Sets whether to draw the background color of the `Theme`. - pub fn background(mut self, background: bool) -> Self { - self.show_background = background; + /// Sets the default text [`Color`] of the [`Themer`]. + pub fn text_color(mut self, f: fn(&NewTheme) -> Color) -> Self { + self.text_color = Some(f); + self + } + + /// Sets the [`Background`] of the [`Themer`]. + pub fn background(mut self, f: fn(&NewTheme) -> Background) -> Self { + self.background = Some(f); self } } -impl<'a, AnyTheme, Message, Theme, Renderer> Widget - for Themer<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, NewTheme, F, Renderer> Widget + for Themer<'a, Message, Theme, NewTheme, F, Renderer> where + F: Fn(&Theme) -> NewTheme, Renderer: crate::core::Renderer, - Theme: application::StyleSheet, { fn tag(&self) -> tree::Tag { self.content.as_widget().tag() @@ -134,38 +144,36 @@ where &self, tree: &Tree, renderer: &mut Renderer, - _theme: &AnyTheme, - _style: &renderer::Style, + theme: &Theme, + style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { - let appearance = self.theme.appearance(&self.style); + let theme = (self.to_theme)(theme); - if self.show_background { + if let Some(background) = self.background { container::draw_background( renderer, &container::Appearance { - background: Some(Background::Color( - appearance.background_color, - )), + background: Some(background(&theme)), ..container::Appearance::default() }, layout.bounds(), ); } - self.content.as_widget().draw( - tree, - renderer, - &self.theme, - &renderer::Style { - text_color: appearance.text_color, - }, - layout, - cursor, - viewport, - ); + let style = if let Some(text_color) = self.text_color { + renderer::Style { + text_color: text_color(&theme), + } + } else { + *style + }; + + self.content + .as_widget() + .draw(tree, renderer, &theme, &style, layout, cursor, viewport); } fn overlay<'b>( @@ -174,15 +182,15 @@ where layout: Layout<'_>, renderer: &Renderer, translation: Vector, - ) -> Option> { - struct Overlay<'a, Message, Theme, Renderer> { - theme: &'a Theme, - content: overlay::Element<'a, Message, Theme, Renderer>, + ) -> Option> { + struct Overlay<'a, Message, Theme, NewTheme, Renderer> { + to_theme: &'a dyn Fn(&Theme) -> NewTheme, + content: overlay::Element<'a, Message, NewTheme, Renderer>, } - impl<'a, AnyTheme, Message, Theme, Renderer> - overlay::Overlay - for Overlay<'a, Message, Theme, Renderer> + impl<'a, Message, Theme, NewTheme, Renderer> + overlay::Overlay + for Overlay<'a, Message, Theme, NewTheme, Renderer> where Renderer: crate::core::Renderer, { @@ -197,13 +205,18 @@ where fn draw( &self, renderer: &mut Renderer, - _theme: &AnyTheme, + theme: &Theme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, ) { - self.content - .draw(renderer, self.theme, style, layout, cursor); + self.content.draw( + renderer, + &(self.to_theme)(theme), + style, + layout, + cursor, + ); } fn on_event( @@ -252,12 +265,12 @@ where &'b mut self, layout: Layout<'_>, renderer: &Renderer, - ) -> Option> + ) -> Option> { self.content .overlay(layout, renderer) .map(|content| Overlay { - theme: self.theme, + to_theme: &self.to_theme, content, }) .map(|overlay| overlay::Element::new(Box::new(overlay))) @@ -268,24 +281,26 @@ where .as_widget_mut() .overlay(tree, layout, renderer, translation) .map(|content| Overlay { - theme: &self.theme, + to_theme: &self.to_theme, content, }) .map(|overlay| overlay::Element::new(Box::new(overlay))) } } -impl<'a, AnyTheme, Message, Theme, Renderer> - From> - for Element<'a, Message, AnyTheme, Renderer> +impl<'a, Message, Theme, NewTheme, F, Renderer> + From> + for Element<'a, Message, Theme, Renderer> where Message: 'a, - Theme: 'a + application::StyleSheet, + Theme: 'a, + NewTheme: 'a, + F: Fn(&Theme) -> NewTheme + 'a, Renderer: 'a + crate::core::Renderer, { fn from( - themer: Themer<'a, Message, Theme, Renderer>, - ) -> Element<'a, Message, AnyTheme, Renderer> { + themer: Themer<'a, Message, Theme, NewTheme, F, Renderer>, + ) -> Element<'a, Message, Theme, Renderer> { Element::new(themer) } } -- cgit From f4a4845ddbdced81ae4ff60bfa19f0e602d84709 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 Mar 2024 20:42:37 +0100 Subject: Simplify theming for `Button` widget --- core/src/background.rs | 13 ++ core/src/color.rs | 8 + core/src/gradient.rs | 4 +- examples/editor/src/main.rs | 2 +- examples/game_of_life/src/main.rs | 6 +- examples/lazy/src/main.rs | 3 +- examples/pane_grid/src/main.rs | 11 +- examples/screenshot/src/main.rs | 6 +- examples/stopwatch/src/main.rs | 4 +- examples/todos/src/main.rs | 14 +- examples/tour/src/main.rs | 30 ++- style/src/button.rs | 78 -------- style/src/checkbox.rs | 10 +- style/src/theme.rs | 123 +----------- widget/src/button.rs | 382 +++++++++++++++++++++++--------------- widget/src/helpers.rs | 2 +- 16 files changed, 295 insertions(+), 401 deletions(-) diff --git a/core/src/background.rs b/core/src/background.rs index 347c52c0..2e28e560 100644 --- a/core/src/background.rs +++ b/core/src/background.rs @@ -11,6 +11,19 @@ pub enum Background { // TODO: Add image variant } +impl Background { + /// Increases the translucency of the [`Background`] + /// by the given factor. + pub fn transparentize(self, factor: f32) -> Self { + match self { + Self::Color(color) => Self::Color(color.transparentize(factor)), + Self::Gradient(gradient) => { + Self::Gradient(gradient.transparentize(factor)) + } + } + } +} + impl From for Background { fn from(color: Color) -> Self { Background::Color(color) diff --git a/core/src/color.rs b/core/src/color.rs index b8db322f..6526e220 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -151,6 +151,14 @@ impl Color { pub fn inverse(self) -> Color { Color::new(1.0f32 - self.r, 1.0f32 - self.g, 1.0f32 - self.b, self.a) } + + /// Transparentizes the [`Color`] by the given factor. + pub fn transparentize(self, factor: f32) -> Color { + Self { + a: self.a * factor, + ..self + } + } } impl From<[f32; 3]> for Color { diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 4711b044..ecf7830f 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -13,11 +13,11 @@ pub enum Gradient { impl Gradient { /// Adjust the opacity of the gradient by a multiplier applied to each color stop. - pub fn mul_alpha(mut self, alpha_multiplier: f32) -> Self { + pub fn transparentize(mut self, factor: f32) -> Self { match &mut self { Gradient::Linear(linear) => { for stop in linear.stops.iter_mut().flatten() { - stop.color.a *= alpha_multiplier; + stop.color.a *= factor; } } } diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 53c9cf7c..b5870e9e 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -290,7 +290,7 @@ fn action<'a, Message: Clone + 'a>( .style(theme::Container::Box) .into() } else { - action.style(theme::Button::Secondary).into() + action.style(button::secondary).into() } } diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 9cbb7fff..b362381c 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -6,7 +6,6 @@ use grid::Grid; use preset::Preset; use iced::executor; -use iced::theme::{self, Theme}; use iced::time; use iced::widget::{ button, checkbox, column, container, pick_list, row, slider, text, @@ -14,6 +13,7 @@ use iced::widget::{ use iced::window; use iced::{ Alignment, Application, Command, Element, Length, Settings, Subscription, + Theme, }; use std::time::Duration; @@ -171,7 +171,7 @@ fn view_controls<'a>( .on_press(Message::TogglePlayback), button("Next") .on_press(Message::Next) - .style(theme::Button::Secondary), + .style(button::secondary), ] .spacing(10); @@ -195,7 +195,7 @@ fn view_controls<'a>( .text_size(16), button("Clear") .on_press(Message::Clear) - .style(theme::Button::Destructive), + .style(button::destructive), ] .padding(10) .spacing(20) diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 37b5d52c..1c5f59d5 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -1,4 +1,3 @@ -use iced::theme; use iced::widget::{ button, column, horizontal_space, lazy, pick_list, row, scrollable, text, text_input, @@ -181,7 +180,7 @@ impl Sandbox for App { column(items.into_iter().map(|item| { let button = button("Delete") .on_press(Message::DeleteItem(item.clone())) - .style(theme::Button::Destructive); + .style(button::destructive); row![ text(&item.name).color(item.color), diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index c4bedccc..2bed5a03 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -1,13 +1,13 @@ use iced::alignment::{self, Alignment}; use iced::executor; use iced::keyboard; -use iced::theme::{self, Theme}; use iced::widget::pane_grid::{self, PaneGrid}; use iced::widget::{ button, column, container, responsive, row, scrollable, text, }; use iced::{ Application, Color, Command, Element, Length, Settings, Size, Subscription, + Theme, }; pub fn main() -> iced::Result { @@ -287,10 +287,7 @@ fn view_content<'a>( ) ] .push_maybe(if total_panes > 1 && !is_pinned { - Some( - button("Close", Message::Close(pane)) - .style(theme::Button::Destructive), - ) + Some(button("Close", Message::Close(pane)).style(button::destructive)) } else { None }) @@ -327,7 +324,7 @@ fn view_controls<'a>( Some( button(text(content).size(14)) - .style(theme::Button::Secondary) + .style(button::secondary) .padding(3) .on_press(message), ) @@ -336,7 +333,7 @@ fn view_controls<'a>( }); let close = button(text("Close").size(14)) - .style(theme::Button::Destructive) + .style(button::destructive) .padding(3) .on_press_maybe(if total_panes > 1 && !is_pinned { Some(Message::Close(pane)) diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 79749956..dc4684d4 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -216,9 +216,9 @@ impl Application for Example { ) } else { button(centered_text("Saving...")) - .style(theme::Button::Secondary) + .style(button::secondary) } - .style(theme::Button::Secondary) + .style(button::secondary) .padding([10, 20, 10, 20]) .width(Length::Fill) ] @@ -227,7 +227,7 @@ impl Application for Example { crop_controls, button(centered_text("Crop")) .on_press(Message::Crop) - .style(theme::Button::Destructive) + .style(button::destructive) .padding([10, 20, 10, 20]) .width(Length::Fill), ] diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs index 8a0674c1..7a097e90 100644 --- a/examples/stopwatch/src/main.rs +++ b/examples/stopwatch/src/main.rs @@ -1,11 +1,11 @@ use iced::alignment; use iced::executor; use iced::keyboard; -use iced::theme::{self, Theme}; use iced::time; use iced::widget::{button, column, container, row, text}; use iced::{ Alignment, Application, Command, Element, Length, Settings, Subscription, + Theme, }; use std::time::{Duration, Instant}; @@ -136,7 +136,7 @@ impl Application for Stopwatch { }; let reset_button = button("Reset") - .style(theme::Button::Destructive) + .style(button::destructive) .on_press(Message::Reset); let controls = row![toggle_button, reset_button].spacing(20); diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index b1aeb4a7..b3b5d87a 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -1,14 +1,14 @@ use iced::alignment::{self, Alignment}; use iced::font::{self, Font}; use iced::keyboard; -use iced::theme::{self, Theme}; use iced::widget::{ self, button, checkbox, column, container, keyed_column, row, scrollable, text, text_input, Text, }; use iced::window; -use iced::{Application, Element}; -use iced::{Command, Length, Settings, Size, Subscription}; +use iced::{ + Application, Command, Element, Length, Settings, Size, Subscription, Theme, +}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -362,7 +362,7 @@ impl Task { button(edit_icon()) .on_press(TaskMessage::Edit) .padding(10) - .style(theme::Button::Text), + .style(button::text), ] .spacing(20) .align_items(Alignment::Center) @@ -385,7 +385,7 @@ impl Task { ) .on_press(TaskMessage::Delete) .padding(10) - .style(theme::Button::Destructive) + .style(button::destructive) ] .spacing(20) .align_items(Alignment::Center) @@ -402,9 +402,9 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element { let label = text(label); let button = button(label).style(if filter == current_filter { - theme::Button::Primary + button::primary } else { - theme::Button::Text + button::text }); button.on_press(Message::FilterChanged(filter)).padding(8) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 52e1bbb7..f5791ad7 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,7 +1,6 @@ use iced::alignment::{self, Alignment}; -use iced::theme; use iced::widget::{ - checkbox, column, container, horizontal_space, image, radio, row, + button, checkbox, column, container, horizontal_space, image, radio, row, scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; @@ -56,18 +55,17 @@ impl Sandbox for Tour { fn view(&self) -> Element { let Tour { steps, .. } = self; - let controls = row![] - .push_maybe(steps.has_previous().then(|| { - button("Back") - .on_press(Message::BackPressed) - .style(theme::Button::Secondary) - })) - .push(horizontal_space()) - .push_maybe( - steps - .can_continue() - .then(|| button("Next").on_press(Message::NextPressed)), - ); + let controls = + row![] + .push_maybe(steps.has_previous().then(|| { + padded_button("Back") + .on_press(Message::BackPressed) + .style(button::secondary) + })) + .push(horizontal_space()) + .push_maybe(steps.can_continue().then(|| { + padded_button("Next").on_press(Message::NextPressed) + })); let content: Element<_> = column![ steps.view(self.debug).map(Message::StepMessage), @@ -676,8 +674,8 @@ fn ferris<'a>( .center_x() } -fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> { - iced::widget::button(text(label)).padding([12, 24]) +fn padded_button<'a, Message: Clone>(label: &str) -> Button<'a, Message> { + button(text(label)).padding([12, 24]) } fn color_slider<'a>( diff --git a/style/src/button.rs b/style/src/button.rs index 0d7a668a..8b137891 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -1,79 +1 @@ -//! Change the apperance of a button. -use iced_core::{Background, Border, Color, Shadow, Vector}; -/// The appearance of a button. -#[derive(Debug, Clone, Copy)] -pub struct Appearance { - /// The amount of offset to apply to the shadow of the button. - pub shadow_offset: Vector, - /// The [`Background`] of the button. - pub background: Option, - /// The text [`Color`] of the button. - pub text_color: Color, - /// The [`Border`] of the buton. - pub border: Border, - /// The [`Shadow`] of the butoon. - pub shadow: Shadow, -} - -impl std::default::Default for Appearance { - fn default() -> Self { - Self { - shadow_offset: Vector::default(), - background: None, - text_color: Color::BLACK, - border: Border::default(), - shadow: Shadow::default(), - } - } -} - -/// A set of rules that dictate the style of a button. -pub trait StyleSheet { - /// The supported style of the [`StyleSheet`]. - type Style: Default; - - /// Produces the active [`Appearance`] of a button. - fn active(&self, style: &Self::Style) -> Appearance; - - /// Produces the hovered [`Appearance`] of a button. - fn hovered(&self, style: &Self::Style) -> Appearance { - let active = self.active(style); - - Appearance { - shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), - ..active - } - } - - /// Produces the pressed [`Appearance`] of a button. - fn pressed(&self, style: &Self::Style) -> Appearance { - Appearance { - shadow_offset: Vector::default(), - ..self.active(style) - } - } - - /// Produces the disabled [`Appearance`] of a button. - fn disabled(&self, style: &Self::Style) -> Appearance { - let active = self.active(style); - - Appearance { - shadow_offset: Vector::default(), - background: active.background.map(|background| match background { - Background::Color(color) => Background::Color(Color { - a: color.a * 0.5, - ..color - }), - Background::Gradient(gradient) => { - Background::Gradient(gradient.mul_alpha(0.5)) - } - }), - text_color: Color { - a: active.text_color.a * 0.5, - ..active.text_color - }, - ..active - } - } -} diff --git a/style/src/checkbox.rs b/style/src/checkbox.rs index 77093f69..5e1c8374 100644 --- a/style/src/checkbox.rs +++ b/style/src/checkbox.rs @@ -30,15 +30,7 @@ pub trait StyleSheet { let active = self.active(style, is_checked); Appearance { - background: match active.background { - Background::Color(color) => Background::Color(Color { - a: color.a * 0.5, - ..color - }), - Background::Gradient(gradient) => { - Background::Gradient(gradient.mul_alpha(0.5)) - } - }, + background: active.background.transparentize(0.5), ..active } } diff --git a/style/src/theme.rs b/style/src/theme.rs index 43e7cafd..f967aebc 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -4,7 +4,6 @@ pub mod palette; pub use palette::Palette; use crate::application; -use crate::button; use crate::checkbox; use crate::container; use crate::core::widget::text; @@ -22,7 +21,7 @@ use crate::text_editor; use crate::text_input; use crate::toggler; -use crate::core::{Background, Border, Color, Shadow, Vector}; +use crate::core::{Background, Border, Color, Shadow}; use std::fmt; use std::rc::Rc; @@ -285,126 +284,6 @@ impl application::Appearance> application::StyleSheet for T { } } -/// The style of a button. -#[derive(Default)] -pub enum Button { - /// The primary style. - #[default] - Primary, - /// The secondary style. - Secondary, - /// The positive style. - Positive, - /// The destructive style. - Destructive, - /// The text style. - /// - /// Useful for links! - Text, - /// A custom style. - Custom(Box>), -} - -impl Button { - /// Creates a custom [`Button`] style variant. - pub fn custom( - style_sheet: impl button::StyleSheet