From 495985f449e46b24e6b734d3aa9e135a779a8b77 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 13 Apr 2025 03:40:38 +0200 Subject: refactor: move `material_theme` and `iced_drop` into separate crates dir --- crates/material_theme/src/lib.rs | 248 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 crates/material_theme/src/lib.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs new file mode 100644 index 0000000..521af2c --- /dev/null +++ b/crates/material_theme/src/lib.rs @@ -0,0 +1,248 @@ +use std::sync::LazyLock; + +use iced_widget::core::Color; +use iced_widget::core::theme::{Base, Style}; +use serde::Deserialize; + +pub mod button; +pub mod container; +#[cfg(feature = "dialog")] +pub mod dialog; +pub mod menu; +pub mod pick_list; +pub mod scrollable; +pub mod text; +pub mod utils; + +const DARK_THEME_CONTENT: &str = include_str!("../assets/themes/dark.toml"); +const LIGHT_THEME_CONTENT: &str = include_str!("../assets/themes/light.toml"); + +#[derive(Debug, PartialEq, Deserialize)] +pub struct Theme { + pub name: String, + #[serde(flatten)] + pub colorscheme: ColorScheme, +} + +impl Theme { + pub fn new(name: impl Into, colorscheme: ColorScheme) -> Self { + Self { + name: name.into(), + colorscheme, + } + } +} + +impl Clone for Theme { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + colorscheme: self.colorscheme, + } + } + + fn clone_from(&mut self, source: &Self) { + self.name = source.name.clone(); + self.colorscheme = source.colorscheme; + } +} + +impl Default for Theme { + fn default() -> Self { + static DEFAULT: LazyLock = LazyLock::new(|| { + match dark_light::detect().unwrap_or(dark_light::Mode::Unspecified) + { + dark_light::Mode::Dark | dark_light::Mode::Unspecified => { + DARK.clone() + } + dark_light::Mode::Light => LIGHT.clone(), + } + }); + + DEFAULT.clone() + } +} + +impl std::fmt::Display for Theme { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + 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, + } + } + + fn palette(&self) -> Option { + // TODO: create a Palette + None + } +} + +#[cfg(feature = "animate")] +impl iced_anim::Animate for Theme { + fn components() -> usize { + ColorScheme::components() + } + + fn update(&mut self, components: &mut impl Iterator) { + let mut colors = self.colorscheme; + colors.update(components); + + *self = Theme::new("Animating Theme", colors); + } + + fn distance_to(&self, end: &Self) -> Vec { + self.colorscheme.distance_to(&end.colorscheme) + } + + fn lerp(&mut self, start: &Self, end: &Self, progress: f32) { + let mut colors = self.colorscheme; + colors.lerp(&start.colorscheme, &end.colorscheme, progress); + + *self = Theme::new("Animating Theme", colors); + } +} + +pub static DARK: LazyLock = LazyLock::new(|| { + toml::from_str(DARK_THEME_CONTENT).expect("parse dark theme") +}); + +pub static LIGHT: LazyLock = LazyLock::new(|| { + toml::from_str(LIGHT_THEME_CONTENT).expect("parse light theme") +}); + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct ColorScheme { + pub primary: Primary, + pub secondary: Secondary, + pub tertiary: Tertiary, + pub error: Error, + pub surface: Surface, + pub inverse: Inverse, + pub outline: Outline, + #[serde(with = "color_serde")] + pub shadow: Color, + #[serde(with = "color_serde")] + pub scrim: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Primary { + #[serde(with = "color_serde")] + pub color: Color, + #[serde(with = "color_serde")] + pub on_primary: Color, + #[serde(with = "color_serde")] + pub primary_container: Color, + #[serde(with = "color_serde")] + pub on_primary_container: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Secondary { + #[serde(with = "color_serde")] + pub color: Color, + #[serde(with = "color_serde")] + pub on_secondary: Color, + #[serde(with = "color_serde")] + pub secondary_container: Color, + #[serde(with = "color_serde")] + pub on_secondary_container: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Tertiary { + #[serde(with = "color_serde")] + pub color: Color, + #[serde(with = "color_serde")] + pub on_tertiary: Color, + #[serde(with = "color_serde")] + pub tertiary_container: Color, + #[serde(with = "color_serde")] + pub on_tertiary_container: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Error { + #[serde(with = "color_serde")] + pub color: Color, + #[serde(with = "color_serde")] + pub on_error: Color, + #[serde(with = "color_serde")] + pub error_container: Color, + #[serde(with = "color_serde")] + pub on_error_container: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Surface { + #[serde(with = "color_serde")] + pub color: Color, + #[serde(with = "color_serde")] + pub on_surface: Color, + #[serde(with = "color_serde")] + pub on_surface_variant: Color, + pub surface_container: SurfaceContainer, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct SurfaceContainer { + #[serde(with = "color_serde")] + pub lowest: Color, + #[serde(with = "color_serde")] + pub low: Color, + #[serde(with = "color_serde")] + pub base: Color, + #[serde(with = "color_serde")] + pub high: Color, + #[serde(with = "color_serde")] + pub highest: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Inverse { + #[serde(with = "color_serde")] + pub inverse_surface: Color, + #[serde(with = "color_serde")] + pub inverse_on_surface: Color, + #[serde(with = "color_serde")] + pub inverse_primary: Color, +} + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +pub struct Outline { + #[serde(with = "color_serde")] + pub color: Color, + #[serde(with = "color_serde")] + pub variant: Color, +} + +mod color_serde { + use iced_widget::core::Color; + use serde::{Deserialize, Deserializer}; + + use super::utils::parse_argb; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(String::deserialize(deserializer) + .map(|hex| parse_argb(&hex))? + .unwrap_or(Color::TRANSPARENT)) + } +} -- cgit v1.2.3 From 911cd4ffc291d2d9b92f90ede1d03d41edc97920 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 15 Apr 2025 23:45:14 +0200 Subject: feat(material_theme): implement `text_input::Catalog` --- Cargo.lock | 6 +-- crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/text_input.rs | 81 +++++++++++++++++++++++++++++++++ theme_test/src/main.rs | 26 +++++++---- 4 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 crates/material_theme/src/text_input.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.lock b/Cargo.lock index c8379a9..4d8371f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2054,7 +2054,7 @@ dependencies = [ [[package]] name = "iced_dialog" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced_dialog?branch=iced%2Fpersonal#a41468011f8e4f566e9301565f3276fb10cc9551" +source = "git+https://github.com/pml68/iced_dialog?branch=iced%2Fpersonal#6e901c21dbb259d337ed1ad2054da3862172b8b3" dependencies = [ "iced_core", "iced_widget", @@ -2563,9 +2563,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libfuzzer-sys" diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 521af2c..adffe09 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -12,6 +12,7 @@ pub mod menu; pub mod pick_list; pub mod scrollable; pub mod text; +pub mod text_input; pub mod utils; const DARK_THEME_CONTENT: &str = include_str!("../assets/themes/dark.toml"); diff --git a/crates/material_theme/src/text_input.rs b/crates/material_theme/src/text_input.rs new file mode 100644 index 0000000..5fa65ef --- /dev/null +++ b/crates/material_theme/src/text_input.rs @@ -0,0 +1,81 @@ +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}; + +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 default(theme: &Theme, status: Status) -> Style { + let surface = theme.colorscheme.surface; + let primary = theme.colorscheme.primary; + + let active = Style { + background: Background::Color(surface.surface_container.highest), + border: Border { + color: theme.colorscheme.outline.color, + width: 1.0, + radius: 4.into(), + }, + icon: surface.on_surface_variant, + placeholder: surface.on_surface_variant, + value: surface.on_surface, + selection: surface.on_surface, + }; + + match status { + Status::Active => active, + Status::Hovered => Style { + border: Border { + color: surface.on_surface, + ..active.border + }, + ..active + }, + Status::Disabled => Style { + background: Color::TRANSPARENT.into(), + border: Border { + color: Color { + a: DISABLED_CONTAINER_OPACITY, + ..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 + }, + }, + Status::Focused { .. } => Style { + border: Border { + color: primary.color, + width: 2.0, + ..active.border + }, + placeholder: primary.color, + ..active + }, + } +} diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index bcf16de..799d68d 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,6 +1,6 @@ use iced::Element; use iced::Length::Fill; -use iced::widget::{button, column, container, pick_list, row}; +use iced::widget::{button, column, container, pick_list, row, text_input}; use iced_anim::{Animated, Animation, Event}; use iced_dialog::dialog; use material_theme::button::{elevated, filled_tonal, outlined, text}; @@ -25,13 +25,15 @@ enum Message { Noop, OpenDialog, CloseDialog, + Input(String), SwitchTheme(Event), } #[derive(Debug, Default)] pub struct State { - show_dialog: bool, theme: Animated, + show_dialog: bool, + content: String, } impl State { @@ -44,6 +46,7 @@ impl State { Message::CloseDialog => { self.show_dialog = false; } + Message::Input(content) => self.content = content, Message::SwitchTheme(event) => { self.theme.update(event); } @@ -109,13 +112,18 @@ impl State { .style(surface_container_highest), ] .spacing(10), - pick_list( - [LIGHT.clone(), DARK.clone()], - Some(self.theme.target()), - |theme| Message::SwitchTheme(theme.into()) - ) - .placeholder("Select a theme..."), - button("Open Dialog").on_press(Message::OpenDialog) + column![ + pick_list( + [LIGHT.clone(), DARK.clone()], + Some(self.theme.target()), + |theme| Message::SwitchTheme(theme.into()) + ) + .placeholder("Select a theme..."), + button("Open Dialog").on_press(Message::OpenDialog), + text_input("Type something here...", &self.content) + .on_input(Message::Input) + ] + .spacing(10) ] .spacing(20), ) -- cgit v1.2.3 From 385187e2db4681b99a5dcef30a0c170f8c60546a Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 00:01:09 +0200 Subject: feat(material_theme): implement `checkbox::Catalog` --- crates/material_theme/Cargo.toml | 2 + crates/material_theme/src/checkbox.rs | 127 ++++++++++++++++++++++++++++++++++ crates/material_theme/src/lib.rs | 1 + theme_test/src/main.rs | 15 +++- 4 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 crates/material_theme/src/checkbox.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/Cargo.toml b/crates/material_theme/Cargo.toml index eef9605..58c5e0c 100644 --- a/crates/material_theme/Cargo.toml +++ b/crates/material_theme/Cargo.toml @@ -13,7 +13,9 @@ rust-version = "1.85" [features] default = [] +# Provides support for animating with `iced_anim` animate = ["dep:iced_anim"] +# Provides support for `iced_dialog` dialog = ["dep:iced_dialog"] [dependencies] diff --git a/crates/material_theme/src/checkbox.rs b/crates/material_theme/src/checkbox.rs new file mode 100644 index 0000000..ac1f974 --- /dev/null +++ b/crates/material_theme/src/checkbox.rs @@ -0,0 +1,127 @@ +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}; + +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: Color, + background_hover: Option, + icon_color: Color, + border_color: Color, + text_color: Option, + is_checked: bool, +) -> Style { + Style { + background: Background::Color(if is_checked { + background_color + } else { + background_hover.unwrap_or(Color::TRANSPARENT) + }), + icon_color, + border: if is_checked { + border::rounded(2) + } else { + Border { + color: border_color, + width: 2.0, + radius: border::radius(2), + } + }, + text_color, + } +} + +pub fn default(theme: &Theme, status: Status) -> Style { + let surface = theme.colorscheme.surface; + let primary = theme.colorscheme.primary; + + match status { + Status::Active { is_checked } => styled( + primary.color, + None, + primary.on_primary, + surface.on_surface_variant, + Some(surface.on_surface), + is_checked, + ), + Status::Hovered { is_checked } => styled( + mix(primary.color, surface.on_surface, HOVERED_LAYER_OPACITY), + Some(Color { + a: HOVERED_LAYER_OPACITY, + ..surface.on_surface + }), + primary.on_primary, + surface.on_surface_variant, + Some(surface.on_surface), + is_checked, + ), + Status::Disabled { is_checked } => styled( + Color { + a: DISABLED_CONTAINER_OPACITY, + ..surface.on_surface + }, + None, + surface.color, + Color { + a: DISABLED_CONTAINER_OPACITY, + ..surface.on_surface + }, + Some(surface.on_surface), + is_checked, + ), + } +} + +pub fn error(theme: &Theme, status: Status) -> Style { + let surface = theme.colorscheme.surface; + let error = theme.colorscheme.error; + + match status { + Status::Active { is_checked } => styled( + error.color, + None, + error.on_error, + error.color, + Some(error.color), + is_checked, + ), + Status::Hovered { is_checked } => styled( + mix(error.color, surface.on_surface, HOVERED_LAYER_OPACITY), + Some(Color { + a: HOVERED_LAYER_OPACITY, + ..error.color + }), + error.on_error, + error.color, + Some(error.color), + is_checked, + ), + Status::Disabled { is_checked } => styled( + Color { + a: DISABLED_CONTAINER_OPACITY, + ..surface.on_surface + }, + None, + surface.color, + Color { + a: DISABLED_CONTAINER_OPACITY, + ..surface.on_surface + }, + Some(surface.on_surface), + is_checked, + ), + } +} diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index adffe09..2440538 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -5,6 +5,7 @@ use iced_widget::core::theme::{Base, Style}; use serde::Deserialize; pub mod button; +pub mod checkbox; pub mod container; #[cfg(feature = "dialog")] pub mod dialog; diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index 799d68d..953d4e0 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,6 +1,8 @@ use iced::Element; use iced::Length::Fill; -use iced::widget::{button, column, container, pick_list, row, text_input}; +use iced::widget::{ + button, checkbox, column, container, pick_list, row, text_input, +}; use iced_anim::{Animated, Animation, Event}; use iced_dialog::dialog; use material_theme::button::{elevated, filled_tonal, outlined, text}; @@ -26,6 +28,7 @@ enum Message { OpenDialog, CloseDialog, Input(String), + CheckBox(bool), SwitchTheme(Event), } @@ -34,6 +37,7 @@ pub struct State { theme: Animated, show_dialog: bool, content: String, + is_checked: bool, } impl State { @@ -47,6 +51,7 @@ impl State { self.show_dialog = false; } Message::Input(content) => self.content = content, + Message::CheckBox(is_checked) => self.is_checked = is_checked, Message::SwitchTheme(event) => { self.theme.update(event); } @@ -121,7 +126,13 @@ impl State { .placeholder("Select a theme..."), button("Open Dialog").on_press(Message::OpenDialog), text_input("Type something here...", &self.content) - .on_input(Message::Input) + .on_input(Message::Input), + checkbox("Normal", self.is_checked) + .on_toggle(Message::CheckBox), + checkbox("Error", self.is_checked) + .on_toggle(Message::CheckBox) + .style(material_theme::checkbox::error), + checkbox("Disabled", self.is_checked), ] .spacing(10) ] -- cgit v1.2.3 From b17edaf591c173495d1097f20684feb20d7db065 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 00:28:53 +0200 Subject: feat(material_theme): implement `markdown::Catalog` (under feature flag) --- crates/material_theme/Cargo.toml | 6 ++++-- crates/material_theme/src/lib.rs | 2 ++ crates/material_theme/src/markdown.rs | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 crates/material_theme/src/markdown.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/Cargo.toml b/crates/material_theme/Cargo.toml index 58c5e0c..0cfa571 100644 --- a/crates/material_theme/Cargo.toml +++ b/crates/material_theme/Cargo.toml @@ -13,10 +13,12 @@ rust-version = "1.85" [features] default = [] -# Provides support for animating with `iced_anim` +# Provides support for animating with `iced_anim`. animate = ["dep:iced_anim"] -# Provides support for `iced_dialog` +# Provides support for `iced_dialog`. dialog = ["dep:iced_dialog"] +# Provides support for the markdown widget. +markdown = ["iced_widget/markdown"] [dependencies] iced_widget = "0.14.0-dev" diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 2440538..e7ff343 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -9,6 +9,8 @@ pub mod checkbox; pub mod container; #[cfg(feature = "dialog")] pub mod dialog; +#[cfg(feature = "markdown")] +pub mod markdown; pub mod menu; pub mod pick_list; pub mod scrollable; diff --git a/crates/material_theme/src/markdown.rs b/crates/material_theme/src/markdown.rs new file mode 100644 index 0000000..bc14ffe --- /dev/null +++ b/crates/material_theme/src/markdown.rs @@ -0,0 +1,10 @@ +use iced_widget::markdown::Catalog; + +use super::{Theme, container}; + +impl Catalog for Theme { + fn code_block<'a>() -> ::Class<'a> + { + Box::new(container::surface_container_highest) + } +} -- cgit v1.2.3 From 70f7cd31eb5055f80664c574e46003b202b2206f Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 00:29:42 +0200 Subject: feat(material_theme): implement `svg::Catalog` (under feature flag) --- crates/material_theme/Cargo.toml | 2 ++ crates/material_theme/src/lib.rs | 2 ++ crates/material_theme/src/svg.rs | 15 +++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 crates/material_theme/src/svg.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/Cargo.toml b/crates/material_theme/Cargo.toml index 0cfa571..ad825fd 100644 --- a/crates/material_theme/Cargo.toml +++ b/crates/material_theme/Cargo.toml @@ -19,6 +19,8 @@ animate = ["dep:iced_anim"] dialog = ["dep:iced_dialog"] # Provides support for the markdown widget. markdown = ["iced_widget/markdown"] +# Provides support for the SVG widget. +svg = ["iced_widget/svg"] [dependencies] iced_widget = "0.14.0-dev" diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index e7ff343..a41dfd5 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -14,6 +14,8 @@ pub mod markdown; pub mod menu; pub mod pick_list; pub mod scrollable; +#[cfg(feature = "svg")] +pub mod svg; pub mod text; pub mod text_input; pub mod utils; diff --git a/crates/material_theme/src/svg.rs b/crates/material_theme/src/svg.rs new file mode 100644 index 0000000..885d743 --- /dev/null +++ b/crates/material_theme/src/svg.rs @@ -0,0 +1,15 @@ +use iced_widget::svg::{Catalog, Status, Style, StyleFn}; + +use super::Theme; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(|_theme, _status| Style::default()) + } + + fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { + class(self, status) + } +} -- cgit v1.2.3 From b2b7dddb28732fbf8b070dd96922eb7df50318cb Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 00:30:03 +0200 Subject: feat(material_theme): implement `qr_code::Catalog` (under feature flag) --- Cargo.lock | 98 ++++++++++++++++++++++++++++++++++++ crates/material_theme/Cargo.toml | 2 + crates/material_theme/src/lib.rs | 2 + crates/material_theme/src/qr_code.rs | 24 +++++++++ 4 files changed, 126 insertions(+) create mode 100644 crates/material_theme/src/qr_code.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.lock b/Cargo.lock index 3bd023a..6f5e4cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1338,6 +1338,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "fnv" version = "1.0.7" @@ -1579,6 +1585,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -2110,6 +2125,7 @@ dependencies = [ "image", "kamadak-exif", "log", + "lyon_path", "raw-window-handle", "rustc-hash 2.1.1", "thiserror 1.0.69", @@ -2181,6 +2197,7 @@ dependencies = [ "iced_debug", "iced_graphics", "log", + "lyon", "resvg", "rustc-hash 2.1.1", "thiserror 1.0.69", @@ -2197,9 +2214,12 @@ dependencies = [ "log", "num-traits", "ouroboros", + "pulldown-cmark", + "qrcode", "rustc-hash 2.1.1", "thiserror 1.0.69", "unicode-segmentation", + "url", ] [[package]] @@ -2685,6 +2705,58 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +[[package]] +name = "lyon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0047f508cd7a85ad6bad9518f68cce7b1bf6b943fb71f6da0ee3bc1e8cb75f25" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + [[package]] name = "lzma-rs" version = "0.3.0" @@ -2992,6 +3064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3632,6 +3705,25 @@ dependencies = [ "syn", ] +[[package]] +name = "pulldown-cmark" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" +dependencies = [ + "bitflags 2.9.0", + "getopts", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + [[package]] name = "qoi" version = "0.4.1" @@ -3641,6 +3733,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "qrcode" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "166f136dfdb199f98186f3649cf7a0536534a61417a1a30221b492b4fb60ce3f" + [[package]] name = "quick-error" version = "2.0.1" diff --git a/crates/material_theme/Cargo.toml b/crates/material_theme/Cargo.toml index ad825fd..e1da2fb 100644 --- a/crates/material_theme/Cargo.toml +++ b/crates/material_theme/Cargo.toml @@ -21,6 +21,8 @@ dialog = ["dep:iced_dialog"] markdown = ["iced_widget/markdown"] # Provides support for the SVG widget. svg = ["iced_widget/svg"] +# Provides support for the QR code widget. +qr_code = ["iced_widget/qr_code"] [dependencies] iced_widget = "0.14.0-dev" diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index a41dfd5..06ad838 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -13,6 +13,8 @@ pub mod dialog; pub mod markdown; pub mod menu; pub mod pick_list; +#[cfg(feature = "qr_code")] +pub mod qr_code; pub mod scrollable; #[cfg(feature = "svg")] pub mod svg; diff --git a/crates/material_theme/src/qr_code.rs b/crates/material_theme/src/qr_code.rs new file mode 100644 index 0000000..f93fb77 --- /dev/null +++ b/crates/material_theme/src/qr_code.rs @@ -0,0 +1,24 @@ +use iced_widget::qr_code::{Catalog, Style, StyleFn}; + +use super::Theme; + +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<'_>) -> Style { + class(self) + } +} + +pub fn default(theme: &Theme) -> Style { + let surface = theme.colorscheme.surface; + + Style { + cell: surface.on_surface, + background: surface.color, + } +} -- cgit v1.2.3 From e5c682934668c029ac01700465787e49d772425b Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 00:30:18 +0200 Subject: feat(material_theme): implement `combo_box::Catalog` --- crates/material_theme/src/combo_box.rs | 5 +++++ crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/menu.rs | 7 ++----- crates/material_theme/src/pick_list.rs | 7 ++----- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 crates/material_theme/src/combo_box.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/combo_box.rs b/crates/material_theme/src/combo_box.rs new file mode 100644 index 0000000..3024176 --- /dev/null +++ b/crates/material_theme/src/combo_box.rs @@ -0,0 +1,5 @@ +use iced_widget::combo_box::Catalog; + +use super::Theme; + +impl Catalog for Theme {} diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 06ad838..64e71d3 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -6,6 +6,7 @@ use serde::Deserialize; pub mod button; pub mod checkbox; +pub mod combo_box; pub mod container; #[cfg(feature = "dialog")] pub mod dialog; diff --git a/crates/material_theme/src/menu.rs b/crates/material_theme/src/menu.rs index 5990c00..9f43c72 100644 --- a/crates/material_theme/src/menu.rs +++ b/crates/material_theme/src/menu.rs @@ -1,4 +1,4 @@ -use iced_widget::core::{Background, Border, border}; +use iced_widget::core::{Background, border}; use iced_widget::overlay::menu::{Catalog, Style, StyleFn}; use super::Theme; @@ -20,10 +20,7 @@ pub fn default(theme: &Theme) -> Style { let colors = theme.colorscheme.surface; Style { - border: Border { - radius: border::bottom(4), - ..Default::default() - }, + border: border::rounded(4), background: Background::Color(colors.surface_container.base), text_color: colors.on_surface, selected_background: Background::Color(mix( diff --git a/crates/material_theme/src/pick_list.rs b/crates/material_theme/src/pick_list.rs index 25b09f8..4b34871 100644 --- a/crates/material_theme/src/pick_list.rs +++ b/crates/material_theme/src/pick_list.rs @@ -1,4 +1,4 @@ -use iced_widget::core::{Background, Border, border}; +use iced_widget::core::{Background, border}; use iced_widget::pick_list::{Catalog, Status, Style, StyleFn}; use super::Theme; @@ -38,10 +38,7 @@ pub fn default(theme: &Theme, status: Status) -> Style { }, Status::Opened { .. } => Style { background: Background::Color(surface.surface_container.highest), - border: Border { - radius: border::top(4), - ..Default::default() - }, + border: border::rounded(4), ..active }, } -- cgit v1.2.3 From a1d4112532a41c32ad3ef4d301f1603ae73c50ab Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 00:39:06 +0200 Subject: feat(material_theme): implement `progress_bar::Catalog` --- crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/progress_bar.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 crates/material_theme/src/progress_bar.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 64e71d3..a68f0ae 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -14,6 +14,7 @@ pub mod dialog; pub mod markdown; pub mod menu; pub mod pick_list; +pub mod progress_bar; #[cfg(feature = "qr_code")] pub mod qr_code; pub mod scrollable; diff --git a/crates/material_theme/src/progress_bar.rs b/crates/material_theme/src/progress_bar.rs new file mode 100644 index 0000000..f686fff --- /dev/null +++ b/crates/material_theme/src/progress_bar.rs @@ -0,0 +1,26 @@ +use iced_widget::core::{Background, border}; +use iced_widget::progress_bar::{Catalog, Style, StyleFn}; + +use super::Theme; + +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<'_>) -> Style { + class(self) + } +} + +pub fn default(theme: &Theme) -> Style { + Style { + background: Background::Color( + theme.colorscheme.secondary.secondary_container, + ), + bar: Background::Color(theme.colorscheme.primary.color), + border: border::rounded(400), + } +} -- cgit v1.2.3 From 7a1df9e05c73c3d735792110a863f4cad8e7b96b Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 02:59:58 +0200 Subject: feat(material_theme): implement `radio::Catalog` --- crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/radio.rs | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 crates/material_theme/src/radio.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index a68f0ae..cbebdc9 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -17,6 +17,7 @@ pub mod pick_list; pub mod progress_bar; #[cfg(feature = "qr_code")] pub mod qr_code; +pub mod radio; pub mod scrollable; #[cfg(feature = "svg")] pub mod svg; diff --git a/crates/material_theme/src/radio.rs b/crates/material_theme/src/radio.rs new file mode 100644 index 0000000..d249413 --- /dev/null +++ b/crates/material_theme/src/radio.rs @@ -0,0 +1,65 @@ +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}; + +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 default(theme: &Theme, status: Status) -> Style { + let surface = theme.colorscheme.surface; + let primary = theme.colorscheme.primary; + + let active = Style { + background: Color::TRANSPARENT.into(), + dot_color: primary.color, + border_width: 1.0, + border_color: primary.color, + text_color: None, + }; + + match status { + Status::Active { is_selected } => Style { + border_color: if is_selected { + active.border_color + } else { + surface.on_surface + }, + ..active + }, + Status::Hovered { is_selected } => Style { + dot_color: mix( + primary.color, + surface.on_surface, + HOVERED_LAYER_OPACITY, + ), + border_color: if is_selected { + mix(primary.color, surface.on_surface, HOVERED_LAYER_OPACITY) + } else { + Color { + a: DISABLED_TEXT_OPACITY, + ..surface.on_surface + } + }, + background: Background::Color(if is_selected { + Color { + a: HOVERED_LAYER_OPACITY, + ..surface.on_surface + } + } else { + Color::TRANSPARENT + }), + ..active + }, + } +} -- cgit v1.2.3 From ce62ff16fbd8e839b5f707f8d9637db083945803 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 17 Apr 2025 03:00:52 +0200 Subject: refactor: inline Dark and Light theme definitions, remove toml files --- Cargo.lock | 1 - crates/material_theme/Cargo.toml | 8 +- crates/material_theme/assets/themes/dark.toml | 49 ----- crates/material_theme/assets/themes/light.toml | 49 ----- crates/material_theme/src/lib.rs | 238 +++++++++++++++++++------ crates/material_theme/src/utils.rs | 20 +++ theme_test/src/main.rs | 15 +- 7 files changed, 222 insertions(+), 158 deletions(-) delete mode 100644 crates/material_theme/assets/themes/dark.toml delete mode 100644 crates/material_theme/assets/themes/light.toml (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.lock b/Cargo.lock index 6f5e4cb..e8a5b71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2796,7 +2796,6 @@ dependencies = [ "iced_dialog", "iced_widget", "serde", - "toml", ] [[package]] diff --git a/crates/material_theme/Cargo.toml b/crates/material_theme/Cargo.toml index e1da2fb..941bc3e 100644 --- a/crates/material_theme/Cargo.toml +++ b/crates/material_theme/Cargo.toml @@ -13,6 +13,8 @@ rust-version = "1.85" [features] default = [] +# Provides `serde` support +serde = ["dep:serde"] # Provides support for animating with `iced_anim`. animate = ["dep:iced_anim"] # Provides support for `iced_dialog`. @@ -26,9 +28,11 @@ qr_code = ["iced_widget/qr_code"] [dependencies] iced_widget = "0.14.0-dev" -serde.workspace = true -toml.workspace = true dark-light = "2.0.0" + +serde.workspace = true +serde.optional = true + iced_dialog.workspace = true iced_dialog.optional = true diff --git a/crates/material_theme/assets/themes/dark.toml b/crates/material_theme/assets/themes/dark.toml deleted file mode 100644 index 18a369f..0000000 --- a/crates/material_theme/assets/themes/dark.toml +++ /dev/null @@ -1,49 +0,0 @@ -name = "Dark" - -shadow = "#000000" -scrim = "#4d000000" - -[primary] -color = "#9bd4a1" -on_primary = "#003916" -primary_container = "#1b5129" -on_primary_container = "#b6f1bb" - -[secondary] -color = "#b8ccb6" -on_secondary = "#233425" -secondary_container = "#394b3a" -on_secondary_container = "#d3e8d1" - -[tertiary] -color = "#a1ced7" -on_tertiary = "#00363e" -tertiary_container = "#1f4d55" -on_tertiary_container = "#bdeaf4" - -[error] -color = "#ffb4ab" -on_error = "#690005" -error_container = "#93000a" -on_error_container = "#ffdad6" - -[surface] -color = "#101510" -on_surface = "#e0e4dc" -on_surface_variant = "#c1c9be" - -[surface.surface_container] -lowest = "#0b0f0b" -low = "#181d18" -base = "#1c211c" -high = "#262b26" -highest = "#313631" - -[inverse] -inverse_surface = "#e0e4dc" -inverse_on_surface = "#2d322c" -inverse_primary = "#34693f" - -[outline] -color = "#8b9389" -variant = "#414941" diff --git a/crates/material_theme/assets/themes/light.toml b/crates/material_theme/assets/themes/light.toml deleted file mode 100644 index a7115c4..0000000 --- a/crates/material_theme/assets/themes/light.toml +++ /dev/null @@ -1,49 +0,0 @@ -name = "Light" - -shadow = "#000000" -scrim = "#4d000000" - -[primary] -color = "#34693f" -on_primary = "#ffffff" -primary_container = "#b6f1bb" -on_primary_container = "#1b5129" - -[secondary] -color = "#516351" -on_secondary = "#ffffff" -secondary_container = "#d3e8d1" -on_secondary_container = "#394b3a" - -[tertiary] -color = "#39656d" -on_tertiary = "#ffffff" -tertiary_container = "#bdeaf4" -on_tertiary_container = "#1f4d55" - -[error] -color = "#ba1a1a" -on_error = "#ffffff" -error_container = "#ffdad6" -on_error_container = "#93000a" - -[surface] -color = "#f7fbf2" -on_surface = "#181d18" -on_surface_variant = "#414941" - -[surface.surface_container] -lowest = "#ffffff" -low = "#f1f5ed" -base = "#ebefe7" -high = "#e5e9e1" -highest = "#e0e4dc" - -[inverse] -inverse_surface = "#2d322c" -inverse_on_surface = "#eef2ea" -inverse_primary = "#9bd4a1" - -[outline] -color = "#727970" -variant = "#c1c9be" diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index cbebdc9..a9beb2a 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -1,8 +1,7 @@ use std::sync::LazyLock; -use iced_widget::core::Color; use iced_widget::core::theme::{Base, Style}; -use serde::Deserialize; +use iced_widget::core::{Color, color}; pub mod button; pub mod checkbox; @@ -25,13 +24,16 @@ pub mod text; pub mod text_input; pub mod utils; -const DARK_THEME_CONTENT: &str = include_str!("../assets/themes/dark.toml"); -const LIGHT_THEME_CONTENT: &str = include_str!("../assets/themes/light.toml"); +pub static DARK: LazyLock = + LazyLock::new(|| Theme::new("Dark", ColorScheme::DARK)); +pub static LIGHT: LazyLock = + LazyLock::new(|| Theme::new("Light", ColorScheme::LIGHT)); -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Theme { pub name: String, - #[serde(flatten)] + #[cfg_attr(feature = "serde", serde(flatten))] pub colorscheme: ColorScheme, } @@ -119,16 +121,9 @@ impl iced_anim::Animate for Theme { } } -pub static DARK: LazyLock = LazyLock::new(|| { - toml::from_str(DARK_THEME_CONTENT).expect("parse dark theme") -}); - -pub static LIGHT: LazyLock = LazyLock::new(|| { - toml::from_str(LIGHT_THEME_CONTENT).expect("parse light theme") -}); - -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ColorScheme { pub primary: Primary, pub secondary: Secondary, @@ -137,116 +132,240 @@ pub struct ColorScheme { pub surface: Surface, pub inverse: Inverse, pub outline: Outline, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub shadow: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub scrim: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +macro_rules! from_argb { + ($hex:expr) => {{ + let hex = $hex as u32; + + let a = ((hex & 0xff000000) >> 24) as f32 / 255.0; + let r = (hex & 0x00ff0000) >> 16; + let g = (hex & 0x0000ff00) >> 8; + let b = (hex & 0x000000ff); + + color!(r as u8, g as u8, b as u8, a) + }}; +} + +impl ColorScheme { + const DARK: Self = Self { + primary: Primary { + color: from_argb!(0xff9bd4a1), + on_primary: from_argb!(0xff003916), + primary_container: from_argb!(0xff1b5129), + on_primary_container: from_argb!(0xffb6f1bb), + }, + secondary: Secondary { + color: from_argb!(0xffb8ccb6), + on_secondary: from_argb!(0xff233425), + secondary_container: from_argb!(0xff394b3a), + on_secondary_container: from_argb!(0xffd3e8d1), + }, + tertiary: Tertiary { + color: from_argb!(0xffa1ced7), + on_tertiary: from_argb!(0xff00363e), + tertiary_container: from_argb!(0xff1f4d55), + on_tertiary_container: from_argb!(0xffbdeaf4), + }, + error: Error { + color: from_argb!(0xffffb4ab), + on_error: from_argb!(0xff690005), + error_container: from_argb!(0xff93000a), + on_error_container: from_argb!(0xffffdad6), + }, + surface: Surface { + color: from_argb!(0xff101510), + on_surface: from_argb!(0xffe0e4dc), + on_surface_variant: from_argb!(0xffc1c9be), + surface_container: SurfaceContainer { + lowest: from_argb!(0xff0b0f0b), + low: from_argb!(0xff181d18), + base: from_argb!(0xff1c211c), + high: from_argb!(0xff262b26), + highest: from_argb!(0xff313631), + }, + }, + inverse: Inverse { + inverse_surface: from_argb!(0xffe0e4dc), + inverse_on_surface: from_argb!(0xff2d322c), + inverse_primary: from_argb!(0xff34693f), + }, + outline: Outline { + color: from_argb!(0xff8b9389), + variant: from_argb!(0xff414941), + }, + shadow: from_argb!(0xff000000), + scrim: from_argb!(0x4d000000), + }; + + const LIGHT: Self = Self { + primary: Primary { + color: from_argb!(0xff34693f), + on_primary: from_argb!(0xffffffff), + primary_container: from_argb!(0xffb6f1bb), + on_primary_container: from_argb!(0xff1b5129), + }, + secondary: Secondary { + color: from_argb!(0xff516351), + on_secondary: from_argb!(0xffffffff), + secondary_container: from_argb!(0xffd3e8d1), + on_secondary_container: from_argb!(0xff394b3a), + }, + tertiary: Tertiary { + color: from_argb!(0xff39656d), + on_tertiary: from_argb!(0xffffffff), + tertiary_container: from_argb!(0xffbdeaf4), + on_tertiary_container: from_argb!(0xff1f4d55), + }, + error: Error { + color: from_argb!(0xffba1a1a), + on_error: from_argb!(0xffffffff), + error_container: from_argb!(0xffffdad6), + on_error_container: from_argb!(0xff93000a), + }, + surface: Surface { + color: from_argb!(0xfff7fbf2), + on_surface: from_argb!(0xff181d18), + on_surface_variant: from_argb!(0xff414941), + surface_container: SurfaceContainer { + lowest: from_argb!(0xffffffff), + low: from_argb!(0xfff1f5ed), + base: from_argb!(0xffebefe7), + high: from_argb!(0xffe5e9e1), + highest: from_argb!(0xffe0e4dc), + }, + }, + inverse: Inverse { + inverse_surface: from_argb!(0xff2d322c), + inverse_on_surface: from_argb!(0xffeef2ea), + inverse_primary: from_argb!(0xff9bd4a1), + }, + outline: Outline { + color: from_argb!(0xff727970), + variant: from_argb!(0xffc1c9be), + }, + shadow: from_argb!(0xff000000), + scrim: from_argb!(0x4d000000), + }; +} + +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Primary { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub color: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_primary: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub primary_container: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_primary_container: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Secondary { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub color: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_secondary: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub secondary_container: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_secondary_container: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Tertiary { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub color: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_tertiary: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub tertiary_container: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_tertiary_container: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Error { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub color: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_error: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub error_container: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_error_container: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Surface { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub color: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_surface: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub on_surface_variant: Color, pub surface_container: SurfaceContainer, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SurfaceContainer { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub lowest: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub low: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub base: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub high: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub highest: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Inverse { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub inverse_surface: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub inverse_on_surface: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub inverse_primary: Color, } -#[derive(Debug, Clone, Copy, PartialEq, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "animate", derive(iced_anim::Animate))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Outline { - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub color: Color, - #[serde(with = "color_serde")] + #[cfg_attr(feature = "serde", serde(with = "color_serde"))] pub variant: Color, } +#[cfg(feature = "serde")] mod color_serde { use iced_widget::core::Color; - use serde::{Deserialize, Deserializer}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use super::utils::parse_argb; + use super::utils::{color_to_argb, parse_argb}; pub fn deserialize<'de, D>(deserializer: D) -> Result where @@ -256,4 +375,11 @@ mod color_serde { .map(|hex| parse_argb(&hex))? .unwrap_or(Color::TRANSPARENT)) } + + pub fn serialize(color: &Color, serializer: S) -> Result + where + S: Serializer, + { + color_to_argb(*color).serialize(serializer) + } } diff --git a/crates/material_theme/src/utils.rs b/crates/material_theme/src/utils.rs index a05bc62..f35396f 100644 --- a/crates/material_theme/src/utils.rs +++ b/crates/material_theme/src/utils.rs @@ -66,6 +66,26 @@ pub fn parse_argb(s: &str) -> Option { }) } +pub fn color_to_argb(color: Color) -> String { + use std::fmt::Write; + + let mut hex = String::with_capacity(9); + + let [r, g, b, a] = color.into_rgba8(); + + let _ = write!(&mut hex, "#"); + + if a < u8::MAX { + let _ = write!(&mut hex, "{a:02X}"); + } + + let _ = write!(&mut hex, "{r:02X}"); + let _ = write!(&mut hex, "{g:02X}"); + let _ = write!(&mut hex, "{b:02X}"); + + hex +} + pub fn mix(color1: Color, color2: Color, p2: f32) -> Color { if p2 <= 0.0 { return color1; diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index 953d4e0..9aab2fe 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,7 +1,7 @@ use iced::Element; use iced::Length::Fill; use iced::widget::{ - button, checkbox, column, container, pick_list, row, text_input, + button, checkbox, column, container, pick_list, radio, row, text_input, }; use iced_anim::{Animated, Animation, Event}; use iced_dialog::dialog; @@ -29,6 +29,7 @@ enum Message { CloseDialog, Input(String), CheckBox(bool), + Radio(Choice), SwitchTheme(Event), } @@ -38,6 +39,14 @@ pub struct State { show_dialog: bool, content: String, is_checked: bool, + selection: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Choice { + A, + B, + C, } impl State { @@ -52,6 +61,7 @@ impl State { } Message::Input(content) => self.content = content, Message::CheckBox(is_checked) => self.is_checked = is_checked, + Message::Radio(choice) => self.selection = Some(choice), Message::SwitchTheme(event) => { self.theme.update(event); } @@ -133,6 +143,9 @@ impl State { .on_toggle(Message::CheckBox) .style(material_theme::checkbox::error), checkbox("Disabled", self.is_checked), + radio("A", Choice::A, self.selection, Message::Radio,), + radio("B", Choice::B, self.selection, Message::Radio,), + radio("C", Choice::C, self.selection, Message::Radio,), ] .spacing(10) ] -- cgit v1.2.3 From 21214574718a02002e632f6192a611a758595811 Mon Sep 17 00:00:00 2001 From: pml68 Date: Fri, 18 Apr 2025 12:34:49 +0200 Subject: feat(material_theme): implement `slider::Catalog` --- crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/slider.rs | 63 +++++++++++++++++++++++++++++++++++++ theme_test/src/main.rs | 12 +++++-- 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 crates/material_theme/src/slider.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index a9beb2a..03f1ca5 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -18,6 +18,7 @@ pub mod progress_bar; pub mod qr_code; pub mod radio; pub mod scrollable; +pub mod slider; #[cfg(feature = "svg")] pub mod svg; pub mod text; diff --git a/crates/material_theme/src/slider.rs b/crates/material_theme/src/slider.rs new file mode 100644 index 0000000..cc3bf98 --- /dev/null +++ b/crates/material_theme/src/slider.rs @@ -0,0 +1,63 @@ +use iced_widget::core::{Background, Color, border}; +use iced_widget::slider::{ + Catalog, Handle, HandleShape, Rail, Status, Style, StyleFn, +}; + +use super::Theme; +use crate::utils::{HOVERED_LAYER_OPACITY, PRESSED_LAYER_OPACITY, mix}; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> ::Class<'a> { + Box::new(default) + } + + fn style( + &self, + class: &::Class<'_>, + status: Status, + ) -> Style { + class(self, status) + } +} + +pub fn styled(left: Color, right: Color, handle_radius: f32) -> Style { + Style { + rail: Rail { + backgrounds: (left.into(), right.into()), + width: 8.0, + border: border::rounded(400), + }, + handle: Handle { + shape: HandleShape::Circle { + radius: handle_radius, + }, + background: Background::Color(left), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + } +} + +pub fn default(theme: &Theme, status: Status) -> Style { + let surface = theme.colorscheme.surface; + let primary = theme.colorscheme.primary; + let secondary = theme.colorscheme.secondary; + + match status { + Status::Active => { + styled(primary.color, secondary.secondary_container, 16.0) + } + Status::Hovered => styled( + mix(primary.color, surface.on_surface, HOVERED_LAYER_OPACITY), + secondary.secondary_container, + 16.0, + ), + Status::Dragged => styled( + mix(primary.color, surface.on_surface, PRESSED_LAYER_OPACITY), + secondary.secondary_container, + 15.0, + ), + } +} diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index 9aab2fe..c13bde7 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,8 +1,9 @@ -use iced::Element; use iced::Length::Fill; use iced::widget::{ - button, checkbox, column, container, pick_list, radio, row, text_input, + button, center, checkbox, column, container, pick_list, radio, row, slider, + text_input, }; +use iced::{Element, Length}; use iced_anim::{Animated, Animation, Event}; use iced_dialog::dialog; use material_theme::button::{elevated, filled_tonal, outlined, text}; @@ -30,6 +31,7 @@ enum Message { Input(String), CheckBox(bool), Radio(Choice), + Slider(f32), SwitchTheme(Event), } @@ -40,6 +42,7 @@ pub struct State { content: String, is_checked: bool, selection: Option, + value: f32, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -62,6 +65,7 @@ impl State { Message::Input(content) => self.content = content, Message::CheckBox(is_checked) => self.is_checked = is_checked, Message::Radio(choice) => self.selection = Some(choice), + Message::Slider(value) => self.value = value, Message::SwitchTheme(event) => { self.theme.update(event); } @@ -146,6 +150,10 @@ impl State { radio("A", Choice::A, self.selection, Message::Radio,), radio("B", Choice::B, self.selection, Message::Radio,), radio("C", Choice::C, self.selection, Message::Radio,), + center(iced::widget::text!("{:.1}", self.value)) + .width(Length::Fill) + .height(Length::Shrink), + slider(0.0..=100.0, self.value, Message::Slider).step(0.1) ] .spacing(10) ] -- cgit v1.2.3 From 6300486b8ea51b75850708acb271fcce02666dd6 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 19 Apr 2025 01:10:11 +0200 Subject: feat(material_theme): implement `rule::Catalog` --- crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/rule.rs | 33 +++++++++++++++++++++++++++++++++ theme_test/src/main.rs | 15 +++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 crates/material_theme/src/rule.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 03f1ca5..2452d88 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -17,6 +17,7 @@ pub mod progress_bar; #[cfg(feature = "qr_code")] pub mod qr_code; pub mod radio; +pub mod rule; pub mod scrollable; pub mod slider; #[cfg(feature = "svg")] diff --git a/crates/material_theme/src/rule.rs b/crates/material_theme/src/rule.rs new file mode 100644 index 0000000..e433005 --- /dev/null +++ b/crates/material_theme/src/rule.rs @@ -0,0 +1,33 @@ +use iced_widget::core::border::Radius; +use iced_widget::rule::{Catalog, FillMode, Style, StyleFn}; + +use super::Theme; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(inset) + } + + fn style(&self, class: &Self::Class<'_>) -> Style { + class(self) + } +} + +pub fn inset(theme: &Theme) -> Style { + Style { + color: theme.colorscheme.outline.variant, + fill_mode: FillMode::Padded(8), + width: 1, + radius: Radius::default(), + } +} +pub fn full_width(theme: &Theme) -> Style { + Style { + color: theme.colorscheme.outline.variant, + fill_mode: FillMode::Full, + width: 1, + radius: Radius::default(), + } +} diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index c13bde7..7192785 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,7 +1,7 @@ use iced::Length::Fill; use iced::widget::{ - button, center, checkbox, column, container, pick_list, radio, row, slider, - text_input, + button, center, checkbox, column, container, horizontal_rule, pick_list, + radio, row, slider, text_input, }; use iced::{Element, Length}; use iced_anim::{Animated, Animation, Event}; @@ -132,24 +132,35 @@ impl State { ] .spacing(10), column![ + // Pick List pick_list( [LIGHT.clone(), DARK.clone()], Some(self.theme.target()), |theme| Message::SwitchTheme(theme.into()) ) .placeholder("Select a theme..."), + horizontal_rule(1), + // Button button("Open Dialog").on_press(Message::OpenDialog), + horizontal_rule(1), + // Text Input text_input("Type something here...", &self.content) .on_input(Message::Input), + horizontal_rule(1), + // Checkbox checkbox("Normal", self.is_checked) .on_toggle(Message::CheckBox), checkbox("Error", self.is_checked) .on_toggle(Message::CheckBox) .style(material_theme::checkbox::error), checkbox("Disabled", self.is_checked), + horizontal_rule(1), + // Radio radio("A", Choice::A, self.selection, Message::Radio,), radio("B", Choice::B, self.selection, Message::Radio,), radio("C", Choice::C, self.selection, Message::Radio,), + horizontal_rule(1), + // Slider center(iced::widget::text!("{:.1}", self.value)) .width(Length::Fill) .height(Length::Shrink), -- cgit v1.2.3 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/lib.rs | 1 + crates/material_theme/src/toggler.rs | 80 ++++++++++++++++++++++++++++++++++++ theme_test/src/main.rs | 17 +++++--- 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 crates/material_theme/src/toggler.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 2452d88..0df5b1c 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -24,6 +24,7 @@ pub mod slider; pub mod svg; pub mod text; pub mod text_input; +pub mod toggler; pub mod utils; pub static DARK: LazyLock = 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), + ), + } +} diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index 7192785..d564ee5 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,7 +1,7 @@ use iced::Length::Fill; use iced::widget::{ button, center, checkbox, column, container, horizontal_rule, pick_list, - radio, row, slider, text_input, + radio, row, slider, text_input, toggler, }; use iced::{Element, Length}; use iced_anim::{Animated, Animation, Event}; @@ -29,7 +29,7 @@ enum Message { OpenDialog, CloseDialog, Input(String), - CheckBox(bool), + Bool(bool), Radio(Choice), Slider(f32), SwitchTheme(Event), @@ -63,7 +63,7 @@ impl State { self.show_dialog = false; } Message::Input(content) => self.content = content, - Message::CheckBox(is_checked) => self.is_checked = is_checked, + Message::Bool(is_checked) => self.is_checked = is_checked, Message::Radio(choice) => self.selection = Some(choice), Message::Slider(value) => self.value = value, Message::SwitchTheme(event) => { @@ -149,9 +149,9 @@ impl State { horizontal_rule(1), // Checkbox checkbox("Normal", self.is_checked) - .on_toggle(Message::CheckBox), + .on_toggle(Message::Bool), checkbox("Error", self.is_checked) - .on_toggle(Message::CheckBox) + .on_toggle(Message::Bool) .style(material_theme::checkbox::error), checkbox("Disabled", self.is_checked), horizontal_rule(1), @@ -164,7 +164,12 @@ impl State { center(iced::widget::text!("{:.1}", self.value)) .width(Length::Fill) .height(Length::Shrink), - slider(0.0..=100.0, self.value, Message::Slider).step(0.1) + slider(0.0..=100.0, self.value, Message::Slider).step(0.1), + horizontal_rule(1), + // Toggler + toggler(self.is_checked) + .on_toggle(Message::Bool) + .size(24.0) ] .spacing(10) ] -- cgit v1.2.3 From 78c7ac2f3ea0a6e4e88ea9857f37cbc43db12736 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 20 Apr 2025 01:04:42 +0200 Subject: feat(material_theme): implement `pane_grid::Catalog` --- crates/material_theme/src/lib.rs | 1 + crates/material_theme/src/pane_grid.rs | 38 +++++ theme_test/src/main.rs | 284 ++++++++++++++++++++------------- 3 files changed, 216 insertions(+), 107 deletions(-) create mode 100644 crates/material_theme/src/pane_grid.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 0df5b1c..416c958 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -12,6 +12,7 @@ pub mod dialog; #[cfg(feature = "markdown")] pub mod markdown; pub mod menu; +pub mod pane_grid; pub mod pick_list; pub mod progress_bar; #[cfg(feature = "qr_code")] diff --git a/crates/material_theme/src/pane_grid.rs b/crates/material_theme/src/pane_grid.rs new file mode 100644 index 0000000..d66e475 --- /dev/null +++ b/crates/material_theme/src/pane_grid.rs @@ -0,0 +1,38 @@ +use iced_widget::core::{Background, border}; +use iced_widget::pane_grid::{Catalog, Highlight, Line, Style, StyleFn}; + +use super::Theme; +use crate::utils::{HOVERED_LAYER_OPACITY, mix}; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> ::Class<'a> { + Box::new(default) + } + + fn style(&self, class: &::Class<'_>) -> Style { + class(self) + } +} + +pub fn default(theme: &Theme) -> Style { + Style { + hovered_region: Highlight { + background: Background::Color(mix( + theme.colorscheme.tertiary.tertiary_container, + theme.colorscheme.surface.on_surface, + HOVERED_LAYER_OPACITY, + )), + border: border::rounded(12), + }, + picked_split: Line { + color: theme.colorscheme.outline.variant, + width: 2.0, + }, + hovered_split: Line { + color: theme.colorscheme.surface.on_surface, + width: 6.0, + }, + } +} diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index d564ee5..b8653b5 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,7 +1,7 @@ use iced::Length::Fill; use iced::widget::{ - button, center, checkbox, column, container, horizontal_rule, pick_list, - radio, row, slider, text_input, toggler, + button, center, checkbox, column, container, horizontal_rule, pane_grid, + pick_list, radio, row, slider, text_input, toggler, }; use iced::{Element, Length}; use iced_anim::{Animated, Animation, Event}; @@ -32,10 +32,11 @@ enum Message { Bool(bool), Radio(Choice), Slider(f32), + Resize(pane_grid::ResizeEvent), SwitchTheme(Event), } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct State { theme: Animated, show_dialog: bool, @@ -43,6 +44,28 @@ pub struct State { is_checked: bool, selection: Option, value: f32, + panes: pane_grid::State, +} + +impl Default for State { + fn default() -> Self { + Self { + theme: Default::default(), + show_dialog: Default::default(), + content: Default::default(), + is_checked: Default::default(), + selection: Default::default(), + value: Default::default(), + panes: pane_grid::State::with_configuration( + pane_grid::Configuration::Split { + axis: pane_grid::Axis::Vertical, + ratio: 0.5, + a: Box::new(pane_grid::Configuration::Pane(Pane::Left)), + b: Box::new(pane_grid::Configuration::Pane(Pane::Right)), + }, + ), + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -52,6 +75,12 @@ pub enum Choice { C, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Pane { + Left, + Right, +} + impl State { fn update(&mut self, message: Message) { match message { @@ -66,118 +95,159 @@ impl State { Message::Bool(is_checked) => self.is_checked = is_checked, Message::Radio(choice) => self.selection = Some(choice), Message::Slider(value) => self.value = value, + Message::Resize(pane_grid::ResizeEvent { split, ratio }) => { + self.panes.resize(split, ratio); + } Message::SwitchTheme(event) => { self.theme.update(event); } } } fn view(&self) -> Element<'_, Message, Theme> { - let base = container( - row![ - column![ - button("Disabled"), - button("Filled").on_press(Message::Noop), - button("Filled Tonal") - .on_press(Message::Noop) - .style(filled_tonal), - button("Elevated").on_press(Message::Noop).style(elevated), - button("Outlined").on_press(Message::Noop).style(outlined), - button("Text").on_press(Message::Noop).style(text), - button("Text Disabled").style(text), - ] - .spacing(10), - column![ - container("None").padding(8), - container("Primary").padding(8).style(primary), - container("Primary Container") - .padding(8) - .style(primary_container), - container("Secondary").padding(8).style(secondary), - container("Secondary Container") - .padding(8) - .style(secondary_container), - container("Tertiary").padding(8).style(tertiary), - container("Tertiary Container") - .padding(8) - .style(tertiary_container), - container("Error").padding(8).style(error), - container("Error Container") - .padding(8) - .style(error_container), - container("Surface").padding(8).style(surface), - container( - iced::widget::text("Surface Variant") - .style(surface_variant) + let base: pane_grid::PaneGrid<'_, Message, Theme> = + pane_grid(&self.panes, |_pane, state, _is_maximized| { + pane_grid::Content::new(match state { + Pane::Left => container( + row![ + column![ + button("Disabled"), + button("Filled").on_press(Message::Noop), + button("Filled Tonal") + .on_press(Message::Noop) + .style(filled_tonal), + button("Elevated") + .on_press(Message::Noop) + .style(elevated), + button("Outlined") + .on_press(Message::Noop) + .style(outlined), + button("Text") + .on_press(Message::Noop) + .style(text), + button("Text Disabled").style(text), + ] + .spacing(10), + column![ + container("None").padding(8), + container("Primary").padding(8).style(primary), + container("Primary Container") + .padding(8) + .style(primary_container), + container("Secondary") + .padding(8) + .style(secondary), + container("Secondary Container") + .padding(8) + .style(secondary_container), + container("Tertiary") + .padding(8) + .style(tertiary), + container("Tertiary Container") + .padding(8) + .style(tertiary_container), + container("Error").padding(8).style(error), + container("Error Container") + .padding(8) + .style(error_container), + container("Surface").padding(8).style(surface), + container( + iced::widget::text("Surface Variant") + .style(surface_variant) + ) + .padding(8) + .style(surface), + container("Inverse Surface") + .padding(8) + .style(inverse_surface), + container("Surface Container Lowest") + .padding(8) + .style(surface_container_lowest), + container("Surface Container Low") + .padding(8) + .style(surface_container_low), + container("Surface Container") + .padding(8) + .style(surface_container), + container("Surface Container High") + .padding(8) + .style(surface_container_high), + container("Surface Container Highest") + .padding(8) + .style(surface_container_highest), + ] + .spacing(10), + ] + .spacing(10), ) - .padding(8) - .style(surface), - container("Inverse Surface") - .padding(8) - .style(inverse_surface), - container("Surface Container Lowest") - .padding(8) - .style(surface_container_lowest), - container("Surface Container Low") - .padding(8) - .style(surface_container_low), - container("Surface Container") - .padding(8) - .style(surface_container), - container("Surface Container High") - .padding(8) - .style(surface_container_high), - container("Surface Container Highest") - .padding(8) - .style(surface_container_highest), - ] - .spacing(10), - column![ - // Pick List - pick_list( - [LIGHT.clone(), DARK.clone()], - Some(self.theme.target()), - |theme| Message::SwitchTheme(theme.into()) + .width(Length::Fill) + .height(Length::Fill) + .padding(12), + + Pane::Right => container( + column![ + // Pick List + pick_list( + [LIGHT.clone(), DARK.clone()], + Some(self.theme.target()), + |theme| Message::SwitchTheme(theme.into()) + ) + .placeholder("Select a theme..."), + horizontal_rule(1), + // Button + button("Open Dialog").on_press(Message::OpenDialog), + horizontal_rule(1), + // Text Input + text_input("Type something here...", &self.content) + .on_input(Message::Input), + horizontal_rule(1), + // Checkbox + checkbox("Normal", self.is_checked) + .on_toggle(Message::Bool), + checkbox("Error", self.is_checked) + .on_toggle(Message::Bool) + .style(material_theme::checkbox::error), + checkbox("Disabled", self.is_checked), + horizontal_rule(1), + // Radio + radio( + "A", + Choice::A, + self.selection, + Message::Radio, + ), + radio( + "B", + Choice::B, + self.selection, + Message::Radio, + ), + radio( + "C", + Choice::C, + self.selection, + Message::Radio, + ), + horizontal_rule(1), + // Slider + center(iced::widget::text!("{:.1}", self.value)) + .width(Length::Fill) + .height(Length::Shrink), + slider(0.0..=100.0, self.value, Message::Slider) + .step(0.1), + horizontal_rule(1), + // Toggler + toggler(self.is_checked) + .on_toggle(Message::Bool) + .size(24.0) + ] + .spacing(10), ) - .placeholder("Select a theme..."), - horizontal_rule(1), - // Button - button("Open Dialog").on_press(Message::OpenDialog), - horizontal_rule(1), - // Text Input - text_input("Type something here...", &self.content) - .on_input(Message::Input), - horizontal_rule(1), - // Checkbox - checkbox("Normal", self.is_checked) - .on_toggle(Message::Bool), - checkbox("Error", self.is_checked) - .on_toggle(Message::Bool) - .style(material_theme::checkbox::error), - checkbox("Disabled", self.is_checked), - horizontal_rule(1), - // Radio - radio("A", Choice::A, self.selection, Message::Radio,), - radio("B", Choice::B, self.selection, Message::Radio,), - radio("C", Choice::C, self.selection, Message::Radio,), - horizontal_rule(1), - // Slider - center(iced::widget::text!("{:.1}", self.value)) - .width(Length::Fill) - .height(Length::Shrink), - slider(0.0..=100.0, self.value, Message::Slider).step(0.1), - horizontal_rule(1), - // Toggler - toggler(self.is_checked) - .on_toggle(Message::Bool) - .size(24.0) - ] - .spacing(10) - ] - .spacing(20), - ) - .width(Fill) - .height(Fill) - .padding(12); + .width(Length::Fill) + .height(Length::Fill) + .padding(12), + }) + }) + .on_resize(10, Message::Resize); let dialog = dialog(self.show_dialog, base, iced::widget::text("Say Hi!")) -- cgit v1.2.3 From c3c05cef555de305729e53cef8b8e660e31eaf27 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 22 Apr 2025 14:33:36 +0200 Subject: refactor: apply some clippy suggestions --- Cargo.lock | 99 ++++++++++++++++------------------- crates/material_theme/src/button.rs | 4 +- crates/material_theme/src/checkbox.rs | 6 +-- crates/material_theme/src/lib.rs | 5 +- crates/material_theme/src/utils.rs | 6 ++- src/panes/code_view.rs | 2 +- 6 files changed, 59 insertions(+), 63 deletions(-) (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.lock b/Cargo.lock index 4f0a992..fd31403 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,11 +534,11 @@ dependencies = [ [[package]] name = "block2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.1", ] [[package]] @@ -1070,9 +1070,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" dependencies = [ "bitflags 2.9.0", - "block2 0.6.0", + "block2 0.6.1", "libc", - "objc2 0.6.0", + "objc2 0.6.1", +] + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", ] [[package]] @@ -1160,20 +1170,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "embed-resource" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379" -dependencies = [ - "cc", - "memchr", - "rustc_version", - "toml", - "vswhom", - "winreg", -] - [[package]] name = "embed-resource" version = "3.0.2" @@ -1997,7 +1993,7 @@ name = "iced_builder" version = "0.1.0" dependencies = [ "dirs-next", - "embed-resource 3.0.2", + "embed-resource", "fxhash", "iced", "iced_anim", @@ -2611,9 +2607,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" [[package]] name = "libredox" @@ -2675,12 +2671,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - [[package]] name = "log" version = "0.4.27" @@ -3124,9 +3114,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" dependencies = [ "objc2-encode", ] @@ -3149,14 +3139,14 @@ dependencies = [ [[package]] name = "objc2-app-kit" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ "bitflags 2.9.0", - "block2 0.6.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", + "block2 0.6.1", + "objc2 0.6.1", + "objc2-foundation 0.3.1", ] [[package]] @@ -3197,12 +3187,13 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.0", - "objc2 0.6.0", + "dispatch2 0.3.0", + "objc2 0.6.1", ] [[package]] @@ -3250,12 +3241,12 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ "bitflags 2.9.0", - "objc2 0.6.0", + "objc2 0.6.1", "objc2-core-foundation", ] @@ -4035,14 +4026,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" dependencies = [ "ashpd 0.11.0", - "block2 0.6.0", - "dispatch2", + "block2 0.6.1", + "dispatch2 0.2.0", "js-sys", "log", - "objc2 0.6.0", - "objc2-app-kit 0.3.0", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", "objc2-core-foundation", - "objc2-foundation 0.3.0", + "objc2-foundation 0.3.1", "pollster", "raw-window-handle", "urlencoding", @@ -4383,9 +4374,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -5952,12 +5943,12 @@ checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_exe_info" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0960cd3c8e7c1a55327ac8206395748c8145dc56c524057d1d50ab80300f49f" +checksum = "3a7c2cd292e8e58e012eaf18f18f6b64ef74e0b90677b4f9e1f15bfca24056c7" dependencies = [ "camino", - "embed-resource 2.5.1", + "embed-resource", ] [[package]] @@ -6515,15 +6506,13 @@ dependencies = [ [[package]] name = "zopfli" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", - "lockfree-object-pool", "log", - "once_cell", "simd-adler32", ] diff --git a/crates/material_theme/src/button.rs b/crates/material_theme/src/button.rs index 21d77b7..e1369eb 100644 --- a/crates/material_theme/src/button.rs +++ b/crates/material_theme/src/button.rs @@ -143,7 +143,7 @@ pub fn outlined(theme: &Theme, status: Status) -> Style { Status::Active | Status::Pressed | Status::Hovered => Border { color: outline, width: 1.0, - radius: 400.0.into(), + radius: 400.into(), }, Status::Disabled => Border { color: Color { @@ -151,7 +151,7 @@ pub fn outlined(theme: &Theme, status: Status) -> Style { ..disabled }, width: 1.0, - radius: 400.0.into(), + radius: 400.into(), }, }; diff --git a/crates/material_theme/src/checkbox.rs b/crates/material_theme/src/checkbox.rs index ac1f974..ff038b0 100644 --- a/crates/material_theme/src/checkbox.rs +++ b/crates/material_theme/src/checkbox.rs @@ -18,7 +18,7 @@ impl Catalog for Theme { pub fn styled( background_color: Color, - background_hover: Option, + background_unchecked: Option, icon_color: Color, border_color: Color, text_color: Option, @@ -28,7 +28,7 @@ pub fn styled( background: Background::Color(if is_checked { background_color } else { - background_hover.unwrap_or(Color::TRANSPARENT) + background_unchecked.unwrap_or(Color::TRANSPARENT) }), icon_color, border: if is_checked { @@ -37,7 +37,7 @@ pub fn styled( Border { color: border_color, width: 2.0, - radius: border::radius(2), + radius: 2.into(), } }, text_color, diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 416c958..13f13b1 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -24,6 +24,7 @@ pub mod slider; #[cfg(feature = "svg")] pub mod svg; pub mod text; +pub mod text_editor; pub mod text_input; pub mod toggler; pub mod utils; @@ -59,7 +60,7 @@ impl Clone for Theme { } fn clone_from(&mut self, source: &Self) { - self.name = source.name.clone(); + self.name.clone_from(&source.name); self.colorscheme = source.colorscheme; } } @@ -142,6 +143,7 @@ pub struct ColorScheme { pub scrim: Color, } +#[allow(clippy::cast_precision_loss)] macro_rules! from_argb { ($hex:expr) => {{ let hex = $hex as u32; @@ -155,6 +157,7 @@ macro_rules! from_argb { }}; } +#[allow(clippy::cast_precision_loss)] impl ColorScheme { const DARK: Self = Self { primary: Primary { diff --git a/crates/material_theme/src/utils.rs b/crates/material_theme/src/utils.rs index f35396f..5ad137e 100644 --- a/crates/material_theme/src/utils.rs +++ b/crates/material_theme/src/utils.rs @@ -1,5 +1,7 @@ use iced_widget::core::{Color, Shadow, Vector}; +const COLOR_ERROR_MARGIN: f32 = 0.0001; + pub const HOVERED_LAYER_OPACITY: f32 = 0.08; pub const PRESSED_LAYER_OPACITY: f32 = 0.1; @@ -95,7 +97,9 @@ pub fn mix(color1: Color, color2: Color, p2: f32) -> Color { let p1 = 1.0 - p2; - if color1.a != 1.0 || color2.a != 1.0 { + if (color1.a - 1.0).abs() > COLOR_ERROR_MARGIN + || (color2.a - 1.0) > COLOR_ERROR_MARGIN + { let a = color1.a * p1 + color2.a * p2; if a > 0.0 { let c1 = color1.into_linear().map(|c| c * color1.a * p1); diff --git a/src/panes/code_view.rs b/src/panes/code_view.rs index 85b0bbe..890af8a 100644 --- a/src/panes/code_view.rs +++ b/src/panes/code_view.rs @@ -71,7 +71,7 @@ pub fn view( palette.background.base.color, ), border: Border { - radius: 2.0.into(), + radius: 2.into(), width: 1.0, color: palette.background.strong.color, }, -- cgit v1.2.3 From 4dc531f63712e8072a785bd1ec22f35aab3b9140 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 22 Apr 2025 20:27:20 +0200 Subject: refactor(material_theme): make `Theme` `Copy`, add `Theme::ALL` constant --- Cargo.toml | 1 + crates/material_theme/src/lib.rs | 60 +++++++++++++++++----------------------- theme_test/src/main.rs | 6 ++-- 3 files changed, 29 insertions(+), 38 deletions(-) (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.toml b/Cargo.toml index 1c0984b..a4abf56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ from_over_into = "deny" needless_borrow = "deny" new_without_default = "deny" useless_conversion = "deny" +clone_on_copy = "deny" [patch.crates-io] iced_anim = { git = "https://github.com/pml68/iced_anim" } diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 13f13b1..25b6a9c 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::LazyLock; +use iced_widget::core::Color; use iced_widget::core::theme::{Base, Style}; -use iced_widget::core::{Color, color}; pub mod button; pub mod checkbox; @@ -29,55 +29,48 @@ pub mod text_input; pub mod toggler; pub mod utils; -pub static DARK: LazyLock = - LazyLock::new(|| Theme::new("Dark", ColorScheme::DARK)); -pub static LIGHT: LazyLock = - LazyLock::new(|| Theme::new("Light", ColorScheme::LIGHT)); - -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Theme { - pub name: String, + pub name: &'static str, #[cfg_attr(feature = "serde", serde(flatten))] pub colorscheme: ColorScheme, } 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 fn new(name: impl Into, colorscheme: ColorScheme) -> Self { Self { - name: name.into(), + name: Box::leak(name.into().into_boxed_str()), colorscheme, } } } -impl Clone for Theme { - fn clone(&self) -> Self { - Self { - name: self.name.clone(), - colorscheme: self.colorscheme, - } - } - - fn clone_from(&mut self, source: &Self) { - self.name.clone_from(&source.name); - self.colorscheme = source.colorscheme; - } -} - impl Default for Theme { fn default() -> Self { static DEFAULT: LazyLock = LazyLock::new(|| { match dark_light::detect().unwrap_or(dark_light::Mode::Unspecified) { dark_light::Mode::Dark | dark_light::Mode::Unspecified => { - DARK.clone() + Theme::DARK } - dark_light::Mode::Light => LIGHT.clone(), + dark_light::Mode::Light => Theme::LIGHT, } }); - DEFAULT.clone() + *DEFAULT } } @@ -108,10 +101,8 @@ impl iced_anim::Animate for Theme { } fn update(&mut self, components: &mut impl Iterator) { - let mut colors = self.colorscheme; - colors.update(components); - - *self = Theme::new("Animating Theme", colors); + self.colorscheme.update(components); + self.name = "Animating Theme"; } fn distance_to(&self, end: &Self) -> Vec { @@ -119,10 +110,9 @@ impl iced_anim::Animate for Theme { } fn lerp(&mut self, start: &Self, end: &Self, progress: f32) { - let mut colors = self.colorscheme; - colors.lerp(&start.colorscheme, &end.colorscheme, progress); - - *self = Theme::new("Animating Theme", colors); + self.colorscheme + .lerp(&start.colorscheme, &end.colorscheme, progress); + self.name = "Animating Theme"; } } @@ -153,7 +143,7 @@ macro_rules! from_argb { let g = (hex & 0x0000ff00) >> 8; let b = (hex & 0x000000ff); - color!(r as u8, g as u8, b as u8, a) + ::iced_widget::core::color!(r as u8, g as u8, b as u8, a) }}; } diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index 3331196..7ec7f15 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -5,6 +5,7 @@ use iced::widget::{ use iced::{Element, Length}; use iced_anim::{Animated, Animation, Event}; use iced_dialog::dialog; +use material_theme::Theme; use material_theme::button::{elevated, filled_tonal, outlined, text}; use material_theme::container::{ error, error_container, inverse_surface, primary, primary_container, @@ -13,11 +14,10 @@ use material_theme::container::{ surface_container_lowest, tertiary, tertiary_container, }; use material_theme::text::surface_variant; -use material_theme::{DARK, LIGHT, Theme}; fn main() -> iced::Result { iced::application(State::default, State::update, State::view) - .theme(|state| state.theme.value().clone()) + .theme(|state| *state.theme.value()) .run() } @@ -190,7 +190,7 @@ impl State { column![ // Pick List pick_list( - [LIGHT.clone(), DARK.clone()], + Theme::ALL, Some(self.theme.target()), |theme| Message::SwitchTheme(theme.into()) ) -- cgit v1.2.3 From f9f854f124d4676e8c79adeda6bfaceba586805f Mon Sep 17 00:00:00 2001 From: pml68 Date: Fri, 25 Apr 2025 11:30:46 +0200 Subject: feat(material_theme): create an iced `Palette` for `iced::theme::Base` impl --- Cargo.toml | 3 ++- crates/material_theme/src/lib.rs | 44 +++++++++++++++++++++++++--------------- src/main.rs | 2 +- theme_test/Cargo.toml | 1 + 4 files changed, 32 insertions(+), 18 deletions(-) (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.toml b/Cargo.toml index a4abf56..0a7d769 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,8 @@ toml = "0.8.20" [workspace.dependencies.iced] git = "https://github.com/pml68/iced" branch = "feat/rehighlight-on-redraw" -features = ["image", "svg", "advanced", "tokio", "lazy"] +default-features = false +features = ["wgpu", "tiny-skia", "web-colors", "auto-detect-theme", "image", "svg", "advanced", "tokio", "lazy"] [build-dependencies] iced_fontello = "0.13.2" diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 25b6a9c..569b06c 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -29,6 +29,20 @@ pub mod text_input; pub mod toggler; pub mod utils; +#[allow(clippy::cast_precision_loss)] +macro_rules! from_argb { + ($hex:expr) => {{ + let hex = $hex as u32; + + let a = ((hex & 0xff000000) >> 24) as f32 / 255.0; + let r = (hex & 0x00ff0000) >> 16; + let g = (hex & 0x0000ff00) >> 8; + let b = (hex & 0x000000ff); + + ::iced_widget::core::color!(r as u8, g as u8, b as u8, a) + }}; +} + #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Theme { @@ -89,8 +103,20 @@ impl Base for Theme { } fn palette(&self) -> Option { - // TODO: create a Palette - None + let colors = self.colorscheme; + + 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, + ), + danger: colors.error.color, + }) } } @@ -133,20 +159,6 @@ pub struct ColorScheme { pub scrim: Color, } -#[allow(clippy::cast_precision_loss)] -macro_rules! from_argb { - ($hex:expr) => {{ - let hex = $hex as u32; - - let a = ((hex & 0xff000000) >> 24) as f32 / 255.0; - let r = (hex & 0x00ff0000) >> 16; - let g = (hex & 0x0000ff00) >> 8; - let b = (hex & 0x000000ff); - - ::iced_widget::core::color!(r as u8, g as u8, b as u8, a) - }}; -} - #[allow(clippy::cast_precision_loss)] impl ColorScheme { const DARK: Self = Self { diff --git a/src/main.rs b/src/main.rs index 0f1a1bf..5014077 100644 --- a/src/main.rs +++ b/src/main.rs @@ -146,8 +146,8 @@ impl IcedBuilder { Message::ConfigLoad(result) => match result { Ok(config) => { self.config = Arc::new(config); - self.theme.update(self.config.selected_theme().into()); + return if let Some(path) = self.config.last_project.clone() { if path.exists() && path.is_file() { diff --git a/theme_test/Cargo.toml b/theme_test/Cargo.toml index 29fcdc8..300a7af 100644 --- a/theme_test/Cargo.toml +++ b/theme_test/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] iced.workspace = true +iced.features = ["debug"] iced_anim.workspace = true iced_dialog.workspace = true material_theme = { path = "../crates/material_theme", features = ["dialog", "animate"] } -- cgit v1.2.3 From 3bdd724119eb2dbcb2480e441cd13f82575c6536 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 26 Apr 2025 13:17:58 +0200 Subject: feat(material_theme): implement `image::Catalog` (under feature flag) --- Cargo.lock | 89 +++++++++++++++++--------------- crates/iced_drop/src/widget/droppable.rs | 2 + crates/material_theme/Cargo.toml | 2 + crates/material_theme/src/image.rs | 21 ++++++++ crates/material_theme/src/lib.rs | 2 + theme_test/src/main.rs | 4 +- 6 files changed, 78 insertions(+), 42 deletions(-) create mode 100644 crates/material_theme/src/image.rs (limited to 'crates/material_theme/src/lib.rs') diff --git a/Cargo.lock b/Cargo.lock index fd31403..9154eb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,7 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", "zerocopy 0.7.35", @@ -657,9 +657,9 @@ checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", @@ -1592,9 +1592,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -1941,7 +1941,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_core", "iced_debug", @@ -1958,7 +1958,7 @@ dependencies = [ [[package]] name = "iced_anim" version = "0.2.1" -source = "git+https://github.com/pml68/iced_anim#0c833c0943493c8fc22a60e138785e4c688ef8d8" +source = "git+https://github.com/pml68/iced_anim#c881a8a9c49b0536f4ee3c236ec24285eabc0d76" dependencies = [ "iced", "iced_anim_derive", @@ -1967,7 +1967,7 @@ dependencies = [ [[package]] name = "iced_anim_derive" version = "0.2.0" -source = "git+https://github.com/pml68/iced_anim#0c833c0943493c8fc22a60e138785e4c688ef8d8" +source = "git+https://github.com/pml68/iced_anim#c881a8a9c49b0536f4ee3c236ec24285eabc0d76" dependencies = [ "quote", "syn", @@ -1976,7 +1976,7 @@ dependencies = [ [[package]] name = "iced_beacon" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "bincode", "futures", @@ -2017,7 +2017,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "bitflags 2.9.0", "bytes", @@ -2046,7 +2046,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_beacon", "iced_core", @@ -2056,7 +2056,7 @@ dependencies = [ [[package]] name = "iced_devtools" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_debug", "iced_program", @@ -2066,7 +2066,7 @@ dependencies = [ [[package]] name = "iced_dialog" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced_dialog?branch=iced%2Fpersonal#6e901c21dbb259d337ed1ad2054da3862172b8b3" +source = "git+https://github.com/pml68/iced_dialog?branch=iced%2Fpersonal#57388a202159052ccc771bfb537bd46e8f3b9e70" dependencies = [ "iced_core", "iced_widget", @@ -2096,7 +2096,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "futures", "iced_core", @@ -2110,7 +2110,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "bitflags 2.9.0", "bytemuck", @@ -2131,7 +2131,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_graphics", "iced_runtime", @@ -2140,7 +2140,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2152,7 +2152,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "bytes", "iced_core", @@ -2165,7 +2165,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "bytemuck", "cosmic-text", @@ -2182,7 +2182,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "bitflags 2.9.0", "bytemuck", @@ -2203,7 +2203,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_renderer", "iced_runtime", @@ -2221,7 +2221,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0-dev" -source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#b6433c4637ddf4697154feeb14c66e06200e4e1b" +source = "git+https://github.com/pml68/iced?branch=feat%2Frehighlight-on-redraw#64d4a365f3eb25f716c0632316d46d82bfe818c6" dependencies = [ "iced_debug", "iced_program", @@ -3636,7 +3636,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy 0.8.25", ] [[package]] @@ -3806,7 +3806,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -3940,7 +3940,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] @@ -4060,7 +4060,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -4943,9 +4943,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -4956,9 +4956,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "900f6c86a685850b1bc9f6223b20125115ee3f31e01207d81655bbcc0aea9231" dependencies = [ "serde", "serde_spanned", @@ -4968,26 +4968,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28391a4201ba7eb1984cfeb6862c0b3ea2cfe23332298967c749dddc0d6cd976" + [[package]] name = "tower" version = "0.5.2" @@ -6136,9 +6143,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" dependencies = [ "memchr", ] @@ -6384,11 +6391,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive 0.8.25", ] [[package]] @@ -6404,9 +6411,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/crates/iced_drop/src/widget/droppable.rs b/crates/iced_drop/src/widget/droppable.rs index 947cf5b..196464a 100644 --- a/crates/iced_drop/src/widget/droppable.rs +++ b/crates/iced_drop/src/widget/droppable.rs @@ -418,6 +418,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, + _viewport: &iced::Rectangle, _translation: Vector, ) -> Option> { let state: &mut State = tree.state.downcast_mut::(); @@ -434,6 +435,7 @@ where &mut tree.children[0], layout, renderer, + _viewport, _translation, ) } diff --git a/crates/material_theme/Cargo.toml b/crates/material_theme/Cargo.toml index 941bc3e..30de38b 100644 --- a/crates/material_theme/Cargo.toml +++ b/crates/material_theme/Cargo.toml @@ -21,6 +21,8 @@ animate = ["dep:iced_anim"] dialog = ["dep:iced_dialog"] # Provides support for the markdown widget. markdown = ["iced_widget/markdown"] +# Provides support for the image widget. +image = ["iced_widget/image"] # Provides support for the SVG widget. svg = ["iced_widget/svg"] # Provides support for the QR code widget. diff --git a/crates/material_theme/src/image.rs b/crates/material_theme/src/image.rs new file mode 100644 index 0000000..de5942a --- /dev/null +++ b/crates/material_theme/src/image.rs @@ -0,0 +1,21 @@ +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) + } + + 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 569b06c..1b4f90e 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -9,6 +9,8 @@ pub mod combo_box; pub mod container; #[cfg(feature = "dialog")] pub mod dialog; +#[cfg(feature = "image")] +pub mod image; #[cfg(feature = "markdown")] pub mod markdown; pub mod menu; diff --git a/theme_test/src/main.rs b/theme_test/src/main.rs index 7ec7f15..b4a7731 100644 --- a/theme_test/src/main.rs +++ b/theme_test/src/main.rs @@ -1,6 +1,7 @@ use iced::widget::{ button, center, checkbox, column, container, horizontal_rule, pane_grid, - pick_list, radio, row, slider, text_editor, text_input, toggler, + pick_list, progress_bar, radio, row, slider, text_editor, text_input, + toggler, }; use iced::{Element, Length}; use iced_anim::{Animated, Animation, Event}; @@ -238,6 +239,7 @@ impl State { .height(Length::Shrink), slider(0.0..=100.0, self.value, Message::Slider) .step(0.1), + progress_bar(0.0..=100.0, self.value), horizontal_rule(1), // Toggler toggler(self.is_checked) -- 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/lib.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 From 0c12ef78b697c0f57ac1d35bcfcd089cb269a433 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 29 Apr 2025 23:32:01 +0200 Subject: fix(material_theme): broken serde impl on `Theme` --- crates/material_theme/src/lib.rs | 191 ++++++++++++++++++++++----------------- 1 file changed, 110 insertions(+), 81 deletions(-) (limited to 'crates/material_theme/src/lib.rs') diff --git a/crates/material_theme/src/lib.rs b/crates/material_theme/src/lib.rs index 7b6ac58..b04edf0 100644 --- a/crates/material_theme/src/lib.rs +++ b/crates/material_theme/src/lib.rs @@ -1,7 +1,8 @@ +use std::borrow::Cow; use std::sync::LazyLock; -use iced_widget::core::Color; use iced_widget::core::theme::{Base, Style}; +use iced_widget::core::{Color, color}; use utils::{lightness, mix}; pub mod button; @@ -49,6 +50,7 @@ macro_rules! from_argb { #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(from = "Custom", into = "Custom"))] pub enum Theme { Dark, Light, @@ -58,27 +60,22 @@ pub enum Theme { impl Theme { pub const ALL: &'static [Self] = &[Self::Dark, Self::Light]; - pub fn new(name: impl Into, colorscheme: ColorScheme) -> Self { + pub fn new( + name: impl Into>, + colorscheme: ColorScheme, + ) -> Self { Self::Custom(Custom { name: name.into(), - colorscheme, is_dark: lightness(colorscheme.surface.color) <= 0.5, + colorscheme, }) } - pub fn name(&self) -> &str { + pub fn name(&self) -> Cow<'static, 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, + Self::Dark => "Dark".into(), + Self::Light => "Light".into(), + Self::Custom(custom) => custom.name.clone(), } } @@ -89,6 +86,14 @@ impl Theme { Self::Custom(custom) => custom.is_dark, } } + + pub fn colors(&self) -> ColorScheme { + match self { + Self::Dark => ColorScheme::DARK, + Self::Light => ColorScheme::LIGHT, + Self::Custom(custom) => custom.colorscheme, + } + } } impl Default for Theme { @@ -144,7 +149,11 @@ impl iced_anim::Animate for Theme { fn update(&mut self, components: &mut impl Iterator) { let mut colorscheme = self.colors(); colorscheme.update(components); - *self = Self::new("Animating Theme", colorscheme); + *self = Self::Custom(Custom { + name: "Animating Theme".into(), + is_dark: self.is_dark(), + colorscheme, + }); } fn distance_to(&self, end: &Self) -> Vec { @@ -154,32 +163,52 @@ impl iced_anim::Animate for Theme { fn lerp(&mut self, start: &Self, end: &Self, progress: f32) { let mut colorscheme = self.colors(); colorscheme.lerp(&start.colors(), &end.colors(), progress); - *self = Self::new("Animating Theme", colorscheme); + *self = Self::Custom(Custom { + name: "Animating Theme".into(), + is_dark: self.is_dark(), + colorscheme, + }); } } #[derive(Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Custom { - pub name: String, + pub name: Cow<'static, str>, + pub is_dark: bool, #[cfg_attr(feature = "serde", serde(flatten))] pub colorscheme: ColorScheme, - pub is_dark: bool, +} + +impl From for Theme { + fn from(custom: Custom) -> Self { + Self::Custom(custom) + } +} + +impl From for Custom { + fn from(theme: Theme) -> Self { + Self { + name: theme.name(), + is_dark: theme.is_dark(), + colorscheme: theme.colors(), + } + } } impl Clone for Custom { fn clone(&self) -> Self { Self { name: self.name.clone(), - colorscheme: self.colorscheme, is_dark: self.is_dark, + colorscheme: self.colorscheme, } } fn clone_from(&mut self, source: &Self) { self.name.clone_from(&source.name); - self.colorscheme = source.colorscheme; self.is_dark = source.is_dark; + self.colorscheme = source.colorscheme; } } @@ -204,101 +233,101 @@ pub struct ColorScheme { impl ColorScheme { const DARK: Self = Self { primary: Primary { - color: from_argb!(0xff9bd4a1), - on_primary: from_argb!(0xff003916), - primary_container: from_argb!(0xff1b5129), - on_primary_container: from_argb!(0xffb6f1bb), + color: color!(0x9bd4a1), + on_primary: color!(0x003916), + primary_container: color!(0x1b5129), + on_primary_container: color!(0xb6f1bb), }, secondary: Secondary { - color: from_argb!(0xffb8ccb6), - on_secondary: from_argb!(0xff233425), - secondary_container: from_argb!(0xff394b3a), - on_secondary_container: from_argb!(0xffd3e8d1), + color: color!(0xb8ccb6), + on_secondary: color!(0x233425), + secondary_container: color!(0x394b3a), + on_secondary_container: color!(0xd3e8d1), }, tertiary: Tertiary { - color: from_argb!(0xffa1ced7), - on_tertiary: from_argb!(0xff00363e), - tertiary_container: from_argb!(0xff1f4d55), - on_tertiary_container: from_argb!(0xffbdeaf4), + color: color!(0xa1ced7), + on_tertiary: color!(0x00363e), + tertiary_container: color!(0x1f4d55), + on_tertiary_container: color!(0xbdeaf4), }, error: Error { - color: from_argb!(0xffffb4ab), - on_error: from_argb!(0xff690005), - error_container: from_argb!(0xff93000a), - on_error_container: from_argb!(0xffffdad6), + color: color!(0xffb4ab), + on_error: color!(0x690005), + error_container: color!(0x93000a), + on_error_container: color!(0xffdad6), }, surface: Surface { - color: from_argb!(0xff101510), - on_surface: from_argb!(0xffe0e4dc), - on_surface_variant: from_argb!(0xffc1c9be), + color: color!(0x101510), + on_surface: color!(0xe0e4dc), + on_surface_variant: color!(0xc1c9be), surface_container: SurfaceContainer { - lowest: from_argb!(0xff0b0f0b), - low: from_argb!(0xff181d18), - base: from_argb!(0xff1c211c), - high: from_argb!(0xff262b26), - highest: from_argb!(0xff313631), + lowest: color!(0x0b0f0b), + low: color!(0x181d18), + base: color!(0x1c211c), + high: color!(0x262b26), + highest: color!(0x313631), }, }, inverse: Inverse { - inverse_surface: from_argb!(0xffe0e4dc), - inverse_on_surface: from_argb!(0xff2d322c), - inverse_primary: from_argb!(0xff34693f), + inverse_surface: color!(0xe0e4dc), + inverse_on_surface: color!(0x2d322c), + inverse_primary: color!(0x34693f), }, outline: Outline { - color: from_argb!(0xff8b9389), - variant: from_argb!(0xff414941), + color: color!(0x8b9389), + variant: color!(0x414941), }, - shadow: from_argb!(0xff000000), + shadow: color!(0x000000), scrim: from_argb!(0x4d000000), }; const LIGHT: Self = Self { primary: Primary { - color: from_argb!(0xff34693f), - on_primary: from_argb!(0xffffffff), - primary_container: from_argb!(0xffb6f1bb), - on_primary_container: from_argb!(0xff1b5129), + color: color!(0x34693f), + on_primary: color!(0xffffff), + primary_container: color!(0xb6f1bb), + on_primary_container: color!(0x1b5129), }, secondary: Secondary { - color: from_argb!(0xff516351), - on_secondary: from_argb!(0xffffffff), - secondary_container: from_argb!(0xffd3e8d1), - on_secondary_container: from_argb!(0xff394b3a), + color: color!(0x516351), + on_secondary: color!(0xffffff), + secondary_container: color!(0xd3e8d1), + on_secondary_container: color!(0x394b3a), }, tertiary: Tertiary { - color: from_argb!(0xff39656d), - on_tertiary: from_argb!(0xffffffff), - tertiary_container: from_argb!(0xffbdeaf4), - on_tertiary_container: from_argb!(0xff1f4d55), + color: color!(0x39656d), + on_tertiary: color!(0xffffff), + tertiary_container: color!(0xbdeaf4), + on_tertiary_container: color!(0x1f4d55), }, error: Error { - color: from_argb!(0xffba1a1a), - on_error: from_argb!(0xffffffff), - error_container: from_argb!(0xffffdad6), - on_error_container: from_argb!(0xff93000a), + color: color!(0xba1a1a), + on_error: color!(0xffffff), + error_container: color!(0xffdad6), + on_error_container: color!(0x93000a), }, surface: Surface { - color: from_argb!(0xfff7fbf2), - on_surface: from_argb!(0xff181d18), - on_surface_variant: from_argb!(0xff414941), + color: color!(0xf7fbf2), + on_surface: color!(0x181d18), + on_surface_variant: color!(0x414941), surface_container: SurfaceContainer { - lowest: from_argb!(0xffffffff), - low: from_argb!(0xfff1f5ed), - base: from_argb!(0xffebefe7), - high: from_argb!(0xffe5e9e1), - highest: from_argb!(0xffe0e4dc), + lowest: color!(0xffffff), + low: color!(0xf1f5ed), + base: color!(0xebefe7), + high: color!(0xe5e9e1), + highest: color!(0xe0e4dc), }, }, inverse: Inverse { - inverse_surface: from_argb!(0xff2d322c), - inverse_on_surface: from_argb!(0xffeef2ea), - inverse_primary: from_argb!(0xff9bd4a1), + inverse_surface: color!(0x2d322c), + inverse_on_surface: color!(0xeef2ea), + inverse_primary: color!(0x9bd4a1), }, outline: Outline { - color: from_argb!(0xff727970), - variant: from_argb!(0xffc1c9be), + color: color!(0x727970), + variant: color!(0xc1c9be), }, - shadow: from_argb!(0xff000000), + shadow: color!(0x000000), scrim: from_argb!(0x4d000000), }; } -- cgit v1.2.3