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` --- core/src/closure.rs | 33 +++++++++++++++++++++++++++++++++ core/src/lib.rs | 1 + 2 files changed, 34 insertions(+) create mode 100644 core/src/closure.rs (limited to 'core/src') diff --git a/core/src/closure.rs b/core/src/closure.rs new file mode 100644 index 00000000..dc7b4fee --- /dev/null +++ b/core/src/closure.rs @@ -0,0 +1,33 @@ +//! Box closures with ease. +//! +//! These are just a bunch of types that wrap boxed closures with +//! blanket [`From`] implementations for easy conversions. +//! +//! Mainly, it allows functions to take `Into` where `T` may end +//! up being a boxed closure. + +/// A boxed closure that takes `A` by reference and produces `O`. +#[allow(missing_debug_implementations)] +pub struct Unary<'a, A, O>(pub Box O + 'a>); + +impl<'a, A, O, T> From for Unary<'a, A, O> +where + T: Fn(&A) -> O + 'a, +{ + fn from(f: T) -> Self { + Self(Box::new(f)) + } +} + +/// A boxed closure that takes `A` by reference and `B` by value and produces `O`. +#[allow(missing_debug_implementations)] +pub struct Binary<'a, A, B, O>(pub Box O + 'a>); + +impl<'a, A, B, O, T> From for Binary<'a, A, B, O> +where + T: Fn(&A, B) -> O + 'a, +{ + fn from(f: T) -> Self { + Self(Box::new(f)) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index d076413e..36b47d80 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -19,6 +19,7 @@ pub mod alignment; pub mod border; pub mod clipboard; +pub mod closure; pub mod event; pub mod font; pub mod gradient; -- 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` --- core/src/closure.rs | 33 --------------------------------- core/src/lib.rs | 1 - 2 files changed, 34 deletions(-) delete mode 100644 core/src/closure.rs (limited to 'core/src') diff --git a/core/src/closure.rs b/core/src/closure.rs deleted file mode 100644 index dc7b4fee..00000000 --- a/core/src/closure.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Box closures with ease. -//! -//! These are just a bunch of types that wrap boxed closures with -//! blanket [`From`] implementations for easy conversions. -//! -//! Mainly, it allows functions to take `Into` where `T` may end -//! up being a boxed closure. - -/// A boxed closure that takes `A` by reference and produces `O`. -#[allow(missing_debug_implementations)] -pub struct Unary<'a, A, O>(pub Box O + 'a>); - -impl<'a, A, O, T> From for Unary<'a, A, O> -where - T: Fn(&A) -> O + 'a, -{ - fn from(f: T) -> Self { - Self(Box::new(f)) - } -} - -/// A boxed closure that takes `A` by reference and `B` by value and produces `O`. -#[allow(missing_debug_implementations)] -pub struct Binary<'a, A, B, O>(pub Box O + 'a>); - -impl<'a, A, B, O, T> From for Binary<'a, A, B, O> -where - T: Fn(&A, B) -> O + 'a, -{ - fn from(f: T) -> Self { - Self(Box::new(f)) - } -} diff --git a/core/src/lib.rs b/core/src/lib.rs index 36b47d80..d076413e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -19,7 +19,6 @@ pub mod alignment; pub mod border; pub mod clipboard; -pub mod closure; pub mod event; pub mod font; pub mod gradient; -- 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 --- core/src/widget/text.rs | 117 +++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 46 deletions(-) (limited to 'core/src') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 66e2d066..12f6956a 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -18,6 +18,7 @@ pub use text::{LineHeight, Shaping}; #[allow(missing_debug_implementations)] pub struct Text<'a, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { content: Cow<'a, str>, @@ -29,18 +30,16 @@ where vertical_alignment: alignment::Vertical, font: Option, shaping: Shaping, - style: Style<'a, Theme>, + class: Theme::Class<'a>, } impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { /// Create a new fragment of [`Text`] with the given contents. - pub fn new(content: impl Into>) -> Self - where - Theme: DefaultStyle + 'a, - { + pub fn new(content: impl Into>) -> Self { Text { content: content.into(), size: None, @@ -51,7 +50,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: Shaping::Basic, - style: Box::new(Theme::default_style), + class: Theme::default(), } } @@ -75,25 +74,6 @@ where self } - /// Sets the style of the [`Text`]. - pub fn style(mut self, style: impl Fn(&Theme) -> Appearance + 'a) -> Self { - self.style = Box::new(style); - self - } - - /// Sets the [`Color`] of the [`Text`]. - pub fn color(self, color: impl Into) -> Self { - self.color_maybe(Some(color)) - } - - /// Sets the [`Color`] of the [`Text`], if `Some`. - pub fn color_maybe(mut self, color: Option>) -> Self { - let color = color.map(Into::into); - - self.style = Box::new(move |_theme| Appearance { color }); - self - } - /// Sets the width of the [`Text`] boundaries. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); @@ -129,6 +109,42 @@ where self.shaping = shaping; self } + + /// Sets the style of the [`Text`]. + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); + self + } + + /// Sets the [`Color`] of the [`Text`]. + pub fn color(self, color: impl Into) -> Self + where + Theme::Class<'a>: From>, + { + self.color_maybe(Some(color)) + } + + /// Sets the [`Color`] of the [`Text`], if `Some`. + pub fn color_maybe(self, color: Option>) -> Self + where + Theme::Class<'a>: From>, + { + let color = color.map(Into::into); + + self.style(move |_theme| Style { color }) + } + + /// Sets the style class of the [`Text`]. + #[cfg(feature = "advanced")] + #[must_use] + pub fn class(mut self, class: impl Into>) -> Self { + self.class = class.into(); + self + } } /// The internal state of a [`Text`] widget. @@ -138,6 +154,7 @@ pub struct State(P); impl<'a, Message, Theme, Renderer> Widget for Text<'a, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { fn tag(&self) -> tree::Tag { @@ -182,15 +199,15 @@ where tree: &Tree, renderer: &mut Renderer, theme: &Theme, - style: &renderer::Style, + defaults: &renderer::Style, layout: Layout<'_>, _cursor_position: mouse::Cursor, viewport: &Rectangle, ) { let state = tree.state.downcast_ref::>(); - let appearance = (self.style)(theme); + let style = theme.style(&self.class); - draw(renderer, style, layout, state, appearance, viewport); + draw(renderer, defaults, layout, state, style, viewport); } } @@ -250,7 +267,7 @@ pub fn draw( style: &renderer::Style, layout: Layout<'_>, state: &State, - appearance: Appearance, + appearance: Style, viewport: &Rectangle, ) where Renderer: text::Renderer, @@ -281,7 +298,7 @@ pub fn draw( impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where - Theme: 'a, + Theme: Catalog + 'a, Renderer: text::Renderer + 'a, { fn from( @@ -293,7 +310,7 @@ where impl<'a, Theme, Renderer> From<&'a str> for Text<'a, Theme, Renderer> where - Theme: DefaultStyle + 'a, + Theme: Catalog + 'a, Renderer: text::Renderer, { fn from(content: &'a str) -> Self { @@ -304,7 +321,7 @@ where impl<'a, Message, Theme, Renderer> From<&'a str> for Element<'a, Message, Theme, Renderer> where - Theme: DefaultStyle + 'a, + Theme: Catalog + 'a, Renderer: text::Renderer + 'a, { fn from(content: &'a str) -> Self { @@ -314,30 +331,38 @@ where /// The appearance of some text. #[derive(Debug, Clone, Copy, Default)] -pub struct Appearance { +pub struct Style { /// The [`Color`] of the text. /// /// The default, `None`, means using the inherited color. pub color: Option, } -/// The style of some [`Text`]. -pub type Style<'a, Theme> = Box Appearance + 'a>; +/// The theme catalog of a [`Text`]. +pub trait Catalog: Sized { + /// The item class of this [`Catalog`]. + type Class<'a>; -/// The default style of some [`Text`]. -pub trait DefaultStyle { - /// Returns the default style of some [`Text`]. - fn default_style(&self) -> Appearance; + /// The default class produced by this [`Catalog`]. + fn default<'a>() -> Self::Class<'a>; + + /// The [`Style`] of a class with the given status. + fn style(&self, item: &Self::Class<'_>) -> Style; } -impl DefaultStyle for Theme { - fn default_style(&self) -> Appearance { - Appearance::default() +/// A styling function for a [`Text`]. +/// +/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. +pub type StyleFn<'a, Theme> = Box Style + 'a>; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(|_theme| Style::default()) } -} -impl DefaultStyle for Color { - fn default_style(&self) -> Appearance { - Appearance { color: Some(*self) } + fn style(&self, class: &Self::Class<'_>) -> Style { + class(self) } } -- cgit