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

@ -98,5 +98,6 @@ exclude = [
[patch."https://github.com/pop-os/libcosmic"]
libcosmic = { path = "./", features = ["wayland", "tokio", "a11y"]}
# TODO Remove me when the palette crate gets an update & before merging
[patch.crates-io]
palette = {git = "https://github.com/Ogeon/palette", features = ["serializing"] }

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)
}

View file

@ -1,11 +1,11 @@
use almost::equal;
use palette::{convert::FromColorUnclamped, ClampAssign, Oklch, Srgb};
use palette::{convert::FromColorUnclamped, ClampAssign, Oklcha, Srgb, Srgba};
/// Get an array of 100 colors with a specific hue and chroma
/// over the full range of lightness.
/// Colors which are not valid Srgb will fallback to a color with the nearest valid chroma.
pub fn steps(mut c: Oklch) -> [Srgb; 100] {
let mut steps = [Srgb::new(0.0, 0.0, 0.0); 100];
/// Colors which are not valid Srgba will fallback to a color with the nearest valid chroma.
pub fn steps(mut c: Oklcha) -> [Srgba; 100] {
let mut steps = [Srgba::new(0.0, 0.0, 0.0, 1.0); 100];
for i in 0..steps.len() {
let lightness = i as f32 / 100.0;
@ -16,12 +16,12 @@ pub fn steps(mut c: Oklch) -> [Srgb; 100] {
steps
}
/// find the nearest chroma which makes our color a valid color in Srgb
pub fn oklch_to_srgba_nearest_chroma(mut c: Oklch) -> Srgb {
/// find the nearest chroma which makes our color a valid color in Srgba
pub fn oklch_to_srgba_nearest_chroma(mut c: Oklcha) -> Srgba {
let mut r_chroma = c.chroma;
let mut l_chroma = 0.0;
// exit early if we found it right away
let mut new_c = Srgb::from_color_unclamped(c);
let mut new_c = Srgba::from_color_unclamped(c);
if is_valid_srgb(new_c) {
new_c.clamp_assign();
@ -30,7 +30,7 @@ pub fn oklch_to_srgba_nearest_chroma(mut c: Oklch) -> Srgb {
// is this an excessive depth to search?
for _ in 0..64 {
let new_c = Srgb::from_color_unclamped(c);
let new_c = Srgba::from_color_unclamped(c);
if is_valid_srgb(new_c) {
l_chroma = c.chroma;
c.chroma = (c.chroma + r_chroma) / 2.0;
@ -39,11 +39,11 @@ pub fn oklch_to_srgba_nearest_chroma(mut c: Oklch) -> Srgb {
c.chroma = (c.chroma + l_chroma) / 2.0;
}
}
Srgb::from_color_unclamped(c)
Srgba::from_color_unclamped(c)
}
/// checks that the color is valid srgb
pub fn is_valid_srgb(c: Srgb) -> bool {
pub fn is_valid_srgb(c: Srgba) -> bool {
(equal(c.red, Srgb::max_red()) || (c.red >= Srgb::min_red() && c.red <= Srgb::max_red()))
&& (equal(c.blue, Srgb::max_blue())
|| (c.blue >= Srgb::min_blue() && c.blue <= Srgb::max_blue()))
@ -54,38 +54,38 @@ pub fn is_valid_srgb(c: Srgb) -> bool {
#[cfg(test)]
mod tests {
use almost::equal;
use palette::{OklabHue, Srgb};
use palette::{OklabHue, Srgba};
use super::{is_valid_srgb, oklch_to_srgba_nearest_chroma};
#[test]
fn test_valid_check() {
assert!(is_valid_srgb(Srgb::new(1.0, 1.0, 1.0)));
assert!(is_valid_srgb(Srgb::new(0.0, 0.0, 0.0)));
assert!(is_valid_srgb(Srgb::new(0.5, 0.5, 0.5)));
assert!(!is_valid_srgb(Srgb::new(-0.1, 0.0, 0.0)));
assert!(!is_valid_srgb(Srgb::new(0.0, -0.1, 0.0)));
assert!(!is_valid_srgb(Srgb::new(-0.0, 0.0, -0.1)));
assert!(!is_valid_srgb(Srgb::new(-100.1, 0.0, 0.0)));
assert!(!is_valid_srgb(Srgb::new(0.0, -100.1, 0.0)));
assert!(!is_valid_srgb(Srgb::new(-0.0, 0.0, -100.1)));
assert!(!is_valid_srgb(Srgb::new(1.1, 0.0, 0.0)));
assert!(!is_valid_srgb(Srgb::new(0.0, 1.1, 0.0)));
assert!(!is_valid_srgb(Srgb::new(-0.0, 0.0, 1.1)));
assert!(!is_valid_srgb(Srgb::new(100.1, 0.0, 0.0)));
assert!(!is_valid_srgb(Srgb::new(0.0, 100.1, 0.0)));
assert!(!is_valid_srgb(Srgb::new(-0.0, 0.0, 100.1)));
assert!(is_valid_srgb(Srgba::new(1.0, 1.0, 1.0, 1.0)));
assert!(is_valid_srgb(Srgba::new(0.0, 0.0, 0.0, 1.0)));
assert!(is_valid_srgb(Srgba::new(0.5, 0.5, 0.5, 1.0)));
assert!(!is_valid_srgb(Srgba::new(-0.1, 0.0, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(0.0, -0.1, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(-0.0, 0.0, -0.1, 1.0)));
assert!(!is_valid_srgb(Srgba::new(-100.1, 0.0, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(0.0, -100.1, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(-0.0, 0.0, -100.1, 1.0)));
assert!(!is_valid_srgb(Srgba::new(1.1, 0.0, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(0.0, 1.1, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(-0.0, 0.0, 1.1, 1.0)));
assert!(!is_valid_srgb(Srgba::new(100.1, 0.0, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(0.0, 100.1, 0.0, 1.0)));
assert!(!is_valid_srgb(Srgba::new(-0.0, 0.0, 100.1, 1.0)));
}
#[test]
fn test_conversion_boundaries() {
let c1 = palette::Oklch::new(0.0, 0.288, OklabHue::from_degrees(0.0));
let c1 = palette::Oklcha::new(0.0, 0.288, OklabHue::from_degrees(0.0), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1);
equal(srgb.red, 0.0);
equal(srgb.blue, 0.0);
equal(srgb.green, 0.0);
let c1 = palette::Oklch::new(1.0, 0.288, OklabHue::from_degrees(0.0));
let c1 = palette::Oklcha::new(1.0, 0.288, OklabHue::from_degrees(0.0), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1);
equal(srgb.red, 1.0);
@ -95,20 +95,20 @@ mod tests {
#[test]
fn test_conversion_colors() {
let c1 = palette::Oklch::new(0.4608, 0.11111, OklabHue::new(57.31));
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8>();
let c1 = palette::Oklcha::new(0.4608, 0.11111, OklabHue::new(57.31), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
assert!(srgb.red == 133);
assert!(srgb.green == 69);
assert!(srgb.blue == 0);
let c1 = palette::Oklch::new(0.30, 0.08, OklabHue::new(35.0));
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8>();
let c1 = palette::Oklcha::new(0.30, 0.08, OklabHue::new(35.0), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
assert!(srgb.red == 78);
assert!(srgb.green == 27);
assert!(srgb.blue == 15);
let c1 = palette::Oklch::new(0.757, 0.146, OklabHue::new(301.2));
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8>();
let c1 = palette::Oklcha::new(0.757, 0.146, OklabHue::new(301.2), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
assert!(srgb.red == 192);
assert!(srgb.green == 153);
assert!(srgb.blue == 253);
@ -116,20 +116,20 @@ mod tests {
#[test]
fn test_conversion_fallback_colors() {
let c1 = palette::Oklch::new(0.70, 0.284, OklabHue::new(35.0));
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8>();
let c1 = palette::Oklcha::new(0.70, 0.284, OklabHue::new(35.0), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
assert!(srgb.red == 255);
assert!(srgb.green == 103);
assert!(srgb.blue == 65);
let c1 = palette::Oklch::new(0.757, 0.239, OklabHue::new(301.2));
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8>();
let c1 = palette::Oklcha::new(0.757, 0.239, OklabHue::new(301.2), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
assert!(srgb.red == 193);
assert!(srgb.green == 152);
assert!(srgb.blue == 255);
let c1 = palette::Oklch::new(0.163, 0.333, OklabHue::new(141.0));
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8>();
let c1 = palette::Oklcha::new(0.163, 0.333, OklabHue::new(141.0), 1.0);
let srgb = oklch_to_srgba_nearest_chroma(c1).into_format::<u8, u8>();
assert!(srgb.red == 1);
assert!(srgb.green == 19);
assert!(srgb.blue == 0);

View file

@ -44,13 +44,12 @@ use palette::Srgba;
pub type CosmicColor = ::palette::rgb::Srgba;
pub type CosmicComponent = cosmic_theme::Component<CosmicColor>;
pub type CosmicTheme = cosmic_theme::Theme<CosmicColor>;
pub type CosmicThemeCss = cosmic_theme::Theme<cosmic_theme::util::CssColor>;
lazy_static::lazy_static! {
pub static ref COSMIC_DARK: CosmicTheme = CosmicThemeCss::dark_default().into_srgba();
pub static ref COSMIC_HC_DARK: CosmicTheme = CosmicThemeCss::high_contrast_dark_default().into_srgba();
pub static ref COSMIC_LIGHT: CosmicTheme = CosmicThemeCss::light_default().into_srgba();
pub static ref COSMIC_HC_LIGHT: CosmicTheme = CosmicThemeCss::high_contrast_light_default().into_srgba();
pub static ref COSMIC_DARK: CosmicTheme = CosmicTheme::dark_default();
pub static ref COSMIC_HC_DARK: CosmicTheme = CosmicTheme::high_contrast_dark_default();
pub static ref COSMIC_LIGHT: CosmicTheme = CosmicTheme::light_default();
pub static ref COSMIC_HC_LIGHT: CosmicTheme = CosmicTheme::high_contrast_light_default();
pub static ref TRANSPARENT_COMPONENT: Component<CosmicColor> = Component {
base: CosmicColor::new(0.0, 0.0, 0.0, 0.0),
hover: CosmicColor::new(0.0, 0.0, 0.0, 0.0),
@ -1202,6 +1201,7 @@ impl text_input::StyleSheet for Theme {
}
}
#[must_use]
pub fn theme() -> Theme {
let Ok(helper) = crate::cosmic_config::Config::new(
crate::cosmic_theme::NAME,
@ -1209,34 +1209,28 @@ pub fn theme() -> Theme {
) else {
return crate::theme::Theme::dark();
};
let t = crate::cosmic_theme::Theme::get_entry(&helper).map_or_else(
|(errors, theme)| {
for err in errors {
tracing::error!("{:?}", err);
}
theme.into_srgba()
},
crate::cosmic_theme::Theme::into_srgba,
);
let t = crate::cosmic_theme::Theme::get_entry(&helper).unwrap_or_else(|(errors, theme)| {
for err in errors {
tracing::error!("{:?}", err);
}
theme
});
crate::theme::Theme::custom(Arc::new(t))
}
pub fn subscription(id: u64) -> Subscription<crate::theme::Theme> {
config_subscription::<u64, crate::cosmic_theme::Theme<CssColor>>(
config_subscription::<u64, crate::cosmic_theme::Theme<Srgba>>(
id,
crate::cosmic_theme::NAME.into(),
crate::cosmic_theme::Theme::<CssColor>::version(),
crate::cosmic_theme::Theme::<Srgba>::version(),
)
.map(|(_, res)| {
let theme = res.map_or_else(
|(errors, theme)| {
for err in errors {
tracing::error!("{:?}", err);
}
theme.into_srgba()
},
crate::cosmic_theme::Theme::into_srgba,
);
let theme = res.unwrap_or_else(|(errors, theme)| {
for err in errors {
tracing::error!("{:?}", err);
}
theme
});
crate::theme::Theme::custom(Arc::new(theme))
})
}