wip: calculate theme using new method
This commit is contained in:
parent
607883e4ad
commit
a618c1b94a
6 changed files with 257 additions and 423 deletions
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
cosmic-theme/src/model/layout.rs
Normal file
5
cosmic-theme/src/model/layout.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#[derive(Default)]
|
||||
pub struct Layout {
|
||||
corner_radii: [u32;4],
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue