From 29326215ccf13e1d1e25bf3bf5ada007856bff69 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 5 Mar 2024 03:48:08 +0100 Subject: Simplify theming for `Container` widget --- widget/src/container.rs | 118 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 15 deletions(-) (limited to 'widget/src/container.rs') diff --git a/widget/src/container.rs b/widget/src/container.rs index e0174177..66e80820 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -8,12 +8,11 @@ use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::widget::{self, Operation}; use crate::core::{ - Background, Clipboard, Color, Element, Layout, Length, Padding, Pixels, - Point, Rectangle, Shell, Size, Vector, Widget, + Background, Border, Clipboard, Color, Element, Layout, Length, Padding, + Pixels, Point, Rectangle, Shadow, Shell, Size, Vector, Widget, }; use crate::runtime::Command; - -pub use iced_style::container::{Appearance, StyleSheet}; +use crate::style::Theme; /// An element decorating some content. /// @@ -25,7 +24,6 @@ pub struct Container< Theme = crate::Theme, Renderer = crate::Renderer, > where - Theme: StyleSheet, Renderer: crate::core::Renderer, { id: Option, @@ -36,19 +34,19 @@ pub struct Container< max_height: f32, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - style: Theme::Style, + style: fn(&Theme, Status) -> Appearance, clip: bool, content: Element<'a, Message, Theme, Renderer>, } impl<'a, Message, Theme, Renderer> Container<'a, Message, Theme, Renderer> where - Theme: StyleSheet, Renderer: crate::core::Renderer, { /// Creates an empty [`Container`]. pub fn new(content: T) -> Self where + Theme: Style, T: Into>, { let content = content.into(); @@ -63,7 +61,7 @@ where max_height: f32::INFINITY, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - style: Default::default(), + style: Theme::default(), clip: false, content, } @@ -130,8 +128,8 @@ where } /// Sets the style of the [`Container`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { + self.style = style; self } @@ -146,7 +144,6 @@ where impl<'a, Message, Theme, Renderer> Widget for Container<'a, Message, Theme, Renderer> where - Theme: StyleSheet, Renderer: crate::core::Renderer, { fn tag(&self) -> tree::Tag { @@ -262,10 +259,18 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - let style = theme.appearance(&self.style); + let bounds = layout.bounds(); + + let status = if cursor.is_over(bounds) { + Status::Hovered + } else { + Status::Idle + }; - if let Some(clipped_viewport) = layout.bounds().intersection(viewport) { - draw_background(renderer, &style, layout.bounds()); + let style = (self.style)(theme, status); + + if let Some(clipped_viewport) = bounds.intersection(viewport) { + draw_background(renderer, &style, bounds); self.content.as_widget().draw( tree, @@ -307,7 +312,7 @@ impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, - Theme: 'a + StyleSheet, + Theme: 'a, Renderer: 'a + crate::core::Renderer, { fn from( @@ -482,3 +487,86 @@ pub fn visible_bounds(id: Id) -> Command> { bounds: None, }) } + +/// The appearance of a container. +#[derive(Debug, Clone, Copy, Default)] +pub struct Appearance { + /// The text [`Color`] of the container. + pub text_color: Option, + /// The [`Background`] of the container. + pub background: Option, + /// The [`Border`] of the container. + pub border: Border, + /// The [`Shadow`] of the container. + pub shadow: Shadow, +} + +impl Appearance { + /// Derives a new [`Appearance`] with a border of the given [`Color`] and + /// `width`. + pub fn with_border( + self, + color: impl Into, + width: impl Into, + ) -> Self { + Self { + border: Border { + color: color.into(), + width: width.into().0, + ..Border::default() + }, + ..self + } + } + + /// Derives a new [`Appearance`] with the given [`Background`]. + pub fn with_background(self, background: impl Into) -> Self { + Self { + background: Some(background.into()), + ..self + } + } +} + +/// The possible status of a [`Container`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Status { + /// The [`Container`] is idle. + Idle, + /// The [`Container`] is being hovered. + Hovered, +} + +/// The style of a [`Container`] for a theme. +pub trait Style { + /// The default style of a [`Container`]. + fn default() -> fn(&Self, Status) -> Appearance; +} + +impl Style for Theme { + fn default() -> fn(&Self, Status) -> Appearance { + transparent + } +} + +impl Style for Appearance { + fn default() -> fn(&Self, Status) -> Appearance { + |appearance, _status| *appearance + } +} + +/// A transparent [`Container`]. +pub fn transparent(_theme: &Theme, _status: Status) -> Appearance { + ::default() +} + +/// A rounded [`Container`] with a background. +pub fn box_(theme: &Theme, _status: Status) -> Appearance { + let palette = theme.extended_palette(); + + Appearance { + background: Some(palette.background.weak.color.into()), + border: Border::with_radius(2), + ..::default() + } +} -- cgit From 597a41cea73f078eda04eb3ff40cfda5d37d6135 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 6 Mar 2024 17:08:28 +0100 Subject: Simplify theming for `PickList`, `ComboBox`, and `Menu` widgets --- widget/src/container.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'widget/src/container.rs') diff --git a/widget/src/container.rs b/widget/src/container.rs index 66e80820..58a24339 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -61,7 +61,7 @@ where max_height: f32::INFINITY, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - style: Theme::default(), + style: Theme::style(), clip: false, content, } @@ -540,24 +540,24 @@ pub enum Status { /// The style of a [`Container`] for a theme. pub trait Style { /// The default style of a [`Container`]. - fn default() -> fn(&Self, Status) -> Appearance; + fn style() -> fn(&Self, Status) -> Appearance; } impl Style for Theme { - fn default() -> fn(&Self, Status) -> Appearance { + fn style() -> fn(&Self, Status) -> Appearance { transparent } } impl Style for Appearance { - fn default() -> fn(&Self, Status) -> Appearance { + fn style() -> fn(&Self, Status) -> Appearance { |appearance, _status| *appearance } } /// A transparent [`Container`]. pub fn transparent(_theme: &Theme, _status: Status) -> Appearance { - ::default() + Appearance::default() } /// A rounded [`Container`] with a background. @@ -567,6 +567,21 @@ pub fn box_(theme: &Theme, _status: Status) -> Appearance { Appearance { background: Some(palette.background.weak.color.into()), border: Border::with_radius(2), - ..::default() + ..Appearance::default() + } +} + +/// A bordered [`Container`] with a background. +pub fn bordered_box(theme: &Theme, _status: Status) -> Appearance { + let palette = theme.extended_palette(); + + Appearance { + background: Some(palette.background.weak.color.into()), + border: Border { + width: 1.0, + radius: 0.0.into(), + color: palette.background.strong.color, + }, + ..Appearance::default() } } -- cgit From 34e7c6593a9e0f56cee5db18b7258717cf6bc11b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 6 Mar 2024 20:30:58 +0100 Subject: Use `Style` struct pattern instead of trait for all widgets --- widget/src/container.rs | 67 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 19 deletions(-) (limited to 'widget/src/container.rs') diff --git a/widget/src/container.rs b/widget/src/container.rs index 58a24339..97b481a2 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -34,21 +34,30 @@ pub struct Container< max_height: f32, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - style: fn(&Theme, Status) -> Appearance, clip: bool, content: Element<'a, Message, Theme, Renderer>, + style: Style, } impl<'a, Message, Theme, Renderer> Container<'a, Message, Theme, Renderer> where Renderer: crate::core::Renderer, { - /// Creates an empty [`Container`]. - pub fn new(content: T) -> Self + /// Creates a [`Container`] with the given content. + pub fn new( + content: impl Into>, + ) -> Self where - Theme: Style, - T: Into>, + Style: Default, { + Self::with_style(content, Style::default().0) + } + + /// Creates a [`Container`] with the given content and style. + pub fn with_style( + content: impl Into>, + style: fn(&Theme, Status) -> Appearance, + ) -> Self { let content = content.into(); let size = content.as_widget().size_hint(); @@ -61,9 +70,9 @@ where max_height: f32::INFINITY, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - style: Theme::style(), clip: false, content, + style: Style(style), } } @@ -129,7 +138,7 @@ where /// Sets the style of the [`Container`]. pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { - self.style = style; + self.style = Style(style); self } @@ -267,7 +276,7 @@ where Status::Idle }; - let style = (self.style)(theme, status); + let style = (self.style.0)(theme, status); if let Some(clipped_viewport) = bounds.intersection(viewport) { draw_background(renderer, &style, bounds); @@ -537,26 +546,46 @@ pub enum Status { Hovered, } -/// The style of a [`Container`] for a theme. -pub trait Style { - /// The default style of a [`Container`]. - fn style() -> fn(&Self, Status) -> Appearance; +/// The style of a [`Container`]. +#[derive(Debug, PartialEq, Eq)] +pub struct Style(fn(&Theme, Status) -> Appearance); + +impl Style { + /// Resolves the [`Style`] with the given `Theme` and [`Status`] to + /// produce an [`Appearance`]. + pub fn resolve(self, theme: &Theme, status: Status) -> Appearance { + (self.0)(theme, status) + } +} + +impl Clone for Style { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Style {} + +impl Default for Style { + fn default() -> Self { + Style(transparent) + } } -impl Style for Theme { - fn style() -> fn(&Self, Status) -> Appearance { - transparent +impl Default for Style { + fn default() -> Self { + Style(|appearance, _status| *appearance) } } -impl Style for Appearance { - fn style() -> fn(&Self, Status) -> Appearance { - |appearance, _status| *appearance +impl From Appearance> for Style { + fn from(f: fn(&Theme, Status) -> Appearance) -> Self { + Style(f) } } /// A transparent [`Container`]. -pub fn transparent(_theme: &Theme, _status: Status) -> Appearance { +pub fn transparent(_theme: &Theme, _status: Status) -> Appearance { Appearance::default() } -- cgit From 905f2160e6eb7504f52d9bd62c7bfa42c8ec2902 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 7 Mar 2024 00:14:41 +0100 Subject: Move `Theme` type to `iced_core` --- widget/src/container.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'widget/src/container.rs') diff --git a/widget/src/container.rs b/widget/src/container.rs index 97b481a2..99d877fe 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -9,10 +9,9 @@ use crate::core::widget::tree::{self, Tree}; use crate::core::widget::{self, Operation}; use crate::core::{ Background, Border, Clipboard, Color, Element, Layout, Length, Padding, - Pixels, Point, Rectangle, Shadow, Shell, Size, Vector, Widget, + Pixels, Point, Rectangle, Shadow, Shell, Size, Theme, Vector, Widget, }; use crate::runtime::Command; -use crate::style::Theme; /// An element decorating some content. /// -- cgit From 833538ee7f3a60a839304762dfc29b0881d19094 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 7 Mar 2024 20:11:32 +0100 Subject: Leverage `DefaultStyle` traits instead of `Default` --- widget/src/container.rs | 49 ++++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) (limited to 'widget/src/container.rs') diff --git a/widget/src/container.rs b/widget/src/container.rs index 99d877fe..5e16312c 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -47,9 +47,9 @@ where content: impl Into>, ) -> Self where - Style: Default, + Theme: DefaultStyle, { - Self::with_style(content, Style::default().0) + Self::with_style(content, Theme::default_style()) } /// Creates a [`Container`] with the given content and style. @@ -71,7 +71,7 @@ where vertical_alignment: alignment::Vertical::Top, clip: false, content, - style: Style(style), + style, } } @@ -137,7 +137,7 @@ where /// Sets the style of the [`Container`]. pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { - self.style = Style(style); + self.style = style; self } @@ -275,7 +275,7 @@ where Status::Idle }; - let style = (self.style.0)(theme, status); + let style = (self.style)(theme, status); if let Some(clipped_viewport) = bounds.intersection(viewport) { draw_background(renderer, &style, bounds); @@ -546,40 +546,23 @@ pub enum Status { } /// The style of a [`Container`]. -#[derive(Debug, PartialEq, Eq)] -pub struct Style(fn(&Theme, Status) -> Appearance); +pub type Style = fn(&Theme, Status) -> Appearance; -impl Style { - /// Resolves the [`Style`] with the given `Theme` and [`Status`] to - /// produce an [`Appearance`]. - pub fn resolve(self, theme: &Theme, status: Status) -> Appearance { - (self.0)(theme, status) - } -} - -impl Clone for Style { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for Style {} - -impl Default for Style { - fn default() -> Self { - Style(transparent) - } +/// The default style of a [`Container`]. +pub trait DefaultStyle { + /// Returns the default style of a [`Container`]. + fn default_style() -> Style; } -impl Default for Style { - fn default() -> Self { - Style(|appearance, _status| *appearance) +impl DefaultStyle for Theme { + fn default_style() -> Style { + transparent } } -impl From Appearance> for Style { - fn from(f: fn(&Theme, Status) -> Appearance) -> Self { - Style(f) +impl DefaultStyle for Appearance { + fn default_style() -> Style { + |appearance, _status| *appearance } } -- cgit From 7ece5eea509f3595432babfc7729701f2e063b21 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 7 Mar 2024 21:02:17 +0100 Subject: Implement additional helpers for `Border` and `container::Appearance` --- widget/src/container.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'widget/src/container.rs') diff --git a/widget/src/container.rs b/widget/src/container.rs index 5e16312c..81b9a29e 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -1,6 +1,7 @@ //! Decorate content and apply alignment. use crate::core::alignment::{self, Alignment}; use crate::core::event::{self, Event}; +use crate::core::gradient::{self, Gradient}; use crate::core::layout; use crate::core::mouse; use crate::core::overlay; @@ -510,8 +511,7 @@ pub struct Appearance { } impl Appearance { - /// Derives a new [`Appearance`] with a border of the given [`Color`] and - /// `width`. + /// Updates the border of the [`Appearance`] with the given [`Color`] and `width`. pub fn with_border( self, color: impl Into, @@ -527,7 +527,7 @@ impl Appearance { } } - /// Derives a new [`Appearance`] with the given [`Background`]. + /// Updates the background of the [`Appearance`]. pub fn with_background(self, background: impl Into) -> Self { Self { background: Some(background.into()), @@ -566,6 +566,24 @@ impl DefaultStyle for Appearance { } } +impl DefaultStyle for Color { + fn default_style() -> Style { + |color, _status| Appearance::default().with_background(*color) + } +} + +impl DefaultStyle for Gradient { + fn default_style() -> Style { + |gradient, _status| Appearance::default().with_background(*gradient) + } +} + +impl DefaultStyle for gradient::Linear { + fn default_style() -> Style { + |gradient, _status| Appearance::default().with_background(*gradient) + } +} + /// A transparent [`Container`]. pub fn transparent(_theme: &Theme, _status: Status) -> Appearance { Appearance::default() @@ -577,7 +595,7 @@ pub fn box_(theme: &Theme, _status: Status) -> Appearance { Appearance { background: Some(palette.background.weak.color.into()), - border: Border::with_radius(2), + border: Border::rounded(2), ..Appearance::default() } } -- cgit