Seting the background color of a widget

I wanted to change the background color of a container and, using the twentyone project chapter 5 as a starting point, I was able to get it to work.

However, it would be much more intuitive if you could just write ‘container.set_background_color(color)’ or more generally 'widget.set_<some_attribute(attribute_value)> so I came up with this code:

use iced::widget::{container, row, text};

use iced::{Background, Color, Element, Sandbox, Settings};
use iced::theme::Container;

#[derive(Default)]
pub struct BackgroundColor {
    color: Color,
}

impl BackgroundColor {
    const fn new(color: Color) -> Self {
        Self { color: color }
    }

    const RED:    Color = Color::from_rgb(1.0, 0.0, 0.0);
    const GREEN:  Color = Color::from_rgb(0.0, 1.0, 0.0);
    const BLUE:   Color = Color::from_rgb(0.0, 0.0, 1.0);
    const CYAN:   Color = Color::from_rgb(0.0, 1.0, 1.0);
    const YELLOW: Color = Color::from_rgb(1.0, 1.0, 0.0);
}

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

    fn appearance(&self, _style: &Self::Style) -> container::Appearance {
        container::Appearance {
            background: Some(Background::from(self.color)),
            ..Default::default()
        }
    }
}

/////////////////////////////////////////////
pub trait SetAppearance {
    fn set_background(self, color: Color) -> Self;
}

impl SetAppearance for container::Container<'_, Message> {
    fn set_background(self, color: Color) -> Self {
        self.style(Container::Custom(Box::new(BackgroundColor::new(color))))
    }
}
/////////////////////////////////////////////

#[derive(Debug)]
enum Message {}

struct SimGui;

fn main() -> iced::Result {
    SimGui::run(Settings::default())
}

impl Sandbox for SimGui {
    type Message = Message;

    fn new() -> Self {
        Self
    }

    fn title(&self) -> String {
        String::from("SimGui")
    }

    fn update(&mut self, message: Message) {
        match message {}
    }

    fn view(&self) ->Element<'_, Message> {
        let t1 = text("This Should have a red background");
        let c1 = container(t1)
                    .set_background(BackgroundColor::RED)
                    .padding(10);

        let t2 = text("This should have a green background");
        let c2 = container(t2)
                    .set_background(BackgroundColor::GREEN)
                    .padding(10);

        let t3 = text("This should have a blue background");
        let c3 = container(t3)
                    .set_background(BackgroundColor::BLUE)
                    .padding(10);

        row![c1,c2,c3].into()
    } 
}

which works but there is a problem in that if I wanted to add another function to the SetAppearance trait, say, set_border(), and chain set_background() and set_border(), then the second call would negate the first one because of the use of …Default::default() in the appearance() function.
What I really need to do is get the current Appearance of the widget and modify it rather than creating a new one but I could not find a way to do that.

So my first question is: is there a way to get the Appearance that a widget is displayed with?

The second thing that I wanted to do is move the BackgroundColor and SetAppearance into another file but I was not able to get it to work. Message is defined in the main.rs but it is needed by SetAppearance.

1 Like
use iced::{gradient, widget::container, Border, Color, Radians};

pub struct CustomerContainer;
impl container::StyleSheet for CustomerContainer {
    type Style = iced::Theme;

    fn appearance(&self, style: &Self::Style) -> container::Appearance {
        let palette = style.palette();
        let bg = palette.danger;
        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)),
            border: Border {
                color: Color::TRANSPARENT,
                width: 0.0,
                radius: 8.0.into(),
            },
            ..Default::default()
        }
    }
}

pub fn get_custom_container_style() -> iced::theme::Container {
    iced::theme::Container::Custom(Box::new(CustomerContainer))
}

And later when you want to change background of container you just call the get_custom fn on style. like :

            .style(container_style::get_custom_container_style()),

which version of iced are you using? mine is 0.10.0 and does not include border.rs module :thinking: