Error when trying to define a color for a button

when trying to set the style for a button by iced::widget::button("set 9991").style(style); I get:

 .style(style)
    | ----- ^^^^^ the trait `From<iced::application::Appearance>` is not implemented for `iced::theme::Button`, which is required by `iced::application::Appearance: Into<_>`

What can I do about that ?

style of type iced_widget::button::Appearance also does not work.

How to find out which types are allowed as argument for Button<_,_,>.style() ?

Edit:

 let style = <Theme as iced_widget::button::StyleSheet>::Style::default();

compiles.
But I don’t understand this syntax and I don’t see how to change attributes w´in such kind of style.

As indicated at the doc here the .style function expects a struct that implements an Into trait.

The problem with iced_widget::button::Appearance [2] and iced::application::Appearance [3] is that NONE of them implements the necessary trait.

To satisfy the impl Into<<Theme as StyleSheet>::Style> requirement you need to implement something like bellow!

Remember that you’re trying to customize a button so the iced::theme::Container should probably be replaced by the iced::theme::Button [4]

use iced::{gradient, widget::container, Radians};

pub struct BlueContainerStyleSheet;

impl BlueContainerStyleSheet {
    pub fn new() -> iced::theme::Container {
        iced::theme::Container::Custom(Box::new(Self))
    }
}

impl container::StyleSheet for BlueContainerStyleSheet {
    type Style = iced::Theme;

    fn appearance(&self, style: &Self::Style) -> container::Appearance {
        let palette = style.palette();
        let bg = palette.primary;
        let fg = palette.primary;
        let gradient = gradient::Linear::new(Radians(90.0))
            .add_stop(0.0, bg)
            .add_stop(1.0, fg)
            .into();
        container::Appearance {
            text_color: Some(palette.text),
            background: Some(iced::Background::Gradient(gradient)),
            ..Default::default()
        }
    }
}

Let us know if you got it figured out :slight_smile:

URL References:
2. docs.rs/iced/latest/iced/widget/button/struct.Appearance.html
3. docs.rs/iced/latest/iced/application/struct.Appearance.html
4. docs.rs/iced/latest/iced/theme/enum.Button.html

Thanks !!!
This seems to work:

pub struct BlueButtonStyleSheet;

impl BlueButtonStyleSheet {
    pub fn new() -> iced::theme::Button {
        iced::theme::Button::Custom(Box::new(Self))
    }
}

impl button::StyleSheet for BlueButtonStyleSheet {
    type Style = iced::Theme;

    fn active(&self, style: &Self::Style) -> button::Appearance {
        let palette = style.palette();
        let background = iced::Background::Color(Color::from_rgb(0.5, 0.5, 0.5));
        button::Appearance {
            background: Some(background),
            text_color: palette.text,
            ..Default::default()
        }
    }
}

lets see if I can create a user friendly functionality from this …

Would it make sense to do it ion a way that it might be considered for including in the crate ?

Do you mean: include into the iced crate?

I see two ways to proceed:

  1. use the iced crate as it is and build a “button color” functionality on top of it
  2. locally modify the iced... crate and add support for the style() function allowing for providing more “user friendly” modification.

(2) certainly is harder, but could be evolve into a suggestion for enhancing the crate for many widgets, not just button.

Regarding the button color I just only tested the “active” appearance, but in the end a “user friendly” “current style” setting should/could modify all three appearances (active, hovered, pressed) at the same time in a consistent way.

Just raving…
Maybe an idea could be to try to do a “CommonWidgetStyle” type/trade for which a from function for all widgets could/should be implemented in a way that the (each optional) common style parameters are modified in a consistent way.
E.g.
With Container, a style() call with that type featuring a non None background_color would set the appearance background_color.
With Button, a style() call with non None background_color would set the appearance of the Active display to use that color, while trying to set the colors of the Pushed and Hovered variants in accordance to their previous relations to the previous Active color by some clever algorithm.

Ranting on…
I hae been able to do a my_sytyle() function in a trait that is added to Button, that takes a CommonWidgetStyle currently is just able to optionally modify the “active” color of the button.

seems promising :slight_smile:

With generic traits, overloading functions with different parameter types should be possible and the compiler should select the correct one when infering types (I did see examples and I seem to understand from() and into() work this way).
Unfortunately the additional my_style(self, value: CommonWidgetStyle) function I implemented using an additional trait trait MyStyle<TWidget> and which does work promisingly, can’t just be renamed to style(), as in that case the compiler uses the original function (which of course does result in an error, due to the type requested by that function is notCommonWidgetStyle) ).

Any ideas ?
Thanks !

Trying to do a style tweaker function that mixes new defined style elements with previously in the builder pattern defined style elements, I seemingly would need to get the previous elements out of the self parameter (here type Button). unfortunately I do not find a public member or a function that provides the current Theme / Stylesheet.

Any help ?
Thanks !

I did an example with overloading (i.e. overriding) methods according to the type of the argument in a builder pattern.

An “original” method that is defined using a trait gets overridden by a new method that accepts a new type.

maybe this paradigm can be used with style()


mod orig {
    use std::any::type_name;

