How to replace StyleSheet

I’m using the " 0.13.0-dev " branch and trying to follow an example that uses the StyleSheet trait for styling its Button widdgets, but this trait has apparently been removed.

How can I duplicate the behaviour which used to have methods to return a style for “active”, “hovered” etc.?

I found that this is done by calling the

pub fn style(
        mut self,
        style: impl Fn(&Theme, Status) -> Appearance + 'a,
    ) -> Self {
        self.style = Box::new(style);
        self
    }

and within the function/closure using the Status to work out what Appearance to return. eg

match status {
        Status::Active => {
            Appearance {
                background: Some(Background::from(Color::from_rgb(0.8, 0.1, 0.1))),
                text_color: Color::BLACK,
                border: Border::default().with_width(Pixels::from(2)).with_color(Color::BLACK),
                shadow: Default::default()
            }
        }
etc.....

Hello. I am a beginner with this library. Is it possible to get a full example on how to style a button with the new API?

let custom_button = Button::new("Hello").style()...

What goes into style()? From what I have gathered it is supposed to be a struct that implements the Catalog trait.

Thanks

You put a closure into the style function that produces a Style object. The closure usually takes the current theme and some status of the widget as parameters.

You can find a full example here Pocket Guide - Styling.

Thank you so much. Here is an example of what I ended up doing for reference:

let custom_button = button("Hello")
    .style(|theme: &Theme, status| {
        CustomButtonStyle::style(theme, status)
    })

pub struct CustomButtonStyle;

impl CustomButtonStyle {
    fn style(theme: &Theme, status: button::Status) -> button::Style {
        match status {
            button::Status::Active => button::Style {
                // rest of the code here
            }
        }
    }
}

I am here again. I find this way of styling does not to scale. How can I get a custom Button that will I can just supply maybe a text or image icon?

My first thought is to put in a function like this:

fn custom_btn<'a, Message: 'a>(icon_svg: svg::Svg<'a>, text: &'a str) -> Button<'a, Message> { ... }

But it is clumsy to wire it up with on_press to pass a message in. How can I create my own version of the Button widget? so I can supply my own methods (along with the supported methods for the default Button) without creating a full-blown custom widget?

Looking at

fn custom_btn<'a, Message: 'a>(icon_svg: svg::Svg<'a>, text: &'a str) -> Button<'a, Message> { ... }

why can’t you use it like custom_btn(my_icon, "foo").on_press(Message::Pressed)?

it’s no different from using iced::widget::button()

unrelated but your type CustomButtonStyle in the previous snippet is superfluous. I would just use something like

mod style {
    fn my_style(theme: &Theme, status: button::Status) -> button::Style {
        // ...
    }
}

and then button(/* ... */).style(style::my_style)

2 Likes

Ok that tracks. Thanks for the help!

1 Like

That approach is a very common approach. But you would have to apply on_press like with any other button. For me personally, I mostly used it without any issues. But I did it with impl Into<Element<Message>> as parameter types instead of specific widgets. That way, it accepts all kinds of widgets.

Another common approach is to do what @airstrike showed.

Another approach would be to build a custom struct with the data/settings that you need that implements Into<Element<Message>> where you create the actual button. Then you should be able to pass it to widgets like column and row.

Extending the existing button widget, by adding methods to Button like in OOP, is not possible.

Can I get some example as to what you mean by this?

Another approach would be to build a custom struct with the data/settings that you need that implements Into<Element<Message>> where you create the actual button. Then you should be able to pass it to widgets like column and row .

Here is an example for modals that I use:

fn modal<'a, Message>(
    base: impl Into<iced::Element<'a, Message>>,
    content: impl Into<iced::Element<'a, Message>>,
    on_blur: Message,
    open: bool,
) -> iced::Element<'a, Message>
where
    Message: Clone + 'a,
{
    widget::stack![base.into(),]
        .push_maybe(if open {
            Some(widget::opaque(
                widget::mouse_area(widget::center(widget::opaque(content)).style(|_theme| {
                    widget::container::Style {
                        background: Some(
                            iced::Color {
                                a: 0.8,
                                ..iced::Color::BLACK
                            }
                            .into(),
                        ),
                        ..widget::container::Style::default()
                    }
                }))
                .on_press(on_blur),
            ))
        } else {
            None
        })
        .into()
}

As you can see it takes impl Into<iced::Element<'a, Message>> as parameter. That way I can pass any kind of widget to it. You could do the same for you button function.

1 Like