wip: calculate theme using new method

This commit is contained in:
Ashley Wulber 2023-08-03 19:30:08 -04:00 committed by Ashley Wulber
parent 607883e4ad
commit a618c1b94a
6 changed files with 257 additions and 423 deletions

View file

@ -2,7 +2,7 @@ use palette::Srgba;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::fmt;
use crate::{composite::over, CosmicPalette};
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)]
@ -31,134 +31,20 @@ where
}
}
pub(crate) fn new(
palette: CosmicPalette<C>,
container_type: ComponentType,
bg: C,
on_bg: C,
) -> Self {
pub(crate) fn new(component: Component<C>, 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: (palette, container_type).into(),
component,
divider: divider.into(),
on: on_bg,
}
}
}
impl<C> From<(CosmicPalette<C>, ContainerType)> for Container<C>
where
C: Clone + fmt::Debug + Default + Into<Srgba> + From<Srgba> + Serialize + DeserializeOwned,
{
fn from((p, t): (CosmicPalette<C>, ContainerType)) -> Self {
match (p, t) {
(CosmicPalette::Dark(p), ContainerType::Background) => Self::new(
CosmicPalette::Dark(p.clone()),
ComponentType::Background,
p.gray_1.clone(),
p.neutral_7.clone(),
),
(CosmicPalette::Dark(p), ContainerType::Primary) => Self::new(
CosmicPalette::Dark(p.clone()),
ComponentType::Primary,
p.gray_2.clone(),
p.neutral_8.clone(),
),
(CosmicPalette::Dark(p), ContainerType::Secondary) => Self::new(
CosmicPalette::Dark(p.clone()),
ComponentType::Secondary,
p.gray_3.clone(),
p.neutral_8.clone(),
),
(CosmicPalette::HighContrastDark(p), ContainerType::Background) => Self::new(
CosmicPalette::HighContrastDark(p.clone()),
ComponentType::Background,
p.gray_1.clone(),
p.neutral_8.clone(),
),
(CosmicPalette::HighContrastDark(p), ContainerType::Primary) => Self::new(
CosmicPalette::HighContrastDark(p.clone()),
ComponentType::Primary,
p.gray_2.clone(),
p.neutral_9.clone(),
),
(CosmicPalette::HighContrastDark(p), ContainerType::Secondary) => Self::new(
CosmicPalette::HighContrastDark(p.clone()),
ComponentType::Secondary,
p.gray_3.clone(),
p.neutral_9.clone(),
),
(CosmicPalette::Light(p), ContainerType::Background) => Self::new(
CosmicPalette::Light(p.clone()),
ComponentType::Background,
p.gray_1.clone(),
p.neutral_9.clone(),
),
(CosmicPalette::Light(p), ContainerType::Primary) => Self::new(
CosmicPalette::Light(p.clone()),
ComponentType::Primary,
p.gray_2.clone(),
p.neutral_8.clone(),
),
(CosmicPalette::Light(p), ContainerType::Secondary) => Self::new(
CosmicPalette::Light(p.clone()),
ComponentType::Secondary,
p.gray_3.clone(),
p.neutral_8.clone(),
),
(CosmicPalette::HighContrastLight(p), ContainerType::Background) => Self::new(
CosmicPalette::HighContrastLight(p.clone()),
ComponentType::Background,
p.gray_1.clone(),
p.neutral_10.clone(),
),
(CosmicPalette::HighContrastLight(p), ContainerType::Primary) => Self::new(
CosmicPalette::HighContrastLight(p.clone()),
ComponentType::Primary,
p.gray_2.clone(),
p.neutral_9.clone(),
),
(CosmicPalette::HighContrastLight(p), ContainerType::Secondary) => Self::new(
CosmicPalette::HighContrastLight(p.clone()),
ComponentType::Secondary,
p.gray_3.clone(),
p.neutral_9.clone(),
),
}
}
}
/// The type of the container
#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
pub enum ContainerType {
/// Background type
Background,
/// Primary type
Primary,
/// Secondary type
Secondary,
}
impl Default for ContainerType {
fn default() -> Self {
Self::Background
}
}
impl fmt::Display for ContainerType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ContainerType::Background => write!(f, "Background"),
ContainerType::Primary => write!(f, "Primary Container"),
ContainerType::Secondary => write!(f, "Secondary Container"),
}
}
}
/// The colors for a widget of the Cosmic theme
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize, Eq)]
pub struct Component<C> {
@ -263,8 +149,6 @@ where
pub fn component(
base: C,
component_state_overlay: C,
base_overlay: C,
base_overlay_alpha: f32,
accent: C,
on_component: C,
is_high_contrast: bool,
@ -276,9 +160,6 @@ where
component_state_overlay_20.alpha = 0.2;
let base = base.into();
let mut base_overlay = base_overlay.into();
base_overlay.alpha = base_overlay_alpha;
let base = over(base_overlay, base);
let mut base_50 = base.clone();
base_50.alpha = 0.5;
@ -306,175 +187,3 @@ where
}
}
}
/// Derived theme element from a palette and constraints
#[derive(Debug)]
pub struct Derivation<E> {
/// Derived theme element
pub derived: E,
/// Derivation errors (Failed constraints)
pub errors: Vec<anyhow::Error>,
}
pub(crate) enum ComponentType {
Background,
Primary,
Secondary,
Destructive,
Warning,
Success,
Accent,
}
impl<C> From<(CosmicPalette<C>, ComponentType)> for Component<C>
where
C: Clone + fmt::Debug + Default + Into<Srgba> + From<Srgba> + Serialize + DeserializeOwned,
{
fn from((p, t): (CosmicPalette<C>, ComponentType)) -> Self {
match (p, t) {
(CosmicPalette::Dark(p), ComponentType::Background) => Self::component(
p.gray_1,
p.neutral_0,
p.neutral_10,
0.08,
p.accent,
p.neutral_8,
false,
),
(CosmicPalette::Dark(p), ComponentType::Primary) => Self::component(
p.gray_2,
p.neutral_0,
p.neutral_10,
0.08,
p.accent,
p.neutral_8,
false,
),
(CosmicPalette::Dark(p), ComponentType::Secondary) => Self::component(
p.gray_3,
p.neutral_0,
p.neutral_10,
0.08,
p.accent,
p.neutral_9,
false,
),
(CosmicPalette::HighContrastDark(p), ComponentType::Background) => Self::component(
p.gray_1,
p.neutral_0,
p.neutral_10,
0.08,
p.accent,
p.neutral_9,
true,
),
(CosmicPalette::HighContrastDark(p), ComponentType::Primary) => Self::component(
p.gray_2,
p.neutral_0,
p.neutral_10,
0.08,
p.accent,
p.neutral_9,
true,
),
(CosmicPalette::HighContrastDark(p), ComponentType::Secondary) => Self::component(
p.gray_3,
p.neutral_0,
p.neutral_10.clone(),
0.08,
p.accent,
p.neutral_10,
true,
),
(CosmicPalette::Light(p), ComponentType::Background) => Component::component(
p.gray_1.clone(),
p.neutral_0.clone(),
p.neutral_0,
0.75,
p.accent.clone(),
p.neutral_8,
false,
),
(CosmicPalette::Light(p), ComponentType::Primary) => Component::component(
p.gray_2.clone(),
p.neutral_0.clone(),
p.neutral_0,
0.9,
p.accent.clone(),
p.neutral_8,
false,
),
(CosmicPalette::Light(p), ComponentType::Secondary) => Component::component(
p.gray_3.clone(),
p.neutral_0.clone(),
p.neutral_0,
1.0,
p.accent.clone(),
p.neutral_8,
false,
),
(CosmicPalette::HighContrastLight(p), ComponentType::Background) => {
Component::component(
p.gray_1.clone(),
p.neutral_0.clone(),
p.neutral_0,
0.75,
p.accent.clone(),
p.neutral_9,
true,
)
}
(CosmicPalette::HighContrastLight(p), ComponentType::Primary) => Component::component(
p.gray_2.clone(),
p.neutral_0.clone(),
p.neutral_0,
0.9,
p.accent.clone(),
p.neutral_9,
true,
),
(CosmicPalette::HighContrastLight(p), ComponentType::Secondary) => {
Component::component(
p.gray_3.clone(),
p.neutral_0.clone(),
p.neutral_0,
1.0,
p.accent.clone(),
p.neutral_9,
true,
)
}
(CosmicPalette::Dark(p), ComponentType::Destructive)
| (CosmicPalette::Light(p), ComponentType::Destructive)
| (CosmicPalette::HighContrastLight(p), ComponentType::Destructive)
| (CosmicPalette::HighContrastDark(p), ComponentType::Destructive) => {
Component::colored_component(p.red.clone(), p.neutral_1.clone(), p.blue.clone())
}
(CosmicPalette::Dark(p), ComponentType::Warning)
| (CosmicPalette::Light(p), ComponentType::Warning)
| (CosmicPalette::HighContrastLight(p), ComponentType::Warning)
| (CosmicPalette::HighContrastDark(p), ComponentType::Warning) => {
Component::colored_component(p.yellow.clone(), p.neutral_0, p.blue.clone())
}
(CosmicPalette::Dark(p), ComponentType::Success)
| (CosmicPalette::Light(p), ComponentType::Success)
| (CosmicPalette::HighContrastLight(p), ComponentType::Success)
| (CosmicPalette::HighContrastDark(p), ComponentType::Success) => {
Component::colored_component(p.green.clone(), p.neutral_0, p.blue.clone())
}
(CosmicPalette::Dark(p), ComponentType::Accent)
| (CosmicPalette::Light(p), ComponentType::Accent)
| (CosmicPalette::HighContrastDark(p), ComponentType::Accent)
| (CosmicPalette::HighContrastLight(p), ComponentType::Accent) => {
Component::colored_component(p.blue.clone(), p.neutral_0, p.blue.clone())
}
}
}
}

