Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Theming Revolutions #2350

Merged
merged 4 commits into from
Mar 25, 2024
Merged

Theming Revolutions #2350

merged 4 commits into from
Mar 25, 2024

Conversation

hecrj
Copy link
Member

@hecrj hecrj commented Mar 24, 2024

Hey! It's me changing theming completely once again!

Well, not really. There should not be any breaking changes for users of the built-in theme—except just the rename of Appearance structs to simply Style.

The main change here lets custom theme implementors define a Class associated type that the widget will carry. This Class is found in the DefaultStyle trait, which has been renamed to Catalog.

/// The theme catalog of a [`Button`].
pub trait Catalog {
    /// The item class of the [`Catalog`].
    type Class<'a>;

    /// 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, class: &Self::Class<'_>, status: Status) -> Style;
}

This lets custom theme users control the styling of a widget completely—allocations included!

The style method of widgets is still readable:

/// Sets the style of the [`Button`].
#[must_use]
pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
where
    Theme::Class<'a>: From<StyleFn<'a, Theme>>,

And, as you can see, as long as the Class associated type implements From<StyleFn>; widgets will be able to leverage the "closure styling" approach introduced in #2312.

StyleFn is just a type alias that every widget defines and is, as you'd expect, just a boxed style function:

/// A styling function for a [`Button`].
pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;

Finally, the widgets also expose a new class method:

/// Sets the style class of the [`Button`].
#[cfg(feature = "advanced")]
#[must_use]
pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self

Which can be used to set the particular Class of the widget directly and, since all of this is for custom theme users only, it's gated under the advanced feature.

@hecrj hecrj added this to the 0.13 milestone Mar 24, 2024
@alex-ds13
Copy link
Contributor

Hey there. This is looking really good for creating custom widgets. We'll be able to use the widget classes like before instead of a function for each variant which I like.

I have just one thing that I feel that lost some customization potential, which is the widgets with widgets in it, like the pick_list and combo_box. Previously we could set the default style for the pick_list field and the menu, we could also set the default style for a combo_box text_input and menu.
Now from what I understand we can only set the default style for the pick_list field. The style for the pick_list menu and combo_box menu will be the default style of the menu. This means we can't have different default styles for the pick_list menu and the combo_box menu. Same thing for the combo_box text_input.

@hecrj
Copy link
Member Author

hecrj commented Mar 25, 2024

@alex-ds13 I think you missed these methods.

@alex-ds13
Copy link
Contributor

@alex-ds13 I think you missed these methods.

I saw them, but to use those it has to be on the view function. The idea would be that one could simply create a pick_list or combo_box while having a CustomTheme set on the Program and the default looks would be set already.

This would allow creating a CustomTheme crate and anyone who wants to use it just sets that crate Theme as the Theme of the Program. And the view logic would stay this same. With those methods the user would have to do something like:

use custom_theme::Theme;

pick_list(Theme::ALL, Some(self.theme), Message::ChangeTheme)
.menu_class(Theme::Menu::PickList)

Wouldn't it be possible to have the menu::Style inside the PickList Style like this:

/// The appearance of a pick list.
#[derive(Debug, Clone, Copy)]
pub struct Style {
    /// The text [`Color`] of the pick list.
    pub text_color: Color,
    /// The placeholder [`Color`] of the pick list.
    pub placeholder_color: Color,
    /// The handle [`Color`] of the pick list.
    pub handle_color: Color,
    /// The [`Background`] of the pick list.
    pub background: Background,
    /// The [`Border`] of the pick list.
    pub border: Border,
    /// The [`Menu`] style of the pick list.
    pub menu_style: menu::Style,
}

And then the default function would have:

/// The default style of the field of a [`PickList`].
pub fn default(theme: &Theme, status: Status) -> Style {
    let palette = theme.extended_palette();

    let active = Style {
        text_color: palette.background.weak.text,
        background: palette.background.weak.color.into(),
        placeholder_color: palette.background.strong.color,
        handle_color: palette.background.weak.text,
        border: Border {
            radius: 2.0.into(),
            width: 1.0,
            color: palette.background.strong.color,
        },
        menu_style: menu::default(theme),
    };

    match status {
       // ...
    }

@hecrj
Copy link
Member Author

hecrj commented Mar 25, 2024

@alex-ds13 74373cb should address that use case.

@hecrj hecrj enabled auto-merge March 25, 2024 21:17
@hecrj hecrj merged commit eae4065 into master Mar 25, 2024
24 checks passed
@hecrj hecrj deleted the theming-revolutions branch March 25, 2024 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants