From 999ad2d288a9354f045bb2e1b838014b3d302779 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 23 Mar 2024 19:23:08 +0100 Subject: Try catalog theming approach with `Button` --- widget/src/button.rs | 102 +++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'widget/src/button.rs') diff --git a/widget/src/button.rs b/widget/src/button.rs index 5790f811..8754622f 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -1,4 +1,5 @@ //! Allow your users to perform actions by pressing a button. +use crate::core::closure; use crate::core::event::{self, Event}; use crate::core::layout; use crate::core::mouse; @@ -49,6 +50,7 @@ use crate::core::{ pub struct Button<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> where Renderer: crate::core::Renderer, + Theme: Catalog, { content: Element<'a, Message, Theme, Renderer>, on_press: Option, @@ -56,20 +58,18 @@ where height: Length, padding: Padding, clip: bool, - style: Style<'a, Theme>, + style: Theme::Item<'a>, } impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer> where Renderer: crate::core::Renderer, + Theme: Catalog, { /// Creates a new [`Button`] with the given content. pub fn new( content: impl Into>, - ) -> Self - where - Theme: DefaultStyle + 'a, - { + ) -> Self { let content = content.into(); let size = content.as_widget().size_hint(); @@ -80,7 +80,7 @@ where height: size.height.fluid(), padding: DEFAULT_PADDING, clip: false, - style: Box::new(Theme::default_style), + style: Theme::default(), } } @@ -120,11 +120,8 @@ where } /// Sets the style variant of this [`Button`]. - pub fn style( - mut self, - style: impl Fn(&Theme, Status) -> Appearance + 'a, - ) -> Self { - self.style = Box::new(style); + pub fn style(mut self, style: impl Into>) -> Self { + self.style = style.into(); self } @@ -146,6 +143,7 @@ impl<'a, Message, Theme, Renderer> Widget where Message: 'a + Clone, Renderer: 'a + crate::core::Renderer, + Theme: Catalog, { fn tag(&self) -> tree::Tag { tree::Tag::of::() @@ -304,7 +302,7 @@ where Status::Active }; - let styling = (self.style)(theme, status); + let styling = theme.style(&self.style, status); if styling.background.is_some() || styling.border.width > 0.0 @@ -378,7 +376,7 @@ impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: Clone + 'a, - Theme: 'a, + Theme: Catalog + 'a, Renderer: crate::core::Renderer + 'a, { fn from(button: Button<'a, Message, Theme, Renderer>) -> Self { @@ -409,7 +407,7 @@ pub enum Status { /// The appearance of a button. #[derive(Debug, Clone, Copy, PartialEq)] -pub struct Appearance { +pub struct Style { /// The [`Background`] of the button. pub background: Option, /// The text [`Color`] of the button. @@ -420,7 +418,7 @@ pub struct Appearance { pub shadow: Shadow, } -impl Appearance { +impl Style { /// Updates the [`Appearance`] with the given [`Background`]. pub fn with_background(self, background: impl Into) -> Self { Self { @@ -430,7 +428,7 @@ impl Appearance { } } -impl std::default::Default for Appearance { +impl std::default::Default for Style { fn default() -> Self { Self { background: None, @@ -441,41 +439,43 @@ impl std::default::Default for Appearance { } } -/// The style of a [`Button`]. -pub type Style<'a, Theme> = Box Appearance + 'a>; +/// The theme catalog of a [`Button`]. +pub trait Catalog: Sized { + /// The item type of this [`Catalog`]. + type Item<'a>; -/// The default style of a [`Button`]. -pub trait DefaultStyle { - /// Returns the default style of a [`Button`]. - fn default_style(&self, status: Status) -> Appearance; -} + /// The default item produced by this [`Catalog`]. + fn default<'a>() -> Self::Item<'a>; -impl DefaultStyle for Theme { - fn default_style(&self, status: Status) -> Appearance { - primary(self, status) - } + /// The [`Style`] of an item with the given status. + fn style(&self, item: &Self::Item<'_>, status: Status) -> Style; } -impl DefaultStyle for Appearance { - fn default_style(&self, _status: Status) -> Appearance { - *self +/// The item of a button [`Catalog`] for the built-in [`Theme`]. +/// +/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. +pub type Item<'a, Theme> = closure::Binary<'a, Theme, Status, Style>; + +impl Catalog for Theme { + type Item<'a> = Item<'a, Self>; + + fn default<'a>() -> Self::Item<'a> { + closure::Binary::from(primary) } -} -impl DefaultStyle for Color { - fn default_style(&self, _status: Status) -> Appearance { - Appearance::default().with_background(*self) + fn style(&self, item: &Self::Item<'_>, status: Status) -> Style { + (item.0)(self, status) } } /// A primary button; denoting a main action. -pub fn primary(theme: &Theme, status: Status) -> Appearance { +pub fn primary(theme: &Theme, status: Status) -> Style { let palette = theme.extended_palette(); let base = styled(palette.primary.strong); match status { Status::Active | Status::Pressed => base, - Status::Hovered => Appearance { + Status::Hovered => Style { background: Some(Background::Color(palette.primary.base.color)), ..base }, @@ -484,13 +484,13 @@ pub fn primary(theme: &Theme, status: Status) -> Appearance { } /// A secondary button; denoting a complementary action. -pub fn secondary(theme: &Theme, status: Status) -> Appearance { +pub fn secondary(theme: &Theme, status: Status) -> Style { let palette = theme.extended_palette(); let base = styled(palette.secondary.base); match status { Status::Active | Status::Pressed => base, - Status::Hovered => Appearance { + Status::Hovered => Style { background: Some(Background::Color(palette.secondary.strong.color)), ..base }, @@ -499,13 +499,13 @@ pub fn secondary(theme: &Theme, status: Status) -> Appearance { } /// A success button; denoting a good outcome. -pub fn success(theme: &Theme, status: Status) -> Appearance { +pub fn success(theme: &Theme, status: Status) -> Style { let palette = theme.extended_palette(); let base = styled(palette.success.base); match status { Status::Active | Status::Pressed => base, - Status::Hovered => Appearance { + Status::Hovered => Style { background: Some(Background::Color(palette.success.strong.color)), ..base }, @@ -514,13 +514,13 @@ pub fn success(theme: &Theme, status: Status) -> Appearance { } /// A danger button; denoting a destructive action. -pub fn danger(theme: &Theme, status: Status) -> Appearance { +pub fn danger(theme: &Theme, status: Status) -> Style { let palette = theme.extended_palette(); let base = styled(palette.danger.base); match status { Status::Active | Status::Pressed => base, - Status::Hovered => Appearance { + Status::Hovered => Style { background: Some(Background::Color(palette.danger.strong.color)), ..base }, @@ -529,17 +529,17 @@ pub fn danger(theme: &Theme, status: Status) -> Appearance { } /// A text button; useful for links. -pub fn text(theme: &Theme, status: Status) -> Appearance { +pub fn text(theme: &Theme, status: Status) -> Style { let palette = theme.extended_palette(); - let base = Appearance { + let base = Style { text_color: palette.background.base.text, - ..Appearance::default() + ..Style::default() }; match status { Status::Active | Status::Pressed => base, - Status::Hovered => Appearance { + Status::Hovered => Style { text_color: palette.background.base.text.scale_alpha(0.8), ..base }, @@ -547,17 +547,17 @@ pub fn text(theme: &Theme, status: Status) -> Appearance { } } -fn styled(pair: palette::Pair) -> Appearance { - Appearance { +fn styled(pair: palette::Pair) -> Style { + Style { background: Some(Background::Color(pair.color)), text_color: pair.text, border: Border::rounded(2), - ..Appearance::default() + ..Style::default() } } -fn disabled(appearance: Appearance) -> Appearance { - Appearance { +fn disabled(appearance: Style) -> Style { + Style { background: appearance .background .map(|background| background.scale_alpha(0.5)), -- cgit From e657dc2ecd2ffa72c0abd87f9a59dcc1acbc7083 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 02:08:20 +0100 Subject: Fine-tune `Catalog` approach for `button`, `checkbox`, and `svg` --- widget/src/button.rs | 85 +++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 37 deletions(-) (limited to 'widget/src/button.rs') diff --git a/widget/src/button.rs b/widget/src/button.rs index 8754622f..80a18e82 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -1,5 +1,4 @@ //! Allow your users to perform actions by pressing a button. -use crate::core::closure; use crate::core::event::{self, Event}; use crate::core::layout; use crate::core::mouse; @@ -58,7 +57,7 @@ where height: Length, padding: Padding, clip: bool, - style: Theme::Item<'a>, + class: Theme::Class<'a>, } impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer> @@ -80,7 +79,7 @@ where height: size.height.fluid(), padding: DEFAULT_PADDING, clip: false, - style: Theme::default(), + class: Theme::default(), } } @@ -119,18 +118,30 @@ where self } - /// Sets the style variant of this [`Button`]. - pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); - self - } - /// Sets whether the contents of the [`Button`] should be clipped on /// overflow. pub fn clip(mut self, clip: bool) -> Self { self.clip = clip; self } + + /// Sets the style of this [`Button`]. + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); + self + } + + /// Sets the style class of this [`Button`]. + #[cfg(feature = "advanced")] + #[must_use] + pub fn class(mut self, class: impl Into>) -> Self { + self.class = class.into(); + self + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] @@ -302,19 +313,19 @@ where Status::Active }; - let styling = theme.style(&self.style, status); + let style = theme.style(&self.class, status); - if styling.background.is_some() - || styling.border.width > 0.0 - || styling.shadow.color.a > 0.0 + if style.background.is_some() + || style.border.width > 0.0 + || style.shadow.color.a > 0.0 { renderer.fill_quad( renderer::Quad { bounds, - border: styling.border, - shadow: styling.shadow, + border: style.border, + shadow: style.shadow, }, - styling + style .background .unwrap_or(Background::Color(Color::TRANSPARENT)), ); @@ -331,7 +342,7 @@ where renderer, theme, &renderer::Style { - text_color: styling.text_color, + text_color: style.text_color, }, content_layout, cursor, @@ -405,7 +416,7 @@ pub enum Status { Disabled, } -/// The appearance of a button. +/// The style of a button. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { /// The [`Background`] of the button. @@ -419,7 +430,7 @@ pub struct Style { } impl Style { - /// Updates the [`Appearance`] with the given [`Background`]. + /// Updates the [`Style`] with the given [`Background`]. pub fn with_background(self, background: impl Into) -> Self { Self { background: Some(background.into()), @@ -428,7 +439,7 @@ impl Style { } } -impl std::default::Default for Style { +impl Default for Style { fn default() -> Self { Self { background: None, @@ -441,30 +452,30 @@ impl std::default::Default for Style { /// The theme catalog of a [`Button`]. pub trait Catalog: Sized { - /// The item type of this [`Catalog`]. - type Item<'a>; + /// The item class of this [`Catalog`]. + type Class<'a>; - /// The default item produced by this [`Catalog`]. - fn default<'a>() -> Self::Item<'a>; + /// The default class produced by this [`Catalog`]. + fn default<'a>() -> Self::Class<'a>; - /// The [`Style`] of an item with the given status. - fn style(&self, item: &Self::Item<'_>, status: Status) -> Style; + /// The [`Style`] of a class with the given status. + fn style(&self, item: &Self::Class<'_>, status: Status) -> Style; } -/// The item of a button [`Catalog`] for the built-in [`Theme`]. +/// A styling function for a [`Button`]. /// /// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. -pub type Item<'a, Theme> = closure::Binary<'a, Theme, Status, Style>; +pub type StyleFn<'a, Theme> = Box Style + 'a>; impl Catalog for Theme { - type Item<'a> = Item<'a, Self>; + type Class<'a> = StyleFn<'a, Self>; - fn default<'a>() -> Self::Item<'a> { - closure::Binary::from(primary) + fn default<'a>() -> Self::Class<'a> { + Box::new(primary) } - fn style(&self, item: &Self::Item<'_>, status: Status) -> Style { - (item.0)(self, status) + fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { + class(self, status) } } @@ -556,12 +567,12 @@ fn styled(pair: palette::Pair) -> Style { } } -fn disabled(appearance: Style) -> Style { +fn disabled(style: Style) -> Style { Style { - background: appearance + background: style .background .map(|background| background.scale_alpha(0.5)), - text_color: appearance.text_color.scale_alpha(0.5), - ..appearance + text_color: style.text_color.scale_alpha(0.5), + ..style } } -- cgit From f0ae9a0c38c2532220a7460916604914db94c078 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 05:03:09 +0100 Subject: Use `Catalog` approach for all widgets --- widget/src/button.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'widget/src/button.rs') diff --git a/widget/src/button.rs b/widget/src/button.rs index 80a18e82..dc949671 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -125,7 +125,7 @@ where self } - /// Sets the style of this [`Button`]. + /// Sets the style of the [`Button`]. #[must_use] pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self where @@ -135,7 +135,7 @@ where self } - /// Sets the style class of this [`Button`]. + /// Sets the style class of the [`Button`]. #[cfg(feature = "advanced")] #[must_use] pub fn class(mut self, class: impl Into>) -> Self { @@ -451,20 +451,18 @@ impl Default for Style { } /// The theme catalog of a [`Button`]. -pub trait Catalog: Sized { - /// The item class of this [`Catalog`]. +pub trait Catalog { + /// The item class of the [`Catalog`]. type Class<'a>; - /// The default class produced by this [`Catalog`]. + /// The default class produced by the [`Catalog`]. fn default<'a>() -> Self::Class<'a>; /// The [`Style`] of a class with the given status. - fn style(&self, item: &Self::Class<'_>, status: Status) -> Style; + fn style(&self, class: &Self::Class<'_>, status: Status) -> Style; } /// A styling function for a [`Button`]. -/// -/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. pub type StyleFn<'a, Theme> = Box Style + 'a>; impl Catalog for Theme { -- cgit