View file

@ -0,0 +1,5 @@
#[derive(Default)]
pub struct Layout {
corner_radii: [u32;4],
}

View file

@ -1,11 +1,11 @@
use crate::{
util::CssColor, Component, ComponentType, Container, ContainerType, CornerRadii, CosmicPalette,
CosmicPaletteInner, Spacing, DARK_PALETTE, LIGHT_PALETTE, NAME, THEME_DIR,
steps::steps, Component, Container, CornerRadii, CosmicPalette, CosmicPaletteInner, Spacing,
DARK_PALETTE, LIGHT_PALETTE, NAME, THEME_DIR,
};
use anyhow::Context;
use cosmic_config::{Config, ConfigGet, ConfigSet, CosmicConfigEntry};
use directories::{BaseDirsExt, ProjectDirsExt};
use palette::{Srgb, Srgba};
use palette::{FromColor, Oklcha, Srgb, Srgba};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
fmt,
@ -57,7 +57,7 @@ pub struct Theme<C> {
pub is_high_contrast: bool,
}
impl CosmicConfigEntry for Theme<CssColor> {
impl CosmicConfigEntry for Theme<Srgba> {
fn write_entry(&self, config: &Config) -> Result<(), cosmic_config::Error> {
let self_ = self.clone();
// TODO do as transaction
@ -86,35 +86,35 @@ impl CosmicConfigEntry for Theme<CssColor> {
Ok(name) => default.name = name,
Err(e) => errors.push(e),
}
match config.get::<Container<CssColor>>("background") {
match config.get::<Container<Srgba>>("background") {
Ok(background) => default.background = background,
Err(e) => errors.push(e),
}
match config.get::<Container<CssColor>>("primary") {
match config.get::<Container<Srgba>>("primary") {
Ok(primary) => default.primary = primary,
Err(e) => errors.push(e),
}
match config.get::<Container<CssColor>>("secondary") {
match config.get::<Container<Srgba>>("secondary") {
Ok(secondary) => default.secondary = secondary,
Err(e) => errors.push(e),
}
match config.get::<Component<CssColor>>("accent") {
match config.get::<Component<Srgba>>("accent") {
Ok(accent) => default.accent = accent,
Err(e) => errors.push(e),
}
match config.get::<Component<CssColor>>("success") {
match config.get::<Component<Srgba>>("success") {
Ok(success) => default.success = success,
Err(e) => errors.push(e),
}
match config.get::<Component<CssColor>>("destructive") {
match config.get::<Component<Srgba>>("destructive") {
Ok(destructive) => default.destructive = destructive,
Err(e) => errors.push(e),
}
match config.get::<Component<CssColor>>("warning") {
match config.get::<Component<Srgba>>("warning") {
Ok(warning) => default.warning = warning,
Err(e) => errors.push(e),
}
match config.get::<CosmicPaletteInner<CssColor>>("palette") {
match config.get::<CosmicPaletteInner<Srgba>>("palette") {
Ok(palette) => default.palette = palette,
Err(e) => errors.push(e),
}
@ -144,12 +144,6 @@ impl CosmicConfigEntry for Theme<CssColor> {
}
impl Default for Theme<Srgba> {
fn default() -> Self {
Theme::<CssColor>::dark_default().into_srgba()
}
}
impl Default for Theme<CssColor> {
fn default() -> Self {
Self::dark_default()
}
@ -426,7 +420,7 @@ where
}
}
impl Theme<CssColor> {
impl Theme<Srgba> {
/// get the built in light theme
pub fn light_default() -> Self {
LIGHT_PALETTE.clone().into()
@ -446,54 +440,14 @@ impl Theme<CssColor> {
pub fn high_contrast_light_default() -> Self {
CosmicPalette::HighContrastLight(LIGHT_PALETTE.as_ref().clone()).into()
}
/// convert to srgba
pub fn into_srgba(self) -> Theme<Srgba> {
Theme {
name: self.name,
background: self.background.into_srgba(),
primary: self.primary.into_srgba(),
secondary: self.secondary.into_srgba(),
accent: self.accent.into_srgba(),
success: self.success.into_srgba(),
destructive: self.destructive.into_srgba(),
warning: self.warning.into_srgba(),
palette: self.palette.into(),
is_dark: self.is_dark,
is_high_contrast: self.is_high_contrast,
corner_radii: self.corner_radii,
spacing: self.spacing,
}
}
}
impl<C> From<CosmicPalette<C>> for Theme<C>
impl<C> From<CosmicPalette<C>> for Theme<Srgba>
where
C: Clone + fmt::Debug + Default + Into<Srgba> + From<Srgba> + Serialize + DeserializeOwned,
CosmicPalette<C>: Into<CosmicPalette<Srgba>>,
{
fn from(p: CosmicPalette<C>) -> Self {
let is_dark = p.is_dark();
let is_high_contrast = p.is_high_contrast();
Self {
name: p.name().to_string(),
background: (p.clone(), ContainerType::Background).into(),
primary: (p.clone(), ContainerType::Primary).into(),
secondary: (p.clone(), ContainerType::Secondary).into(),
accent: (p.clone(), ComponentType::Accent).into(),
success: (p.clone(), ComponentType::Success).into(),
destructive: (p.clone(), ComponentType::Destructive).into(),
warning: (p.clone(), ComponentType::Warning).into(),
palette: match p {
CosmicPalette::Dark(p) => p.into(),
CosmicPalette::Light(p) => p.into(),
CosmicPalette::HighContrastLight(p) => p.into(),
CosmicPalette::HighContrastDark(p) => p.into(),
},
is_dark,
is_high_contrast,
spacing: Spacing::default(),
corner_radii: CornerRadii::default(),
}
ThemeBuilder::palette(p.into()).build()
}
}
@ -506,6 +460,7 @@ pub struct ThemeBuilder {
neutral_tint: Option<Srgb>,
bg_color: Option<Srgba>,
primary_container_bg: Option<Srgba>,
secondary_container_bg: Option<Srgba>,
text_tint: Option<Srgb>,
accent: Option<Srgb>,
}
@ -520,6 +475,7 @@ impl Default for ThemeBuilder {
text_tint: Default::default(),
bg_color: Default::default(),
primary_container_bg: Default::default(),
secondary_container_bg: Default::default(),
accent: Default::default(),
}
}
@ -560,6 +516,14 @@ impl ThemeBuilder {
}
}
/// Get a builder that is initialized with the provided palette
pub fn palette(palette: CosmicPalette<Srgba>) -> Self {
Self {
palette,
..Default::default()
}
}
/// set the spacing of the builder
pub fn spacing(mut self, spacing: Spacing) -> Self {
self.spacing = spacing;
@ -612,6 +576,7 @@ impl ThemeBuilder {
text_tint,
bg_color,
primary_container_bg,
secondary_container_bg,
accent,
} = self;
@ -619,15 +584,175 @@ impl ThemeBuilder {
palette.as_mut().accent = accent.into();
}
// TODO apply the customizations
// TODO apply the tint customizations
let is_dark = palette.is_dark();
let is_high_contrast = palette.is_high_contrast();
if let Some(accent) = accent {
palette.as_mut().accent = accent.into();
}
let p_ref = palette.as_ref();
let mut theme: Theme<Srgba> = palette.into();
let bg = if let Some(bg_color) = bg_color {
bg_color
} else {
p_ref.neutral_0.clone()
};
let ok_bg = Oklcha::from_color(bg);
let step_array = steps(ok_bg);
let bg_index = color_index(bg, step_array.len());
let primary_container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
primary_container_bg_color
} else {
get_color(bg_index, 5, &step_array, is_dark, &p_ref.neutral_1)
};
let secondary_container_bg = if let Some(secondary_container_bg) = secondary_container_bg {
secondary_container_bg
} else {
get_color(bg_index, 10, &step_array, is_dark, &p_ref.neutral_2)
};
let bg_component = get_color(bg_index, 8, &step_array, is_dark, &p_ref.neutral_2);
let on_bg_component = get_text(
color_index(bg_component, step_array.len()),
&step_array,
is_dark,
&p_ref.neutral_8,
);
let bg_component = Component::component(
bg_component,
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
on_bg_component,
is_high_contrast,
);
let primary_index = color_index(primary_container_bg, step_array.len());
let primary_component = get_color(primary_index, 6, &step_array, is_dark, &p_ref.neutral_3);
let on_primary_component = get_text(
color_index(primary_component, step_array.len()),
&step_array,
is_dark,
&p_ref.neutral_8,
);
let primary_component = Component::component(
primary_component,
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
on_primary_component,
is_high_contrast,
);
let secondary_index = color_index(secondary_container_bg, step_array.len());
let secondary_component =
get_color(secondary_index, 3, &step_array, is_dark, &p_ref.neutral_4);
let on_secondary_component = get_text(
color_index(secondary_component, step_array.len()),
&step_array,
is_dark,
&p_ref.neutral_10,
);
let secondary_component = Component::component(
secondary_component,
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
on_secondary_component,
is_high_contrast,
);
let mut theme: Theme<Srgba> = Theme {
name: palette.name().to_string(),
background: Container::new(
bg_component,
bg,
get_text(bg_index, &step_array, is_dark, &p_ref.neutral_8),
),
primary: Container::new(
primary_component,
primary_container_bg,
get_text(primary_index, &step_array, is_dark, &p_ref.neutral_8),
),
secondary: Container::new(
secondary_component,
secondary_container_bg,
get_text(secondary_index, &step_array, is_dark, &p_ref.neutral_8),
),
accent: Component::colored_component(
p_ref.accent.to_owned(),
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
),
success: Component::colored_component(
p_ref.green.to_owned(),
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
),
destructive: Component::colored_component(
p_ref.red.to_owned(),
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
),
warning: Component::colored_component(
p_ref.yellow.to_owned(),
p_ref.neutral_0.to_owned(),
p_ref.accent.to_owned(),
),
palette: palette.inner(),
spacing,
corner_radii,
is_dark,
is_high_contrast,
};
theme.spacing = spacing;
theme.corner_radii = corner_radii;
theme
}
}
fn get_index(base_index: usize, steps: usize, step_len: usize, is_dark: bool) -> Option<usize> {
if is_dark {
base_index.checked_add(steps)
} else {
base_index.checked_sub(steps)
}
.filter(|i| *i < step_len)
}
fn get_color(
base_index: usize,
steps: usize,
step_array: &[Srgba; 100],
is_dark: bool,
fallback: &Srgba,
) -> Srgba {
get_index(base_index, steps, step_array.len(), is_dark)
.and_then(|i| step_array.get(i).cloned())
.unwrap_or_else(|| fallback.to_owned())
}
fn get_text(
base_index: usize,
step_array: &[Srgba; 100],
is_dark: bool,
fallback: &Srgba,
) -> Srgba {
let Some(index) = get_index(base_index, 70, step_array.len(), is_dark).or_else(|| get_index(base_index, 50, step_array.len(), is_dark)) else {
return fallback.to_owned();
};
step_array
.get(index)
.cloned()
.unwrap_or_else(|| fallback.to_owned())
}
fn color_index<C>(c: C, array_len: usize) -> usize
where
Oklcha: FromColor<C>,
{
let c = Oklcha::from_color(c);
((c.l * array_len as f32).round() as usize).clamp(0, array_len - 1)
}