    #[derive(Clone, Copy)]
    pub struct S1 {
        pub i: i32,
    }

    #[derive(Clone, Copy)]
    pub struct Sx {
        pub a: S1,
    }
    pub trait OrigTrait<T> {
        fn overloaded_method(self, s: T) -> Self;
    }

    impl OrigTrait<S1> for Sx {
        fn overloaded_method(mut self, s: S1) -> Self {
            let type_name = type_name::<S1>();
            println!("{} = {}", type_name, s.i);
            self.a.i = s.i + 200;
            self
        }
    }
}

mod over {
    use std::any::type_name;

    pub struct S2 {
        pub i: f32,
    }

    impl crate::orig::OrigTrait<S2> for crate::orig::Sx {
        fn overloaded_method(mut self, s: S2) -> Self {
            let type_name = type_name::<S2>();
            println!("{} = {}", type_name, s.i);
            self.a.i = s.i as i32 + 300;
            self
        }
    }
}

use std::any::type_name;
fn main() {
    use orig::{Sx, S1};
    use over::S2;
    let sa = S1 { i: 46 };
    let sb = S2 { i: 17.7 };
    let sc = Sx { a: S1 { i: 99 } };

    use orig::OrigTrait;
    /* same name, different argument types in Builder Pattern*/
    let sca = sc.overloaded_method(sa);
    let scb = sca.overloaded_method(sb);

    let type_name = type_name::<Sx>();
    println!("{} = {}", type_name, sca.a.i);
    println!("{} = {}", type_name, scb.a.i);
}

Ranting on …

As style() is pub fn style(mut self, style: impl Into<Theme::Style>) it seems like I could implement a From<CommonWidgetStyle> / from() for Button<> to allow for my Style definition to be correctly passed into style().
But which types beside the full Button (which I seem to be not able to instanciate in some default way) do implement IntoTheme::Style) (and might be intended for such purpose ?

EDIT:
Seems instead of the above, I need to do

impl From<CommonWidgetStyle> for iced::theme::Button {
    fn from(value: CommonWidgetStyle) -> Self {

Which might be a lot less complex :slight_smile:

At least this does compile and run without error message… Now trying to have it set the button color …

(still learning Rust on the fly :slight_smile: :slight_smile: :slight_smile: )

@prmthz
Using your suggestion as a startintg point I now was able to implementing an overriding feature for the style() function without modifying the library itself:

pub struct ColorButtonStyleSheet {
    background_color: Color,
}

impl ColorButtonStyleSheet {
    pub fn new(background_color: Color) -> iced::theme::Button {
        iced::theme::Button::Custom(Box::new(Self { background_color }))
    }
}

impl button::StyleSheet for ColorButtonStyleSheet {
    type Style = iced::Theme;

    fn active(&self, style: &Self::Style) -> button::Appearance {
        let background = iced::Background::Color(self.background_color);
        button::Appearance {
            background: Some(background),
            ..Default::default()
        }
    }

    fn hovered(&self, style: &Self::Style) -> button::Appearance {
        let active = self.active(style);
        let background = iced::Background::Color(Color::from_rgb(0.2, 0.2, 0.2)); // for testing
        button::Appearance {
            background: Some(background), // for testing
            shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), // todo: does not work
            ..active
        }
    }

    fn pressed(&self, style: &Self::Style) -> button::Appearance {
        button::Appearance {
            shadow_offset: Vector::default(),
            ..self.active(style)
        }
    }
}
#[derive(Default)]
pub struct CommonWidgetStyle {
    backgoud_color: Option<Color>,
}

impl From<CommonWidgetStyle> for iced::theme::Button {
    fn from(value: CommonWidgetStyle) -> Self {
        if value.backgoud_color.is_some() {
            let color = value
                .backgoud_color
                .unwrap_or(Color::from_rgb(0.1, 0.0, 0.0));
            let element = ColorButtonStyleSheet::new(color);
            element
        } else {
            let element = iced::theme::Button::Primary;
            element
        }
    }
}

Now I can do

            let biv = widget.button.view();
            let element: iced_widget::core::Element<Message, Theme, Renderer>;
            let style = ColorButtonStyleSheet::new(Color::from_rgb(0.5, 0.5, 0.5));
            let biv = biv.style(style);
            element = biv.into();

But this is not really satisfying, as it would be good if in the builder pattern style() (the overriden version) could be used to set some Elements of the style of the widget (e.g. color) and leave others as they have been set prior in the builder chain (usually some default).
For this either a different function / method would be necessary (IMHO not desirable) or style() itself could be in a trait to allow overriding it. Moreover in the self parameter the “current” style elements would be necessary to be extracted to copy them to the now XxStyleSheet (which I did not succed in finding out, supposedly this is why I can’t get the shaddow feature working for the hover).
What do you think ?

Or in fact (using CommonWidgetStyle, which actually was intended…)

            let biv = widget.button.view();
            let element: iced_widget::core::Element<Message, Theme, Renderer>;
            let style = CommonWidgetStyle {
                backgoud_color: Some(Color::from_rgb(0.8, 0.5, 0.2)),
            };
           let biv = biv.style(style);
            element = biv.into();