use palette::Srgba; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::fmt; use crate::composite::over; /// Theme Container colors of a theme, can be a theme background container, primary container, or secondary container #[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] pub struct Container { /// the color of the container pub base: C, /// the color of components in the container pub component: Component, /// the color of dividers in the container pub divider: C, /// the color of text in the container pub on: C, } impl Container where C: Clone + fmt::Debug + Default + Into + From + Serialize + DeserializeOwned, { /// convert to srgba pub fn into_srgba(self) -> Container { Container { base: self.base.into(), component: self.component.into_srgba(), divider: self.divider.into(), on: self.on.into(), } } pub(crate) fn new(component: Component, bg: C, on_bg: C) -> Self { let mut divider_c: Srgba = on_bg.clone().into(); divider_c.alpha = 0.2; let divider = over(divider_c.clone(), bg.clone()); Self { base: bg, component, divider: divider.into(), on: on_bg, } } } /// The colors for a widget of the Cosmic theme #[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize, Eq)] pub struct Component { /// The base color of the widget pub base: C, /// The color of the widget when it is hovered pub hover: C, /// the color of the widget when it is pressed pub pressed: C, /// the color of the widget when it is selected pub selected: C, /// the color of the widget when it is selected pub selected_text: C, /// the color of the widget when it is focused pub focus: C, /// the color of dividers for this widget pub divider: C, /// the color of text for this widget pub on: C, // the color of text with opacity 80 for this widget // pub text_opacity_80: C, /// the color of the widget when it is disabled 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 where C: Clone + fmt::Debug + Default + Into + From + Serialize + DeserializeOwned, { /// get @hover_state_color pub fn hover_state_color(&self) -> Srgba { self.hover.clone().into() } /// get @pressed_state_color pub fn pressed_state_color(&self) -> Srgba { self.pressed.clone().into() } /// get @selected_state_color pub fn selected_state_color(&self) -> Srgba { self.selected.clone().into() } /// get @selected_state_text_color pub fn selected_state_text_color(&self) -> Srgba { self.selected_text.clone().into() } /// get @focus_color pub fn focus_color(&self) -> Srgba { self.focus.clone().into() } /// convert to srgba pub fn into_srgba(self) -> Component { Component { base: self.base.into(), hover: self.hover.into(), pressed: self.pressed.into(), selected: self.selected.into(), selected_text: self.selected_text.into(), focus: self.focus.into(), divider: self.divider.into(), on: self.on.into(), disabled: self.disabled.into(), on_disabled: self.on_disabled.into(), border: self.border.into(), disabled_border: self.disabled_border.into(), } } /// helper for producing a component from a base color a neutral and an accent pub fn colored_component(base: C, neutral: C, accent: C) -> Self { let neutral = neutral.clone().into(); let mut neutral_05 = neutral.clone(); let mut neutral_10 = neutral.clone(); let mut neutral_20 = neutral.clone(); neutral_05.alpha = 0.05; neutral_10.alpha = 0.1; neutral_20.alpha = 0.2; let base: Srgba = base.into(); let mut base_50 = base.clone(); base_50.alpha *= 0.5; let on_20 = neutral.clone(); let mut on_50 = on_20.clone(); on_50.alpha = 0.5; Component { base: base.clone().into(), hover: over(neutral_10, base).into(), pressed: over(neutral_20, base).into(), selected: over(neutral_10, base).into(), selected_text: accent.clone(), divider: on_20.into(), on: neutral.into(), disabled: base_50.into(), on_disabled: on_50.into(), focus: accent, border: base.into(), disabled_border: base_50.into(), } } /// helper for producing a button component pub fn colored_button(base: C, overlay: C, on_button: C, accent: C) -> Self { let mut component = Component::colored_component(base, overlay, accent); component.on = on_button.clone(); let mut on_disabled = on_button.into(); on_disabled.alpha = 0.5; component.on_disabled = on_disabled.into(); component } /// helper for producing a component color theme pub fn component( base: C, component_state_overlay: C, 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(); let mut component_state_overlay_20 = component_state_overlay.clone(); component_state_overlay_10.alpha = 0.1; component_state_overlay_20.alpha = 0.2; let base = base.into(); let mut base_50 = base.clone(); base_50.alpha *= 0.5; let mut on_20 = on_component.clone().into(); let mut on_50 = on_20.clone(); 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(), pressed: over(component_state_overlay_20, base).into(), selected: over(component_state_overlay_10, base).into(), selected_text: accent.clone(), focus: accent.clone(), divider: if is_high_contrast { on_50.clone().into() } else { on_20.into() }, on: on_component.clone(), disabled: base_50.into(), on_disabled: on_50.into(), border: border.into(), disabled_border: disabled_border.into(), } } }