From ff83f893ef19531a9cefbdaaa3cd2c692137d7a3 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 7 Aug 2023 13:48:01 -0400 Subject: [PATCH] refactor: updates for buttons and checkboxes --- cosmic-theme/src/model/derivation.rs | 19 +++- cosmic-theme/src/model/theme.rs | 152 ++++++++++++++++----------- examples/cosmic/src/window/demo.rs | 2 +- src/theme/mod.rs | 100 +++++++++--------- 4 files changed, 159 insertions(+), 114 deletions(-) diff --git a/cosmic-theme/src/model/derivation.rs b/cosmic-theme/src/model/derivation.rs index 26b82ca..e70aad9 100644 --- a/cosmic-theme/src/model/derivation.rs +++ b/cosmic-theme/src/model/derivation.rs @@ -70,6 +70,10 @@ pub struct Component { pub disabled: C, /// the color of text in the widget when it is disabled pub on_disabled: C, + /// the color of the border for the widget + pub border: C, + /// the color of the border for the widget when it is disabled + pub disabled_border: C, } impl Component @@ -109,6 +113,8 @@ where on: self.on.into(), disabled: self.disabled.into(), on_disabled: self.on_disabled.into(), + border: self.border.into(), + disabled_border: self.disabled_border.into(), } } @@ -124,7 +130,7 @@ where let base: Srgba = base.into(); let mut base_50 = base.clone(); - base_50.alpha = 0.5; + base_50.alpha *= 0.5; let on_20 = neutral.clone(); let mut on_50 = on_20.clone(); @@ -142,6 +148,8 @@ where disabled: base_50.into(), on_disabled: on_50.into(), focus: accent, + border: base.into(), + disabled_border: base_50.into(), } } @@ -152,6 +160,7 @@ where accent: C, on_component: C, is_high_contrast: bool, + border: C, ) -> Self { let component_state_overlay = component_state_overlay.clone().into(); let mut component_state_overlay_10 = component_state_overlay.clone(); @@ -161,7 +170,7 @@ where let base = base.into(); let mut base_50 = base.clone(); - base_50.alpha = 0.5; + base_50.alpha *= 0.5; let mut on_20 = on_component.clone().into(); let mut on_50 = on_20.clone(); @@ -169,6 +178,10 @@ where on_20.alpha = 0.2; on_50.alpha = 0.5; + let border = border.into(); + let mut disabled_border = border; + disabled_border.alpha *= 0.5; + Component { base: base.clone().into(), hover: over(component_state_overlay_10, base).into(), @@ -184,6 +197,8 @@ where on: on_component.clone(), disabled: base_50.into(), on_disabled: on_50.into(), + border: border.into(), + disabled_border: disabled_border.into(), } } } diff --git a/cosmic-theme/src/model/theme.rs b/cosmic-theme/src/model/theme.rs index 61a1374..f6e80b2 100644 --- a/cosmic-theme/src/model/theme.rs +++ b/cosmic-theme/src/model/theme.rs @@ -4,8 +4,8 @@ use crate::{ }; use cosmic_config::{Config, ConfigGet, ConfigSet, CosmicConfigEntry}; use palette::{IntoColor, Srgb, Srgba}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{fmt, num::NonZeroUsize}; +use serde::{Deserialize, Serialize}; +use std::num::NonZeroUsize; #[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] /// Theme layer type @@ -38,6 +38,8 @@ pub struct Theme { pub destructive: Component, /// warning element colors pub warning: Component, + /// button component styling + pub button: Component, /// palette pub palette: CosmicPaletteInner, /// spacing @@ -162,10 +164,27 @@ impl Theme { } } -impl Theme -where - C: Clone + fmt::Debug + Default + Into + From + Serialize + DeserializeOwned, -{ +impl Theme { + /// get the built in light theme + pub fn light_default() -> Self { + LIGHT_PALETTE.clone().into() + } + + /// get the built in dark theme + pub fn dark_default() -> Self { + DARK_PALETTE.clone().into() + } + + /// get the built in high contrast dark theme + pub fn high_contrast_dark_default() -> Self { + CosmicPalette::HighContrastDark(DARK_PALETTE.as_ref().clone()).into() + } + + /// get the built in high contrast light theme + pub fn high_contrast_light_default() -> Self { + CosmicPalette::HighContrastLight(LIGHT_PALETTE.as_ref().clone()).into() + } + /// Convert the theme to a high-contrast variant pub fn to_high_contrast(&self) -> Self { todo!(); @@ -174,134 +193,142 @@ where // TODO convenient getter functions for each named color variable /// get @accent_color pub fn accent_color(&self) -> Srgba { - self.accent.base.clone().into() + self.accent.base.clone() } /// get @success_color pub fn success_color(&self) -> Srgba { - self.success.base.clone().into() + self.success.base.clone() } /// get @destructive_color pub fn destructive_color(&self) -> Srgba { - self.destructive.base.clone().into() + self.destructive.base.clone() } /// get @warning_color pub fn warning_color(&self) -> Srgba { - self.warning.base.clone().into() + self.warning.base.clone() } // Containers /// get @bg_color pub fn bg_color(&self) -> Srgba { - self.background.base.clone().into() + self.background.base.clone() } /// get @bg_component_color pub fn bg_component_color(&self) -> Srgba { - self.background.component.base.clone().into() + self.background.component.base.clone() } /// get @primary_container_color pub fn primary_container_color(&self) -> Srgba { - self.primary.base.clone().into() + self.primary.base.clone() } /// get @primary_component_color pub fn primary_component_color(&self) -> Srgba { - self.primary.component.base.clone().into() + self.primary.component.base.clone() } /// get @secondary_container_color pub fn secondary_container_color(&self) -> Srgba { - self.secondary.base.clone().into() + self.secondary.base.clone() } /// get @secondary_component_color pub fn secondary_component_color(&self) -> Srgba { - self.secondary.component.base.clone().into() + self.secondary.component.base.clone() + } + /// get @button_bg_color + pub fn button_bg_color(&self) -> Srgba { + self.button.base.clone() } // Text /// get @on_bg_color pub fn on_bg_color(&self) -> Srgba { - self.background.on.clone().into() + self.background.on.clone() } /// get @on_bg_component_color pub fn on_bg_component_color(&self) -> Srgba { - self.background.component.on.clone().into() + self.background.component.on.clone() } /// get @on_primary_color pub fn on_primary_container_color(&self) -> Srgba { - self.primary.on.clone().into() + self.primary.on.clone() } /// get @on_primary_component_color pub fn on_primary_component_color(&self) -> Srgba { - self.primary.component.on.clone().into() + self.primary.component.on.clone() } /// get @on_secondary_color pub fn on_secondary_container_color(&self) -> Srgba { - self.secondary.on.clone().into() + self.secondary.on.clone() } /// get @on_secondary_component_color pub fn on_secondary_component_color(&self) -> Srgba { - self.secondary.component.on.clone().into() + self.secondary.component.on.clone() } /// get @accent_text_color pub fn accent_text_color(&self) -> Srgba { - self.accent.base.clone().into() + self.accent.base.clone() } /// get @success_text_color pub fn success_text_color(&self) -> Srgba { - self.success.base.clone().into() + self.success.base.clone() } /// get @warning_text_color pub fn warning_text_color(&self) -> Srgba { - self.warning.base.clone().into() + self.warning.base.clone() } /// get @destructive_text_color pub fn destructive_text_color(&self) -> Srgba { - self.destructive.base.clone().into() + self.destructive.base.clone() } /// get @on_accent_color pub fn on_accent_color(&self) -> Srgba { - self.accent.on.clone().into() + self.accent.on.clone() } /// get @on_success_color pub fn on_success_color(&self) -> Srgba { - self.success.on.clone().into() + self.success.on.clone() } /// get @oon_warning_color pub fn on_warning_color(&self) -> Srgba { - self.warning.on.clone().into() + self.warning.on.clone() } /// get @on_destructive_color pub fn on_destructive_color(&self) -> Srgba { - self.destructive.on.clone().into() + self.destructive.on.clone() + } + /// get @button_color + pub fn button_color(&self) -> Srgba { + self.button.on.clone() } // Borders and Dividers /// get @bg_divider pub fn bg_divider(&self) -> Srgba { - self.background.divider.clone().into() + self.background.divider.clone() } /// get @bg_component_divider pub fn bg_component_divider(&self) -> Srgba { - self.background.component.divider.clone().into() + self.background.component.divider.clone() } /// get @primary_container_divider pub fn primary_container_divider(&self) -> Srgba { - self.primary.divider.clone().into() + self.primary.divider.clone() } /// get @primary_component_divider pub fn primary_component_divider(&self) -> Srgba { - self.primary.component.divider.clone().into() + self.primary.component.divider.clone() } /// get @secondary_container_divider pub fn secondary_container_divider(&self) -> Srgba { - self.secondary.divider.clone().into() + self.secondary.divider.clone() } - /// get @secondary_component_divider - pub fn secondary_component_divider(&self) -> Srgba { - self.secondary.component.divider.clone().into() + /// get @button_divider + pub fn button_divider(&self) -> Srgba { + self.button.divider.clone() } /// get @window_header_bg pub fn window_header_bg(&self) -> Srgba { - self.background.base.clone().into() + self.background.base.clone() } /// get @space_none @@ -371,28 +398,6 @@ where } } -impl Theme { - /// get the built in light theme - pub fn light_default() -> Self { - LIGHT_PALETTE.clone().into() - } - - /// get the built in dark theme - pub fn dark_default() -> Self { - DARK_PALETTE.clone().into() - } - - /// get the built in high contrast dark theme - pub fn high_contrast_dark_default() -> Self { - CosmicPalette::HighContrastDark(DARK_PALETTE.as_ref().clone()).into() - } - - /// get the built in high contrast light theme - pub fn high_contrast_light_default() -> Self { - CosmicPalette::HighContrastLight(LIGHT_PALETTE.as_ref().clone()).into() - } -} - impl From> for Theme where CosmicPalette: Into>, @@ -643,6 +648,7 @@ impl ThemeBuilder { accent.clone(), on_bg_component, is_high_contrast, + p_ref.neutral_8, ); let primary_index = color_index(primary_container_bg, step_array.len()); @@ -661,6 +667,7 @@ impl ThemeBuilder { accent.clone(), on_primary_component, is_high_contrast, + p_ref.neutral_8, ); let secondary_index = color_index(secondary_container_bg, step_array.len()); @@ -679,7 +686,22 @@ impl ThemeBuilder { accent.clone(), on_secondary_component, is_high_contrast, + p_ref.neutral_8, ); + let neutral_7 = p_ref.neutral_7; + let mut button_bg = neutral_7; + button_bg.alpha = 0.25; + let neutral_10 = p_ref.neutral_10; + let mut button_hover_overlay = neutral_10; + button_hover_overlay.alpha = 0.10; + let mut button_press_overlay = neutral_10; + button_press_overlay.alpha = 0.20; + + let mut button_disabled_bg = button_bg; + button_disabled_bg.alpha *= 0.5; + let button_border = p_ref.neutral_8.clone(); + let mut button_disabled_border = button_border; + button_disabled_border.alpha *= 0.5; let mut theme: Theme = Theme { name: palette.name().to_string(), @@ -736,6 +758,14 @@ impl ThemeBuilder { p_ref.neutral_0.to_owned(), accent.clone(), ), + button: Component::component( + button_bg, + p_ref.neutral_10, + accent, + p_ref.neutral_9, + is_high_contrast, + p_ref.neutral_8, + ), palette: palette.inner(), spacing, corner_radii, diff --git a/examples/cosmic/src/window/demo.rs b/examples/cosmic/src/window/demo.rs index 85e396d..c1856eb 100644 --- a/examples/cosmic/src/window/demo.rs +++ b/examples/cosmic/src/window/demo.rs @@ -492,7 +492,7 @@ impl State { )) .layer(cosmic::cosmic_theme::Layer::Secondary) .padding(16) - .style(cosmic::theme::Container::Secondary) + .style(cosmic::theme::Container::Background) .into(), text_input( "Type to search apps or type “?” for more options...", diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 6acb13e..b335b94 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -62,6 +62,8 @@ lazy_static::lazy_static! { on: CosmicColor::new(0.0, 0.0, 0.0, 0.0), on_disabled: CosmicColor::new(0.0, 0.0, 0.0, 0.0), divider: CosmicColor::new(0.0, 0.0, 0.0, 0.0), + border: CosmicColor::new(0.0, 0.0, 0.0, 0.0), + disabled_border: CosmicColor::new(0.0, 0.0, 0.0, 0.0), }; } @@ -355,69 +357,69 @@ impl checkbox::StyleSheet for Theme { type Style = Checkbox; fn active(&self, style: &Self::Style, is_checked: bool) -> checkbox::Appearance { - let palette = self.cosmic(); - let neutral_7 = palette.palette.neutral_10; + let cosmic = self.cosmic(); + let corners = &cosmic.corner_radii; match style { Checkbox::Primary => checkbox::Appearance { background: Background::Color(if is_checked { - palette.accent.base.into() + cosmic.accent.base.into() } else { - palette.background.base.into() + cosmic.button.base.into() }), - icon_color: palette.accent.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.accent.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { - palette.accent.base + cosmic.accent.base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None, }, Checkbox::Secondary => checkbox::Appearance { background: Background::Color(if is_checked { - palette.background.component.base.into() + cosmic.background.component.base.into() } else { - palette.background.base.into() + cosmic.background.base.into() }), - icon_color: palette.background.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.background.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, - border_color: neutral_7.into(), + border_color: cosmic.button.border.into(), text_color: None, }, Checkbox::Success => checkbox::Appearance { background: Background::Color(if is_checked { - palette.success.base.into() + cosmic.success.base.into() } else { - palette.background.base.into() + cosmic.button.base.into() }), - icon_color: palette.success.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.success.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { - palette.success.base + cosmic.success.base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None, }, Checkbox::Danger => checkbox::Appearance { background: Background::Color(if is_checked { - palette.destructive.base.into() + cosmic.destructive.base.into() } else { - palette.background.base.into() + cosmic.button.base.into() }), - icon_color: palette.destructive.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.destructive.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { - palette.destructive.base + cosmic.destructive.base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None, @@ -426,25 +428,23 @@ impl checkbox::StyleSheet for Theme { } fn hovered(&self, style: &Self::Style, is_checked: bool) -> checkbox::Appearance { - let palette = self.cosmic(); - let mut neutral_10 = palette.palette.neutral_10; - let neutral_7 = palette.palette.neutral_10; + let cosmic = self.cosmic(); + let corners = &cosmic.corner_radii; - neutral_10.alpha = 0.1; match style { Checkbox::Primary => checkbox::Appearance { background: Background::Color(if is_checked { - palette.accent.base.into() + cosmic.accent.base.into() } else { - neutral_10.into() + cosmic.button.base.into() }), - icon_color: palette.accent.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.accent.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { - palette.accent.base + cosmic.accent.base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None, @@ -453,49 +453,49 @@ impl checkbox::StyleSheet for Theme { background: Background::Color(if is_checked { self.current_container().base.into() } else { - neutral_10.into() + cosmic.button.base.into() }), icon_color: self.current_container().on.into(), - border_radius: 4.0.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { self.current_container().base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None, }, Checkbox::Success => checkbox::Appearance { background: Background::Color(if is_checked { - palette.success.base.into() + cosmic.success.base.into() } else { - neutral_10.into() + cosmic.button.base.into() }), - icon_color: palette.success.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.success.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { - palette.success.base + cosmic.success.base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None, }, Checkbox::Danger => checkbox::Appearance { background: Background::Color(if is_checked { - palette.destructive.base.into() + cosmic.destructive.base.into() } else { - neutral_10.into() + cosmic.button.base.into() }), - icon_color: palette.destructive.on.into(), - border_radius: 4.0.into(), + icon_color: cosmic.destructive.on.into(), + border_radius: corners.radius_xs.into(), border_width: if is_checked { 0.0 } else { 1.0 }, border_color: if is_checked { - palette.destructive.base + cosmic.destructive.base } else { - neutral_7 + cosmic.button.border } .into(), text_color: None,