From 4a17d1fa468caf89e33088b3be69a67a52b29a99 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 19 Apr 2025 11:12:59 +0200 Subject: feat(material_theme): implement `slider::Catalog` --- crates/material_theme/src/toggler.rs | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 crates/material_theme/src/toggler.rs (limited to 'crates/material_theme/src/toggler.rs') diff --git a/crates/material_theme/src/toggler.rs b/crates/material_theme/src/toggler.rs new file mode 100644 index 0000000..1cc5082 --- /dev/null +++ b/crates/material_theme/src/toggler.rs @@ -0,0 +1,80 @@ +use iced_widget::core::Color; +use iced_widget::toggler::{Catalog, Status, Style, StyleFn}; + +use super::Theme; +use crate::utils::{ + DISABLED_CONTAINER_OPACITY, DISABLED_TEXT_OPACITY, HOVERED_LAYER_OPACITY, + mix, +}; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(default) + } + + fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { + class(self, status) + } +} + +pub fn styled( + background: Color, + foreground: Color, + border: Option, +) -> Style { + Style { + background, + background_border_width: if border.is_some() { 2.0 } else { 0.0 }, + background_border_color: border.unwrap_or(Color::TRANSPARENT), + foreground, + foreground_border_width: 0.0, + foreground_border_color: Color::TRANSPARENT, + } +} + +pub fn default(theme: &Theme, status: Status) -> Style { + let surface = theme.colorscheme.surface; + let primary = theme.colorscheme.primary; + + match status { + Status::Active { is_toggled } => { + if is_toggled { + styled(primary.color, primary.on_primary, None) + } else { + styled( + surface.surface_container.highest, + theme.colorscheme.outline.color, + Some(theme.colorscheme.outline.color), + ) + } + } + Status::Hovered { is_toggled } => { + if is_toggled { + styled(primary.color, primary.primary_container, None) + } else { + styled( + mix( + surface.surface_container.highest, + surface.on_surface, + HOVERED_LAYER_OPACITY, + ), + surface.on_surface_variant, + Some(theme.colorscheme.outline.color), + ) + } + } + Status::Disabled => styled( + Color { + a: DISABLED_CONTAINER_OPACITY, + ..surface.surface_container.highest + }, + Color { + a: DISABLED_TEXT_OPACITY, + ..surface.on_surface + }, + Some(surface.on_surface), + ), + } +} -- cgit v1.2.3 From 674d1bb1f50707c3473a675820f7c2dc5cbb2e15 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 22 Apr 2025 14:35:39 +0200 Subject: fix(material_theme): disabled checkbox border being fully opaque --- crates/material_theme/src/toggler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'crates/material_theme/src/toggler.rs') diff --git a/crates/material_theme/src/toggler.rs b/crates/material_theme/src/toggler.rs index 1cc5082..8949d07 100644 --- a/crates/material_theme/src/toggler.rs +++ b/crates/material_theme/src/toggler.rs @@ -74,7 +74,10 @@ pub fn default(theme: &Theme, status: Status) -> Style { a: DISABLED_TEXT_OPACITY, ..surface.on_surface }, - Some(surface.on_surface), + Some(Color { + a: DISABLED_TEXT_OPACITY, + ..surface.on_surface + }), ), } } -- cgit v1.2.3 From dac7e82e0bff128097653fe05829b2d576fcdb2f Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 28 Apr 2025 10:56:49 +0200 Subject: refactor(material_theme)!: make `Theme` an enum with `Custom` variant --- crates/material_theme/src/button.rs | 103 +++++++++----------------- crates/material_theme/src/checkbox.rs | 30 +++----- crates/material_theme/src/container.rs | 119 ++++++++++++++++++------------ crates/material_theme/src/dialog.rs | 4 +- crates/material_theme/src/image.rs | 8 +- crates/material_theme/src/lib.rs | 111 +++++++++++++++++++--------- crates/material_theme/src/menu.rs | 2 +- crates/material_theme/src/pane_grid.rs | 8 +- crates/material_theme/src/pick_list.rs | 2 +- crates/material_theme/src/progress_bar.rs | 4 +- crates/material_theme/src/qr_code.rs | 2 +- crates/material_theme/src/radio.rs | 11 +-- crates/material_theme/src/rule.rs | 4 +- crates/material_theme/src/scrollable.rs | 33 ++++----- crates/material_theme/src/slider.rs | 6 +- crates/material_theme/src/text.rs | 22 +++--- crates/material_theme/src/text_editor.rs | 38 +++------- crates/material_theme/src/text_input.rs | 38 +++------- crates/material_theme/src/toggler.rs | 28 +++---- crates/material_theme/src/utils.rs | 18 +++++ 20 files changed, 282 insertions(+), 309 deletions(-) (limited to 'crates/material_theme/src/toggler.rs') diff --git a/crates/material_theme/src/button.rs b/crates/material_theme/src/button.rs index e1369eb..3f16bc7 100644 --- a/crates/material_theme/src/button.rs +++ b/crates/material_theme/src/button.rs @@ -3,8 +3,8 @@ use iced_widget::core::{Background, Border, Color, border}; use crate::Theme; use crate::utils::{ - DISABLED_CONTAINER_OPACITY, DISABLED_TEXT_OPACITY, HOVERED_LAYER_OPACITY, - PRESSED_LAYER_OPACITY, elevation, mix, shadow_from_elevation, + HOVERED_LAYER_OPACITY, PRESSED_LAYER_OPACITY, disabled_container, + disabled_text, elevation, mix, shadow_from_elevation, }; impl Catalog for Theme { @@ -20,9 +20,8 @@ impl Catalog for Theme { } fn button( - foreground: Color, background: Color, - tone_overlay: Color, + foreground: Color, disabled: Color, shadow_color: Color, elevation_level: u8, @@ -40,7 +39,7 @@ fn button( Status::Pressed => Style { background: Some(Background::Color(mix( background, - tone_overlay, + foreground, HOVERED_LAYER_OPACITY, ))), ..active @@ -48,25 +47,18 @@ fn button( Status::Hovered => Style { background: Some(Background::Color(mix( background, - tone_overlay, + foreground, PRESSED_LAYER_OPACITY, ))), - text_color: foreground, - border: border::rounded(400), shadow: shadow_from_elevation( elevation(elevation_level + 1), shadow_color, ), + ..active }, Status::Disabled => Style { - background: Some(Background::Color(Color { - a: DISABLED_CONTAINER_OPACITY, - ..disabled - })), - text_color: Color { - a: DISABLED_TEXT_OPACITY, - ..disabled - }, + background: Some(Background::Color(disabled_container(disabled))), + text_color: disabled_text(disabled), border: border::rounded(400), ..Default::default() }, @@ -74,70 +66,46 @@ fn button( } pub fn elevated(theme: &Theme, status: Status) -> Style { - let surface_colors = theme.colorscheme.surface; + let surface = theme.colors().surface; - let foreground = theme.colorscheme.primary.color; - let background = surface_colors.surface_container.low; - let disabled = surface_colors.on_surface; + let foreground = theme.colors().primary.color; + let background = surface.surface_container.low; + let disabled = surface.on_surface; - let shadow_color = theme.colorscheme.shadow; + let shadow_color = theme.colors().shadow; - button( - foreground, - background, - foreground, - disabled, - shadow_color, - 1, - status, - ) + button(background, foreground, disabled, shadow_color, 1, status) } pub fn filled(theme: &Theme, status: Status) -> Style { - let primary_colors = theme.colorscheme.primary; + let primary = theme.colors().primary; - let foreground = primary_colors.on_primary; - let background = primary_colors.color; - let disabled = theme.colorscheme.surface.on_surface; + let foreground = primary.on_primary; + let background = primary.color; + let disabled = theme.colors().surface.on_surface; - let shadow_color = theme.colorscheme.shadow; + let shadow_color = theme.colors().shadow; - button( - foreground, - background, - foreground, - disabled, - shadow_color, - 0, - status, - ) + button(background, foreground, disabled, shadow_color, 0, status) } pub fn filled_tonal(theme: &Theme, status: Status) -> Style { - let secondary_colors = theme.colorscheme.secondary; + let secondary = theme.colors().secondary; - let foreground = secondary_colors.on_secondary_container; - let background = secondary_colors.secondary_container; - let disabled = theme.colorscheme.surface.on_surface; - let shadow_color = theme.colorscheme.shadow; + let foreground = secondary.on_secondary_container; + let background = secondary.secondary_container; + let disabled = theme.colors().surface.on_surface; + let shadow_color = theme.colors().shadow; - button( - foreground, - background, - foreground, - disabled, - shadow_color, - 0, - status, - ) + button(background, foreground, disabled, shadow_color, 0, status) } pub fn outlined(theme: &Theme, status: Status) -> Style { - let foreground = theme.colorscheme.primary.color; + let foreground = theme.colors().primary.color; let background = Color::TRANSPARENT; - let disabled = theme.colorscheme.surface.on_surface; + let disabled = theme.colors().surface.on_surface; - let outline = theme.colorscheme.outline.color; + let outline = theme.colors().outline.color; let border = match status { Status::Active | Status::Pressed | Status::Hovered => Border { @@ -146,17 +114,13 @@ pub fn outlined(theme: &Theme, status: Status) -> Style { radius: 400.into(), }, Status::Disabled => Border { - color: Color { - a: DISABLED_CONTAINER_OPACITY, - ..disabled - }, + color: disabled_container(disabled), width: 1.0, radius: 400.into(), }, }; let style = button( - foreground, background, foreground, disabled, @@ -169,12 +133,11 @@ pub fn outlined(theme: &Theme, status: Status) -> Style { } pub fn text(theme: &Theme, status: Status) -> Style { - let foreground = theme.colorscheme.primary.color; + let foreground = theme.colors().primary.color; let background = Color::TRANSPARENT; - let disabled = theme.colorscheme.surface.on_surface; + let disabled = theme.colors().surface.on_surface; let style = button( - foreground, background, foreground, disabled, @@ -185,7 +148,7 @@ pub fn text(theme: &Theme, status: Status) -> Style { match status { Status::Hovered | Status::Pressed => style, - _ => Style { + Status::Active | Status::Disabled => Style { background: None, ..style }, diff --git a/crates/material_theme/src/checkbox.rs b/crates/material_theme/src/checkbox.rs index ff038b0..7a3729c 100644 --- a/crates/material_theme/src/checkbox.rs +++ b/crates/material_theme/src/checkbox.rs @@ -2,7 +2,7 @@ use iced_widget::checkbox::{Catalog, Status, Style, StyleFn}; use iced_widget::core::{Background, Border, Color, border}; use super::Theme; -use crate::utils::{DISABLED_CONTAINER_OPACITY, HOVERED_LAYER_OPACITY, mix}; +use crate::utils::{HOVERED_LAYER_OPACITY, disabled_text, mix}; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; @@ -45,8 +45,8 @@ pub fn styled( } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let primary = theme.colorscheme.primary; + let surface = theme.colors().surface; + let primary = theme.colors().primary; match status { Status::Active { is_checked } => styled( @@ -69,16 +69,10 @@ pub fn default(theme: &Theme, status: Status) -> Style { is_checked, ), Status::Disabled { is_checked } => styled( - Color { - a: DISABLED_CONTAINER_OPACITY, - ..surface.on_surface - }, + disabled_text(surface.on_surface), None, surface.color, - Color { - a: DISABLED_CONTAINER_OPACITY, - ..surface.on_surface - }, + disabled_text(surface.on_surface), Some(surface.on_surface), is_checked, ), @@ -86,8 +80,8 @@ pub fn default(theme: &Theme, status: Status) -> Style { } pub fn error(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let error = theme.colorscheme.error; + let surface = theme.colors().surface; + let error = theme.colors().error; match status { Status::Active { is_checked } => styled( @@ -110,16 +104,10 @@ pub fn error(theme: &Theme, status: Status) -> Style { is_checked, ), Status::Disabled { is_checked } => styled( - Color { - a: DISABLED_CONTAINER_OPACITY, - ..surface.on_surface - }, + disabled_text(surface.on_surface), None, surface.color, - Color { - a: DISABLED_CONTAINER_OPACITY, - ..surface.on_surface - }, + disabled_text(surface.on_surface), Some(surface.on_surface), is_checked, ), diff --git a/crates/material_theme/src/container.rs b/crates/material_theme/src/container.rs index a14cfd5..5c253ad 100644 --- a/crates/material_theme/src/container.rs +++ b/crates/material_theme/src/container.rs @@ -1,5 +1,5 @@ use iced_widget::container::{Catalog, Style, StyleFn}; -use iced_widget::core::{Background, border}; +use iced_widget::core::{Background, Border, border}; use super::Theme; @@ -23,151 +23,178 @@ pub fn transparent(_theme: &Theme) -> Style { } pub fn primary(theme: &Theme) -> Style { - let colors = theme.colorscheme.primary; + let primary = theme.colors().primary; + Style { - background: Some(Background::Color(colors.color)), - text_color: Some(colors.on_primary), + background: Some(Background::Color(primary.color)), + text_color: Some(primary.on_primary), border: border::rounded(4), ..Style::default() } } pub fn primary_container(theme: &Theme) -> Style { - let colors = theme.colorscheme.primary; + let primary = theme.colors().primary; + Style { - background: Some(Background::Color(colors.primary_container)), - text_color: Some(colors.on_primary_container), + background: Some(Background::Color(primary.primary_container)), + text_color: Some(primary.on_primary_container), border: border::rounded(8), ..Style::default() } } pub fn secondary(theme: &Theme) -> Style { - let colors = theme.colorscheme.secondary; + let secondary = theme.colors().secondary; + Style { - background: Some(Background::Color(colors.color)), - text_color: Some(colors.on_secondary), + background: Some(Background::Color(secondary.color)), + text_color: Some(secondary.on_secondary), border: border::rounded(4), ..Style::default() } } pub fn secondary_container(theme: &Theme) -> Style { - let colors = theme.colorscheme.secondary; + let secondary = theme.colors().secondary; + Style { - background: Some(Background::Color(colors.secondary_container)), - text_color: Some(colors.on_secondary_container), + background: Some(Background::Color(secondary.secondary_container)), + text_color: Some(secondary.on_secondary_container), border: border::rounded(8), ..Style::default() } } pub fn tertiary(theme: &Theme) -> Style { - let colors = theme.colorscheme.tertiary; + let tertiary = theme.colors().tertiary; + Style { - background: Some(Background::Color(colors.color)), - text_color: Some(colors.on_tertiary), + background: Some(Background::Color(tertiary.color)), + text_color: Some(tertiary.on_tertiary), border: border::rounded(4), ..Style::default() } } pub fn tertiary_container(theme: &Theme) -> Style { - let colors = theme.colorscheme.tertiary; + let tertiary = theme.colors().tertiary; + Style { - background: Some(Background::Color(colors.tertiary_container)), - text_color: Some(colors.on_tertiary_container), + background: Some(Background::Color(tertiary.tertiary_container)), + text_color: Some(tertiary.on_tertiary_container), border: border::rounded(8), ..Style::default() } } pub fn error(theme: &Theme) -> Style { - let colors = theme.colorscheme.error; + let error = theme.colors().error; + Style { - background: Some(Background::Color(colors.color)), - text_color: Some(colors.on_error), + background: Some(Background::Color(error.color)), + text_color: Some(error.on_error), border: border::rounded(4), ..Style::default() } } pub fn error_container(theme: &Theme) -> Style { - let colors = theme.colorscheme.error; + let error = theme.colors().error; + Style { - background: Some(Background::Color(colors.error_container)), - text_color: Some(colors.on_error_container), + background: Some(Background::Color(error.error_container)), + text_color: Some(error.on_error_container), border: border::rounded(8), ..Style::default() } } pub fn surface(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; + Style { - background: Some(Background::Color(colors.color)), - text_color: Some(colors.on_surface), + background: Some(Background::Color(surface.color)), + text_color: Some(surface.on_surface), border: border::rounded(4), ..Style::default() } } pub fn surface_container_lowest(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; + Style { - background: Some(Background::Color(colors.surface_container.lowest)), - text_color: Some(colors.on_surface), + background: Some(Background::Color(surface.surface_container.lowest)), + text_color: Some(surface.on_surface), border: border::rounded(8), ..Style::default() } } pub fn surface_container_low(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; + Style { - background: Some(Background::Color(colors.surface_container.low)), - text_color: Some(colors.on_surface), + background: Some(Background::Color(surface.surface_container.low)), + text_color: Some(surface.on_surface), border: border::rounded(8), ..Style::default() } } pub fn surface_container(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; + Style { - background: Some(Background::Color(colors.surface_container.base)), - text_color: Some(colors.on_surface), + background: Some(Background::Color(surface.surface_container.base)), + text_color: Some(surface.on_surface), border: border::rounded(8), ..Style::default() } } pub fn surface_container_high(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; + Style { - background: Some(Background::Color(colors.surface_container.high)), - text_color: Some(colors.on_surface), + background: Some(Background::Color(surface.surface_container.high)), + text_color: Some(surface.on_surface), border: border::rounded(8), ..Style::default() } } pub fn surface_container_highest(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; + Style { - background: Some(Background::Color(colors.surface_container.highest)), - text_color: Some(colors.on_surface), + background: Some(Background::Color(surface.surface_container.highest)), + text_color: Some(surface.on_surface), border: border::rounded(8), ..Style::default() } } pub fn inverse_surface(theme: &Theme) -> Style { - let colors = theme.colorscheme.inverse; + let inverse = theme.colors().inverse; + Style { - background: Some(Background::Color(colors.inverse_surface)), - text_color: Some(colors.inverse_on_surface), + background: Some(Background::Color(inverse.inverse_surface)), + text_color: Some(inverse.inverse_on_surface), border: border::rounded(4), ..Style::default() } } + +pub fn outlined(theme: &Theme) -> Style { + let base = transparent(theme); + + Style { + border: Border { + color: theme.colors().outline.color, + ..base.border + }, + ..base + } +} diff --git a/crates/material_theme/src/dialog.rs b/crates/material_theme/src/dialog.rs index a022548..c839948 100644 --- a/crates/material_theme/src/dialog.rs +++ b/crates/material_theme/src/dialog.rs @@ -25,7 +25,7 @@ impl Catalog for Theme { } pub fn default_container(theme: &Theme) -> container::Style { - let colors = theme.colorscheme.surface; + let colors = theme.colors().surface; container::Style { background: Some(Background::Color(colors.surface_container.high)), text_color: Some(colors.on_surface_variant), @@ -36,6 +36,6 @@ pub fn default_container(theme: &Theme) -> container::Style { pub fn default(theme: &Theme) -> Style { Style { - backdrop_color: theme.colorscheme.scrim, + backdrop_color: theme.colors().scrim, } } diff --git a/crates/material_theme/src/image.rs b/crates/material_theme/src/image.rs index de5942a..4251b39 100644 --- a/crates/material_theme/src/image.rs +++ b/crates/material_theme/src/image.rs @@ -1,21 +1,15 @@ -use iced_widget::core::{Background, Border, Color, border}; use iced_widget::image::{Catalog, Style, StyleFn}; use super::Theme; -use crate::utils::{elevation, shadow_from_elevation}; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; fn default<'a>() -> Self::Class<'a> { - Box::new(default) + Box::new(|_theme| Style::default()) } fn style(&self, class: &Self::Class<'_>) -> Style { class(self) } } - -pub fn default(_theme: &Theme) -> Style { - Style::default() -} diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 1b4f90e..7b6ac58 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -2,6 +2,7 @@ use std::sync::LazyLock; use iced_widget::core::Color; use iced_widget::core::theme::{Base, Style}; +use utils::{lightness, mix}; pub mod button; pub mod checkbox; @@ -45,31 +46,47 @@ macro_rules! from_argb { }}; } -#[derive(Debug, Clone, Copy, PartialEq)] +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Theme { - pub name: &'static str, - #[cfg_attr(feature = "serde", serde(flatten))] - pub colorscheme: ColorScheme, +pub enum Theme { + Dark, + Light, + Custom(Custom), } impl Theme { - pub const ALL: &'static [Self] = &[Self::DARK, Self::LIGHT]; - - pub const DARK: Self = Self { - name: "Dark", - colorscheme: ColorScheme::DARK, - }; - - pub const LIGHT: Self = Self { - name: "Light", - colorscheme: ColorScheme::LIGHT, - }; + pub const ALL: &'static [Self] = &[Self::Dark, Self::Light]; pub fn new(name: impl Into, colorscheme: ColorScheme) -> Self { - Self { - name: Box::leak(name.into().into_boxed_str()), + Self::Custom(Custom { + name: name.into(), colorscheme, + is_dark: lightness(colorscheme.surface.color) <= 0.5, + }) + } + + pub fn name(&self) -> &str { + match self { + Self::Dark => "Dark", + Self::Light => "Light", + Self::Custom(custom) => &custom.name, + } + } + + pub fn colors(&self) -> ColorScheme { + match self { + Self::Dark => ColorScheme::DARK, + Self::Light => ColorScheme::LIGHT, + Self::Custom(custom) => custom.colorscheme, + } + } + + pub fn is_dark(&self) -> bool { + match self { + Self::Dark => true, + Self::Light => false, + Self::Custom(custom) => custom.is_dark, } } } @@ -80,43 +97,39 @@ impl Default for Theme { match dark_light::detect().unwrap_or(dark_light::Mode::Unspecified) { dark_light::Mode::Dark | dark_light::Mode::Unspecified => { - Theme::DARK + Theme::Dark } - dark_light::Mode::Light => Theme::LIGHT, + dark_light::Mode::Light => Theme::Light, } }); - *DEFAULT + DEFAULT.clone() } } impl std::fmt::Display for Theme { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) + write!(f, "{}", self.name()) } } impl Base for Theme { fn base(&self) -> Style { Style { - background_color: self.colorscheme.surface.color, - text_color: self.colorscheme.surface.on_surface, + background_color: self.colors().surface.color, + text_color: self.colors().surface.on_surface, } } fn palette(&self) -> Option { - let colors = self.colorscheme; + let colors = self.colors(); Some(iced_widget::theme::Palette { background: colors.surface.color, text: colors.surface.on_surface, primary: colors.primary.color, success: colors.primary.primary_container, - warning: utils::mix( - from_argb!(0xffffff00), - colors.primary.color, - 0.25, - ), + warning: mix(from_argb!(0xffffff00), colors.primary.color, 0.25), danger: colors.error.color, }) } @@ -129,18 +142,44 @@ impl iced_anim::Animate for Theme { } fn update(&mut self, components: &mut impl Iterator) { - self.colorscheme.update(components); - self.name = "Animating Theme"; + let mut colorscheme = self.colors(); + colorscheme.update(components); + *self = Self::new("Animating Theme", colorscheme); } fn distance_to(&self, end: &Self) -> Vec { - self.colorscheme.distance_to(&end.colorscheme) + self.colors().distance_to(&end.colors()) } fn lerp(&mut self, start: &Self, end: &Self, progress: f32) { - self.colorscheme - .lerp(&start.colorscheme, &end.colorscheme, progress); - self.name = "Animating Theme"; + let mut colorscheme = self.colors(); + colorscheme.lerp(&start.colors(), &end.colors(), progress); + *self = Self::new("Animating Theme", colorscheme); + } +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Custom { + pub name: String, + #[cfg_attr(feature = "serde", serde(flatten))] + pub colorscheme: ColorScheme, + pub is_dark: bool, +} + +impl Clone for Custom { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + colorscheme: self.colorscheme, + is_dark: self.is_dark, + } + } + + fn clone_from(&mut self, source: &Self) { + self.name.clone_from(&source.name); + self.colorscheme = source.colorscheme; + self.is_dark = source.is_dark; } } diff --git a/crates/material_theme/src/menu.rs b/crates/material_theme/src/menu.rs index 9f43c72..d595c2f 100644 --- a/crates/material_theme/src/menu.rs +++ b/crates/material_theme/src/menu.rs @@ -17,7 +17,7 @@ impl Catalog for Theme { } pub fn default(theme: &Theme) -> Style { - let colors = theme.colorscheme.surface; + let colors = theme.colors().surface; Style { border: border::rounded(4), diff --git a/crates/material_theme/src/pane_grid.rs b/crates/material_theme/src/pane_grid.rs index d66e475..fb69a32 100644 --- a/crates/material_theme/src/pane_grid.rs +++ b/crates/material_theme/src/pane_grid.rs @@ -20,18 +20,18 @@ pub fn default(theme: &Theme) -> Style { Style { hovered_region: Highlight { background: Background::Color(mix( - theme.colorscheme.tertiary.tertiary_container, - theme.colorscheme.surface.on_surface, + theme.colors().tertiary.tertiary_container, + theme.colors().surface.on_surface, HOVERED_LAYER_OPACITY, )), border: border::rounded(12), }, picked_split: Line { - color: theme.colorscheme.outline.variant, + color: theme.colors().outline.variant, width: 2.0, }, hovered_split: Line { - color: theme.colorscheme.surface.on_surface, + color: theme.colors().surface.on_surface, width: 6.0, }, } diff --git a/crates/material_theme/src/pick_list.rs b/crates/material_theme/src/pick_list.rs index 4b34871..1fe015e 100644 --- a/crates/material_theme/src/pick_list.rs +++ b/crates/material_theme/src/pick_list.rs @@ -20,7 +20,7 @@ impl Catalog for Theme { } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; + let surface = theme.colors().surface; let active = Style { text_color: surface.on_surface, diff --git a/crates/material_theme/src/progress_bar.rs b/crates/material_theme/src/progress_bar.rs index f686fff..9b4e844 100644 --- a/crates/material_theme/src/progress_bar.rs +++ b/crates/material_theme/src/progress_bar.rs @@ -18,9 +18,9 @@ impl Catalog for Theme { pub fn default(theme: &Theme) -> Style { Style { background: Background::Color( - theme.colorscheme.secondary.secondary_container, + theme.colors().secondary.secondary_container, ), - bar: Background::Color(theme.colorscheme.primary.color), + bar: Background::Color(theme.colors().primary.color), border: border::rounded(400), } } diff --git a/crates/material_theme/src/qr_code.rs b/crates/material_theme/src/qr_code.rs index f93fb77..f603440 100644 --- a/crates/material_theme/src/qr_code.rs +++ b/crates/material_theme/src/qr_code.rs @@ -15,7 +15,7 @@ impl Catalog for Theme { } pub fn default(theme: &Theme) -> Style { - let surface = theme.colorscheme.surface; + let surface = theme.colors().surface; Style { cell: surface.on_surface, diff --git a/crates/material_theme/src/radio.rs b/crates/material_theme/src/radio.rs index d249413..7fb7a3f 100644 --- a/crates/material_theme/src/radio.rs +++ b/crates/material_theme/src/radio.rs @@ -2,7 +2,7 @@ use iced_widget::core::{Background, Color}; use iced_widget::radio::{Catalog, Status, Style, StyleFn}; use super::Theme; -use crate::utils::{DISABLED_TEXT_OPACITY, HOVERED_LAYER_OPACITY, mix}; +use crate::utils::{HOVERED_LAYER_OPACITY, disabled_text, mix}; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; @@ -17,8 +17,8 @@ impl Catalog for Theme { } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let primary = theme.colorscheme.primary; + let surface = theme.colors().surface; + let primary = theme.colors().primary; let active = Style { background: Color::TRANSPARENT.into(), @@ -46,10 +46,7 @@ pub fn default(theme: &Theme, status: Status) -> Style { border_color: if is_selected { mix(primary.color, surface.on_surface, HOVERED_LAYER_OPACITY) } else { - Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - } + disabled_text(surface.on_surface) }, background: Background::Color(if is_selected { Color { diff --git a/crates/material_theme/src/rule.rs b/crates/material_theme/src/rule.rs index e433005..36e4dd4 100644 --- a/crates/material_theme/src/rule.rs +++ b/crates/material_theme/src/rule.rs @@ -17,7 +17,7 @@ impl Catalog for Theme { pub fn inset(theme: &Theme) -> Style { Style { - color: theme.colorscheme.outline.variant, + color: theme.colors().outline.variant, fill_mode: FillMode::Padded(8), width: 1, radius: Radius::default(), @@ -25,7 +25,7 @@ pub fn inset(theme: &Theme) -> Style { } pub fn full_width(theme: &Theme) -> Style { Style { - color: theme.colorscheme.outline.variant, + color: theme.colors().outline.variant, fill_mode: FillMode::Full, width: 1, radius: Radius::default(), diff --git a/crates/material_theme/src/scrollable.rs b/crates/material_theme/src/scrollable.rs index ee739ba..341f047 100644 --- a/crates/material_theme/src/scrollable.rs +++ b/crates/material_theme/src/scrollable.rs @@ -1,4 +1,4 @@ -use iced_widget::core::{Border, Color, border}; +use iced_widget::core::{Background, Border, border}; use iced_widget::scrollable::{ Catalog, Rail, Scroller, Status, Style, StyleFn, }; @@ -7,8 +7,8 @@ use super::Theme; use super::container::surface_container; use super::utils::mix; use crate::utils::{ - DISABLED_CONTAINER_OPACITY, DISABLED_TEXT_OPACITY, HOVERED_LAYER_OPACITY, - PRESSED_LAYER_OPACITY, + HOVERED_LAYER_OPACITY, PRESSED_LAYER_OPACITY, disabled_container, + disabled_text, }; impl Catalog for Theme { @@ -24,30 +24,23 @@ impl Catalog for Theme { } pub fn default(theme: &Theme, status: Status) -> Style { - let colors = theme.colorscheme.surface; + let surface = theme.colors().surface; let active = Rail { background: None, scroller: Scroller { - color: colors.on_surface, + color: surface.on_surface, border: border::rounded(400), }, border: Border::default(), }; let disabled = Rail { - background: Some( - Color { - a: DISABLED_CONTAINER_OPACITY, - ..colors.on_surface - } - .into(), - ), + background: Some(Background::Color(disabled_container( + surface.on_surface, + ))), scroller: Scroller { - color: Color { - a: DISABLED_TEXT_OPACITY, - ..colors.on_surface - }, + color: disabled_text(surface.on_surface), border: border::rounded(400), }, ..active @@ -86,8 +79,8 @@ pub fn default(theme: &Theme, status: Status) -> Style { let hovered_rail = Rail { scroller: Scroller { color: mix( - colors.on_surface, - colors.color, + surface.on_surface, + surface.color, HOVERED_LAYER_OPACITY, ), border: border::rounded(400), @@ -122,8 +115,8 @@ pub fn default(theme: &Theme, status: Status) -> Style { let dragged_rail = Rail { scroller: Scroller { color: mix( - colors.on_surface, - colors.color, + surface.on_surface, + surface.color, PRESSED_LAYER_OPACITY, ), border: border::rounded(400), diff --git a/crates/material_theme/src/slider.rs b/crates/material_theme/src/slider.rs index 8665459..ae9ee4b 100644 --- a/crates/material_theme/src/slider.rs +++ b/crates/material_theme/src/slider.rs @@ -41,9 +41,9 @@ pub fn styled(left: Color, right: Color, handle_radius: f32) -> Style { } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let primary = theme.colorscheme.primary; - let secondary = theme.colorscheme.secondary; + let surface = theme.colors().surface; + let primary = theme.colors().primary; + let secondary = theme.colors().secondary; match status { Status::Active => { diff --git a/crates/material_theme/src/text.rs b/crates/material_theme/src/text.rs index 10b2e65..8da3cdf 100644 --- a/crates/material_theme/src/text.rs +++ b/crates/material_theme/src/text.rs @@ -21,66 +21,66 @@ pub fn none(_: &Theme) -> Style { pub fn primary(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.primary.on_primary), + color: Some(theme.colors().primary.on_primary), } } pub fn primary_container(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.primary.on_primary_container), + color: Some(theme.colors().primary.on_primary_container), } } pub fn secondary(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.secondary.on_secondary), + color: Some(theme.colors().secondary.on_secondary), } } pub fn secondary_container(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.secondary.on_secondary_container), + color: Some(theme.colors().secondary.on_secondary_container), } } pub fn tertiary(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.tertiary.on_tertiary), + color: Some(theme.colors().tertiary.on_tertiary), } } pub fn tertiary_container(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.tertiary.on_tertiary_container), + color: Some(theme.colors().tertiary.on_tertiary_container), } } pub fn error(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.error.on_error), + color: Some(theme.colors().error.on_error), } } pub fn error_container(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.error.on_error_container), + color: Some(theme.colors().error.on_error_container), } } pub fn surface(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.surface.on_surface), + color: Some(theme.colors().surface.on_surface), } } pub fn surface_variant(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.surface.on_surface_variant), + color: Some(theme.colors().surface.on_surface_variant), } } pub fn inverse_surface(theme: &Theme) -> Style { Style { - color: Some(theme.colorscheme.inverse.inverse_on_surface), + color: Some(theme.colors().inverse.inverse_on_surface), } } diff --git a/crates/material_theme/src/text_editor.rs b/crates/material_theme/src/text_editor.rs index daad7d2..14d7104 100644 --- a/crates/material_theme/src/text_editor.rs +++ b/crates/material_theme/src/text_editor.rs @@ -2,7 +2,7 @@ use iced_widget::core::{Background, Border, Color, border}; use iced_widget::text_editor::{Catalog, Status, Style, StyleFn}; use super::Theme; -use crate::utils::DISABLED_TEXT_OPACITY; +use crate::utils::{disabled_container, disabled_text}; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; @@ -17,23 +17,20 @@ impl Catalog for Theme { } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let primary = theme.colorscheme.primary; + let surface = theme.colors().surface; + let primary = theme.colors().primary; let active = Style { background: Background::Color(surface.surface_container.highest), border: Border { - color: theme.colorscheme.outline.color, + color: theme.colors().outline.color, width: 1.0, radius: 4.into(), }, icon: surface.on_surface_variant, placeholder: surface.on_surface_variant, value: surface.on_surface, - selection: Color { - a: DISABLED_TEXT_OPACITY, - ..primary.color - }, + selection: disabled_text(primary.color), }; match status { @@ -57,29 +54,14 @@ pub fn default(theme: &Theme, status: Status) -> Style { Status::Disabled => Style { background: Color::TRANSPARENT.into(), border: Border { - color: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, + color: disabled_container(surface.on_surface), width: 1.0, radius: border::radius(4), }, - icon: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - placeholder: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - selection: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - value: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, + icon: disabled_text(surface.on_surface), + placeholder: disabled_text(surface.on_surface), + value: disabled_text(surface.on_surface), + selection: disabled_text(surface.on_surface), }, } } diff --git a/crates/material_theme/src/text_input.rs b/crates/material_theme/src/text_input.rs index 5de5993..ed5e02a 100644 --- a/crates/material_theme/src/text_input.rs +++ b/crates/material_theme/src/text_input.rs @@ -2,7 +2,7 @@ use iced_widget::core::{Background, Border, Color}; use iced_widget::text_input::{Catalog, Status, Style, StyleFn}; use super::Theme; -use crate::utils::{DISABLED_CONTAINER_OPACITY, DISABLED_TEXT_OPACITY}; +use crate::utils::{disabled_container, disabled_text}; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; @@ -17,23 +17,20 @@ impl Catalog for Theme { } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let primary = theme.colorscheme.primary; + let surface = theme.colors().surface; + let primary = theme.colors().primary; let active = Style { background: Background::Color(surface.surface_container.highest), border: Border { - color: theme.colorscheme.outline.color, + color: theme.colors().outline.color, width: 1.0, radius: 4.into(), }, icon: surface.on_surface_variant, placeholder: surface.on_surface_variant, value: surface.on_surface, - selection: Color { - a: DISABLED_TEXT_OPACITY, - ..primary.color - }, + selection: disabled_text(primary.color), }; match status { @@ -48,28 +45,13 @@ pub fn default(theme: &Theme, status: Status) -> Style { Status::Disabled => Style { background: Color::TRANSPARENT.into(), border: Border { - color: Color { - a: DISABLED_CONTAINER_OPACITY, - ..surface.on_surface - }, + color: disabled_container(surface.on_surface), ..active.border }, - icon: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - placeholder: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - value: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - selection: Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, + icon: disabled_text(surface.on_surface), + placeholder: disabled_text(surface.on_surface), + value: disabled_text(surface.on_surface), + selection: disabled_text(surface.on_surface), }, Status::Focused { .. } => Style { border: Border { diff --git a/crates/material_theme/src/toggler.rs b/crates/material_theme/src/toggler.rs index 8949d07..5cebc88 100644 --- a/crates/material_theme/src/toggler.rs +++ b/crates/material_theme/src/toggler.rs @@ -3,8 +3,7 @@ use iced_widget::toggler::{Catalog, Status, Style, StyleFn}; use super::Theme; use crate::utils::{ - DISABLED_CONTAINER_OPACITY, DISABLED_TEXT_OPACITY, HOVERED_LAYER_OPACITY, - mix, + HOVERED_LAYER_OPACITY, disabled_container, disabled_text, mix, }; impl Catalog for Theme { @@ -35,8 +34,8 @@ pub fn styled( } pub fn default(theme: &Theme, status: Status) -> Style { - let surface = theme.colorscheme.surface; - let primary = theme.colorscheme.primary; + let surface = theme.colors().surface; + let primary = theme.colors().primary; match status { Status::Active { is_toggled } => { @@ -45,8 +44,8 @@ pub fn default(theme: &Theme, status: Status) -> Style { } else { styled( surface.surface_container.highest, - theme.colorscheme.outline.color, - Some(theme.colorscheme.outline.color), + theme.colors().outline.color, + Some(theme.colors().outline.color), ) } } @@ -61,23 +60,14 @@ pub fn default(theme: &Theme, status: Status) -> Style { HOVERED_LAYER_OPACITY, ), surface.on_surface_variant, - Some(theme.colorscheme.outline.color), + Some(theme.colors().outline.color), ) } } Status::Disabled => styled( - Color { - a: DISABLED_CONTAINER_OPACITY, - ..surface.surface_container.highest - }, - Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }, - Some(Color { - a: DISABLED_TEXT_OPACITY, - ..surface.on_surface - }), + disabled_container(surface.surface_container.highest), + disabled_text(surface.on_surface), + Some(disabled_text(surface.on_surface)), ), } } diff --git a/crates/material_theme/src/utils.rs b/crates/material_theme/src/utils.rs index 5ad137e..963c9fc 100644 --- a/crates/material_theme/src/utils.rs +++ b/crates/material_theme/src/utils.rs @@ -30,6 +30,20 @@ pub fn shadow_from_elevation(elevation: f32, color: Color) -> Shadow { } } +pub fn disabled_text(color: Color) -> Color { + Color { + a: DISABLED_TEXT_OPACITY, + ..color + } +} + +pub fn disabled_container(color: Color) -> Color { + Color { + a: DISABLED_CONTAINER_OPACITY, + ..color + } +} + pub fn parse_argb(s: &str) -> Option { let hex = s.strip_prefix('#').unwrap_or(s); @@ -88,6 +102,10 @@ pub fn color_to_argb(color: Color) -> String { hex } +pub const fn lightness(color: Color) -> f32 { + color.r * 0.299 + color.g * 0.587 + color.b * 0.114 +} + pub fn mix(color1: Color, color2: Color, p2: f32) -> Color { if p2 <= 0.0 { return color1; -- cgit v1.2.3