use crate::{
blend::harmonize,
color::Argb,
dynamic_color::{DynamicScheme, Variant},
palette::{CorePalette, Palette, TonalPalette},
scheme::Scheme,
};
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
#[cfg(feature = "serde")]
use serde::Serialize;
#[cfg(feature = "std")]
use std::{string::String, vec::Vec};
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CustomColor {
pub value: Argb,
pub name: String,
pub blend: bool,
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ColorGroup {
pub color: Argb,
pub on_color: Argb,
pub color_container: Argb,
pub on_color_container: Argb,
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CustomColorGroup {
pub color: CustomColor,
pub value: Argb,
pub light: ColorGroup,
pub dark: ColorGroup,
}
impl CustomColorGroup {
fn new(source: Argb, color: CustomColor) -> Self {
let mut value = color.value;
if color.blend {
value = harmonize(value, source);
}
let palette = CorePalette::of(value);
let tones = palette.primary;
Self {
color,
value,
light: ColorGroup {
color: tones.tone(40),
on_color: tones.tone(100),
color_container: tones.tone(90),
on_color_container: tones.tone(10),
},
dark: ColorGroup {
color: tones.tone(80),
on_color: tones.tone(20),
color_container: tones.tone(30),
on_color_container: tones.tone(90),
},
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Schemes {
pub light: Scheme,
pub dark: Scheme,
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Palettes {
pub primary: TonalPalette,
pub secondary: TonalPalette,
pub tertiary: TonalPalette,
pub neutral: TonalPalette,
pub neutral_variant: TonalPalette,
pub error: TonalPalette,
}
pub struct ThemeBuilder {
source: Argb,
variant: Variant,
color_match: bool,
primary: Option<Argb>,
secondary: Option<Argb>,
tertiary: Option<Argb>,
error: Option<Argb>,
neutral: Option<Argb>,
neutral_variant: Option<Argb>,
custom_colors: Vec<CustomColor>,
}
impl ThemeBuilder {
#[must_use]
pub const fn with_source(source: Argb) -> Self {
Self {
source,
variant: Variant::TonalSpot,
color_match: false,
primary: None,
secondary: None,
tertiary: None,
error: None,
neutral: None,
neutral_variant: None,
custom_colors: Vec::new(),
}
}
#[must_use]
pub const fn variant(mut self, variant: Variant) -> Self {
self.variant = variant;
self
}
#[must_use]
pub const fn primary(mut self, color: Argb) -> Self {
self.primary = Some(color);
self
}
#[must_use]
pub const fn secondary(mut self, color: Argb) -> Self {
self.secondary = Some(color);
self
}
#[must_use]
pub const fn tertiary(mut self, color: Argb) -> Self {
self.tertiary = Some(color);
self
}
#[must_use]
pub const fn error(mut self, color: Argb) -> Self {
self.error = Some(color);
self
}
#[must_use]
pub const fn neutral(mut self, color: Argb) -> Self {
self.neutral = Some(color);
self
}
#[must_use]
pub const fn neutral_variant(mut self, color: Argb) -> Self {
self.neutral_variant = Some(color);
self
}
#[must_use]
pub fn custom_colors(mut self, custom_colors: Vec<CustomColor>) -> Self {
self.custom_colors = custom_colors;
self
}
#[must_use]
pub const fn color_match(mut self, enabled: bool) -> Self {
self.color_match = enabled;
self
}
#[must_use]
pub fn build(mut self) -> Theme {
let palette = CorePalette::of(self.source);
if self.color_match {
self.variant = Variant::Fidelity;
}
let mut light = DynamicScheme::by_variant(self.source, &self.variant, false, None);
let mut dark = DynamicScheme::by_variant(self.source, &self.variant, true, None);
if let Some(color) = self.primary {
let palette = TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Primary);
light.primary_palette = palette;
dark.primary_palette = palette;
}
if let Some(color) = self.secondary {
let palette =
TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Secondary);
light.secondary_palette = palette;
dark.secondary_palette = palette;
}
if let Some(color) = self.tertiary {
let palette =
TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Tertiary);
light.tertiary_palette = palette;
dark.tertiary_palette = palette;
}
if let Some(color) = self.error {
let palette = TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Error);
light.error_palette = palette;
dark.error_palette = palette;
}
if let Some(color) = self.neutral {
let palette = TonalPalette::by_variant(&color.into(), &self.variant, &Palette::Neutral);
light.neutral_palette = palette;
dark.neutral_palette = palette;
}
if let Some(color) = self.neutral_variant {
let palette =
TonalPalette::by_variant(&color.into(), &self.variant, &Palette::NeutralVariant);
light.neutral_variant_palette = palette;
dark.neutral_variant_palette = palette;
}
Theme {
source: self.source,
schemes: Schemes {
light: light.into(),
dark: dark.into(),
},
palettes: Palettes {
primary: palette.primary,
secondary: palette.secondary,
tertiary: palette.tertiary,
neutral: palette.neutral,
neutral_variant: palette.neutral_variant,
error: palette.error,
},
custom_colors: self
.custom_colors
.into_iter()
.map(|color| CustomColorGroup::new(self.source, color))
.collect(),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Theme {
pub source: Argb,
pub schemes: Schemes,
pub palettes: Palettes,
pub custom_colors: Vec<CustomColorGroup>,
}