feat: add ThemeBuilder
This commit is contained in:
parent
620c1adb74
commit
607883e4ad
8 changed files with 354 additions and 15 deletions
31
cosmic-theme/src/model/corner.rs
Normal file
31
cosmic-theme/src/model/corner.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Corner radii variables for the Cosmic theme
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CornerRadii {
|
||||
/// corner radii of 0
|
||||
pub radius_0: [u16; 4],
|
||||
/// smallest size of corner radii that can be non-zero
|
||||
pub radius_xs: [u16; 4],
|
||||
/// small corner radii
|
||||
pub radius_s: [u16; 4],
|
||||
/// medium corner radii
|
||||
pub radius_m: [u16; 4],
|
||||
/// large corner radii
|
||||
pub radius_l: [u16; 4],
|
||||
/// extra large corner radii
|
||||
pub radius_xl: [u16; 4],
|
||||
}
|
||||
|
||||
impl Default for CornerRadii {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
radius_0: [0; 4],
|
||||
radius_xs: [4; 4],
|
||||
radius_s: [8; 4],
|
||||
radius_m: [16; 4],
|
||||
radius_l: [32; 4],
|
||||
radius_xl: [160; 4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,29 @@ pub enum CosmicPalette<C> {
|
|||
HighContrastDark(CosmicPaletteInner<C>),
|
||||
}
|
||||
|
||||
impl<C> CosmicPalette<C> {
|
||||
/// extract the inner palette
|
||||
pub fn inner(self) -> CosmicPaletteInner<C> {
|
||||
match self {
|
||||
CosmicPalette::Dark(p) => p,
|
||||
CosmicPalette::Light(p) => p,
|
||||
CosmicPalette::HighContrastLight(p) => p,
|
||||
CosmicPalette::HighContrastDark(p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> AsMut<CosmicPaletteInner<C>> for CosmicPalette<C> {
|
||||
fn as_mut(&mut self) -> &mut CosmicPaletteInner<C> {
|
||||
match self {
|
||||
CosmicPalette::Dark(p) => p,
|
||||
CosmicPalette::Light(p) => p,
|
||||
CosmicPalette::HighContrastLight(p) => p,
|
||||
CosmicPalette::HighContrastDark(p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> AsRef<CosmicPaletteInner<C>> for CosmicPalette<C>
|
||||
where
|
||||
C: Clone + fmt::Debug + Default + Into<Srgba> + From<Srgba> + Serialize + DeserializeOwned,
|
||||
|
|
@ -85,6 +108,9 @@ pub struct CosmicPaletteInner<C> {
|
|||
/// name of the palette
|
||||
pub name: String,
|
||||
|
||||
/// the selected accent color
|
||||
pub accent: C,
|
||||
|
||||
/// basic palette
|
||||
/// blue: colors used for various points of emphasis in the UI
|
||||
pub blue: C,
|
||||
|
|
@ -161,6 +187,7 @@ impl From<CosmicPaletteInner<CssColor>> for CosmicPaletteInner<Srgba> {
|
|||
fn from(p: CosmicPaletteInner<CssColor>) -> Self {
|
||||
CosmicPaletteInner {
|
||||
name: p.name,
|
||||
accent: p.accent.into(),
|
||||
blue: p.blue.into(),
|
||||
red: p.red.into(),
|
||||
green: p.green.into(),
|
||||
|
|
@ -253,3 +280,14 @@ where
|
|||
Ok(ron::de::from_reader(f)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<CosmicPalette<Srgba>> for CosmicPalette<CssColor> {
|
||||
fn into(self) -> CosmicPalette<Srgba> {
|
||||
match self {
|
||||
CosmicPalette::Dark(p) => CosmicPalette::Dark(p.into()),
|
||||
CosmicPalette::Light(p) => CosmicPalette::Light(p.into()),
|
||||
CosmicPalette::HighContrastLight(p) => CosmicPalette::HighContrastLight(p.into()),
|
||||
CosmicPalette::HighContrastDark(p) => CosmicPalette::HighContrastDark(p.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
Dark (
|
||||
(
|
||||
name: "cosmic-dark",
|
||||
accent: (
|
||||
c: "#94EBEB",
|
||||
),
|
||||
blue: (
|
||||
c: "#94EBEB",
|
||||
),
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ where
|
|||
p.neutral_0,
|
||||
p.neutral_10,
|
||||
0.08,
|
||||
p.blue,
|
||||
p.accent,
|
||||
p.neutral_8,
|
||||
false,
|
||||
),
|
||||
|
|
@ -347,7 +347,7 @@ where
|
|||
p.neutral_0,
|
||||
p.neutral_10,
|
||||
0.08,
|
||||
p.blue,
|
||||
p.accent,
|
||||
p.neutral_8,
|
||||
false,
|
||||
),
|
||||
|
|
@ -357,7 +357,7 @@ where
|
|||
p.neutral_0,
|
||||
p.neutral_10,
|
||||
0.08,
|
||||
p.blue,
|
||||
p.accent,
|
||||
p.neutral_9,
|
||||
false,
|
||||
),
|
||||
|
|
@ -366,7 +366,7 @@ where
|
|||
p.neutral_0,
|
||||
p.neutral_10,
|
||||
0.08,
|
||||
p.blue,
|
||||
p.accent,
|
||||
p.neutral_9,
|
||||
true,
|
||||
),
|
||||
|
|
@ -375,7 +375,7 @@ where
|
|||
p.neutral_0,
|
||||
p.neutral_10,
|
||||
0.08,
|
||||
p.blue,
|
||||
p.accent,
|
||||
p.neutral_9,
|
||||
true,
|
||||
),
|
||||
|
|
@ -384,7 +384,7 @@ where
|
|||
p.neutral_0,
|
||||
p.neutral_10.clone(),
|
||||
0.08,
|
||||
p.blue,
|
||||
p.accent,
|
||||
p.neutral_10,
|
||||
true,
|
||||
),
|
||||
|
|
@ -394,7 +394,7 @@ where
|
|||
p.neutral_0.clone(),
|
||||
p.neutral_0,
|
||||
0.75,
|
||||
p.blue.clone(),
|
||||
p.accent.clone(),
|
||||
p.neutral_8,
|
||||
false,
|
||||
),
|
||||
|
|
@ -403,7 +403,7 @@ where
|
|||
p.neutral_0.clone(),
|
||||
p.neutral_0,
|
||||
0.9,
|
||||
p.blue.clone(),
|
||||
p.accent.clone(),
|
||||
p.neutral_8,
|
||||
false,
|
||||
),
|
||||
|
|
@ -412,7 +412,7 @@ where
|
|||
p.neutral_0.clone(),
|
||||
p.neutral_0,
|
||||
1.0,
|
||||
p.blue.clone(),
|
||||
p.accent.clone(),
|
||||
p.neutral_8,
|
||||
false,
|
||||
),
|
||||
|
|
@ -422,7 +422,7 @@ where
|
|||
p.neutral_0.clone(),
|
||||
p.neutral_0,
|
||||
0.75,
|
||||
p.blue.clone(),
|
||||
p.accent.clone(),
|
||||
p.neutral_9,
|
||||
true,
|
||||
)
|
||||
|
|
@ -432,7 +432,7 @@ where
|
|||
p.neutral_0.clone(),
|
||||
p.neutral_0,
|
||||
0.9,
|
||||
p.blue.clone(),
|
||||
p.accent.clone(),
|
||||
p.neutral_9,
|
||||
true,
|
||||
),
|
||||
|
|
@ -442,7 +442,7 @@ where
|
|||
p.neutral_0.clone(),
|
||||
p.neutral_0,
|
||||
1.0,
|
||||
p.blue.clone(),
|
||||
p.accent.clone(),
|
||||
p.neutral_9,
|
||||
true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
Light (
|
||||
(
|
||||
name: "cosmic-light",
|
||||
accent: (
|
||||
c: "#00496D",
|
||||
),
|
||||
blue: (
|
||||
c: "#00496D",
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
pub use corner::*;
|
||||
pub use cosmic_palette::*;
|
||||
pub use derivation::*;
|
||||
pub use spacing::*;
|
||||
pub use theme::*;
|
||||
|
||||
mod corner;
|
||||
mod cosmic_palette;
|
||||
mod derivation;
|
||||
mod spacing;
|
||||
mod theme;
|
||||
|
|
|
|||
43
cosmic-theme/src/model/spacing.rs
Normal file
43
cosmic-theme/src/model/spacing.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Spacing variables for the Cosmic theme
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct Spacing {
|
||||
/// No spacing
|
||||
pub space_none: u16,
|
||||
/// smallest spacing that can be non-zero
|
||||
pub space_xxxs: u16,
|
||||
/// extra extra small spacing
|
||||
pub space_xxs: u16,
|
||||
/// extra small spacing
|
||||
pub space_xs: u16,
|
||||
/// small spacing
|
||||
pub space_s: u16,
|
||||
/// medium spacing
|
||||
pub space_m: u16,
|
||||
/// large spacing
|
||||
pub space_l: u16,
|
||||
/// extra large spacing
|
||||
pub space_xl: u16,
|
||||
/// extra extra large spacing
|
||||
pub space_xxl: u16,
|
||||
/// largest possible spacing
|
||||
pub space_xxxl: u16,
|
||||
}
|
||||
|
||||
impl Default for Spacing {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
space_none: 0,
|
||||
space_xxxs: 4,
|
||||
space_xxs: 8,
|
||||
space_xs: 12,
|
||||
space_s: 16,
|
||||
space_m: 24,
|
||||
space_l: 32,
|
||||
space_xl: 48,
|
||||
space_xxl: 64,
|
||||
space_xxxl: 128,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{
|
||||
util::CssColor, Component, ComponentType, Container, ContainerType, CosmicPalette,
|
||||
CosmicPaletteInner, DARK_PALETTE, LIGHT_PALETTE, NAME, THEME_DIR,
|
||||
util::CssColor, Component, ComponentType, Container, ContainerType, 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::Srgba;
|
||||
use palette::{Srgb, Srgba};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt,
|
||||
|
|
@ -47,6 +47,10 @@ pub struct Theme<C> {
|
|||
pub warning: Component<C>,
|
||||
/// palette
|
||||
pub palette: CosmicPaletteInner<C>,
|
||||
/// spacing
|
||||
pub spacing: Spacing,
|
||||
/// corner radii
|
||||
pub corner_radii: CornerRadii,
|
||||
/// is dark
|
||||
pub is_dark: bool,
|
||||
/// is high contrast
|
||||
|
|
@ -122,6 +126,14 @@ impl CosmicConfigEntry for Theme<CssColor> {
|
|||
Ok(is_high_contrast) => default.is_high_contrast = is_high_contrast,
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
match config.get::<Spacing>("spacing") {
|
||||
Ok(spacing) => default.spacing = spacing,
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
match config.get::<CornerRadii>("corner_radii") {
|
||||
Ok(corner_radii) => default.corner_radii = corner_radii,
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(default)
|
||||
|
|
@ -346,6 +358,72 @@ where
|
|||
pub fn window_header_bg(&self) -> Srgba {
|
||||
self.background.base.clone().into()
|
||||
}
|
||||
|
||||
/// get @space_none
|
||||
pub fn space_none(&self) -> u16 {
|
||||
self.spacing.space_none
|
||||
}
|
||||
/// get @space_xxxs
|
||||
pub fn space_xxxs(&self) -> u16 {
|
||||
self.spacing.space_xxxs
|
||||
}
|
||||
/// get @space_xxs
|
||||
pub fn space_xxs(&self) -> u16 {
|
||||
self.spacing.space_xxs
|
||||
}
|
||||
/// get @space_xs
|
||||
pub fn space_xs(&self) -> u16 {
|
||||
self.spacing.space_xs
|
||||
}
|
||||
/// get @space_s
|
||||
pub fn space_s(&self) -> u16 {
|
||||
self.spacing.space_s
|
||||
}
|
||||
/// get @space_m
|
||||
pub fn space_m(&self) -> u16 {
|
||||
self.spacing.space_m
|
||||
}
|
||||
/// get @space_l
|
||||
pub fn space_l(&self) -> u16 {
|
||||
self.spacing.space_l
|
||||
}
|
||||
/// get @space_xl
|
||||
pub fn space_xl(&self) -> u16 {
|
||||
self.spacing.space_xl
|
||||
}
|
||||
/// get @space_xxl
|
||||
pub fn space_xxl(&self) -> u16 {
|
||||
self.spacing.space_xxl
|
||||
}
|
||||
/// get @space_xxxl
|
||||
pub fn space_xxxl(&self) -> u16 {
|
||||
self.spacing.space_xxxl
|
||||
}
|
||||
|
||||
/// get @radius_0
|
||||
pub fn radius_0(&self) -> [u16; 4] {
|
||||
self.corner_radii.radius_0
|
||||
}
|
||||
/// get @radius_xs
|
||||
pub fn radius_xs(&self) -> [u16; 4] {
|
||||
self.corner_radii.radius_xs
|
||||
}
|
||||
/// get @radius_s
|
||||
pub fn radius_s(&self) -> [u16; 4] {
|
||||
self.corner_radii.radius_s
|
||||
}
|
||||
/// get @radius_m
|
||||
pub fn radius_m(&self) -> [u16; 4] {
|
||||
self.corner_radii.radius_m
|
||||
}
|
||||
/// get @radius_l
|
||||
pub fn radius_l(&self) -> [u16; 4] {
|
||||
self.corner_radii.radius_l
|
||||
}
|
||||
/// get @radius_xl
|
||||
pub fn radius_xl(&self) -> [u16; 4] {
|
||||
self.corner_radii.radius_xl
|
||||
}
|
||||
}
|
||||
|
||||
impl Theme<CssColor> {
|
||||
|
|
@ -383,6 +461,8 @@ impl Theme<CssColor> {
|
|||
palette: self.palette.into(),
|
||||
is_dark: self.is_dark,
|
||||
is_high_contrast: self.is_high_contrast,
|
||||
corner_radii: self.corner_radii,
|
||||
spacing: self.spacing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,6 +491,143 @@ where
|
|||
},
|
||||
is_dark,
|
||||
is_high_contrast,
|
||||
spacing: Spacing::default(),
|
||||
corner_radii: CornerRadii::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for building customized themes
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ThemeBuilder {
|
||||
palette: CosmicPalette<Srgba>,
|
||||
spacing: Spacing,
|
||||
corner_radii: CornerRadii,
|
||||
neutral_tint: Option<Srgb>,
|
||||
bg_color: Option<Srgba>,
|
||||
primary_container_bg: Option<Srgba>,
|
||||
text_tint: Option<Srgb>,
|
||||
accent: Option<Srgb>,
|
||||
}
|
||||
|
||||
impl Default for ThemeBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
palette: DARK_PALETTE.to_owned().into(),
|
||||
spacing: Spacing::default(),
|
||||
corner_radii: CornerRadii::default(),
|
||||
neutral_tint: Default::default(),
|
||||
text_tint: Default::default(),
|
||||
bg_color: Default::default(),
|
||||
primary_container_bg: Default::default(),
|
||||
accent: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThemeBuilder {
|
||||
/// Get a builder that is initialized with the default dark theme
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
palette: DARK_PALETTE.to_owned().into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a builder that is initialized with the default light theme
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
palette: LIGHT_PALETTE.to_owned().into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a builder that is initialized with the default dark high contrast theme
|
||||
pub fn dark_high_contrast() -> Self {
|
||||
let palette: CosmicPalette<Srgba> = DARK_PALETTE.to_owned().into();
|
||||
Self {
|
||||
palette: CosmicPalette::HighContrastLight(palette.inner()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a builder that is initialized with the default light high contrast theme
|
||||
pub fn light_high_contrast() -> Self {
|
||||
let palette: CosmicPalette<Srgba> = LIGHT_PALETTE.to_owned().into();
|
||||
Self {
|
||||
palette: CosmicPalette::HighContrastLight(palette.inner()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// set the spacing of the builder
|
||||
pub fn spacing(mut self, spacing: Spacing) -> Self {
|
||||
self.spacing = spacing;
|
||||
self
|
||||
}
|
||||
|
||||
/// set the corner_radii of the builder
|
||||
pub fn corner_radii(mut self, corner_radii: CornerRadii) -> Self {
|
||||
self.corner_radii = corner_radii;
|
||||
self
|
||||
}
|
||||
|
||||
/// apply a neutral tint to the palette
|
||||
pub fn neutral_tint(mut self, tint: Srgb) -> Self {
|
||||
self.neutral_tint = Some(tint);
|
||||
self
|
||||
}
|
||||
|
||||
/// apply a text tint to the palette
|
||||
pub fn text_tint(mut self, tint: Srgb) -> Self {
|
||||
self.text_tint = Some(tint);
|
||||
self
|
||||
}
|
||||
|
||||
/// apply a background color to the palette
|
||||
pub fn bg_color(mut self, c: Srgba) -> Self {
|
||||
self.bg_color = Some(c);
|
||||
self
|
||||
}
|
||||
|
||||
/// apply a primary container background color to the palette
|
||||
pub fn primary_container_bg(mut self, c: Srgba) -> Self {
|
||||
self.primary_container_bg = Some(c);
|
||||
self
|
||||
}
|
||||
|
||||
/// apply a accent color to the palette
|
||||
pub fn accent(mut self, c: Srgb) -> Self {
|
||||
self.accent = Some(c);
|
||||
self
|
||||
}
|
||||
|
||||
/// build the theme
|
||||
pub fn build(self) -> Theme<Srgba> {
|
||||
let Self {
|
||||
mut palette,
|
||||
spacing,
|
||||
corner_radii,
|
||||
neutral_tint,
|
||||
text_tint,
|
||||
bg_color,
|
||||
primary_container_bg,
|
||||
accent,
|
||||
} = self;
|
||||
|
||||
if let Some(accent) = accent {
|
||||
palette.as_mut().accent = accent.into();
|
||||
}
|
||||
|
||||
// TODO apply the customizations
|
||||
|
||||
if let Some(accent) = accent {
|
||||
palette.as_mut().accent = accent.into();
|
||||
}
|
||||
|
||||
let mut theme: Theme<Srgba> = palette.into();
|
||||
theme.spacing = spacing;
|
||||
theme.corner_radii = corner_radii;
|
||||
theme
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue