mod cache;
mod motion;
mod paint;
mod registry;
mod selector;
mod source;
mod style;
mod validate;
use std::collections::BTreeMap;
use std::sync::Arc;
pub use motion::Motion;
pub(crate) use motion::{MOTION_KEYS, parse_duration};
pub(crate) use paint::Expr;
pub use paint::Paint;
pub use registry::{Resolved, ThemeRegistry};
pub use selector::{Selector, State};
pub use style::{PropValue, StyleProps, WORD_PROPS};
use crate::animation::CellAnimation;
use crate::color::Rgb;
use crate::icons::IconGlyphs;
use cache::StyleCache;
pub const REQUIRED_COLORS: [&str; 20] = [
"canvas", "surface", "raised", "active", "overlay", "accent", "accent-2", "text", "dim", "muted", "ink", "success",
"warning", "danger", "info", "series-1", "series-2", "series-3", "series-4", "series-5",
];
pub const SERIES_COLORS: usize = 5;
#[derive(Debug, Clone, PartialEq)]
pub struct Theme {
id: String,
name: String,
colors: BTreeMap<String, Rgb>,
motion: Motion,
typography: BTreeMap<String, StyleProps>,
rules: Vec<(Selector, StyleProps)>,
icon_set: String,
icons: BTreeMap<String, IconGlyphs>,
animations: BTreeMap<String, Arc<CellAnimation>>,
cache: StyleCache,
}
impl Theme {
#[must_use]
pub fn id(&self) -> &str {
&self.id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn color(&self, token: &str) -> Option<Rgb> {
self.colors.get(token).copied()
}
pub fn solid(&self, expression: &str) -> Result<Rgb, String> {
paint::Expr::parse(expression)?.solid(&self.colors)
}
#[must_use]
pub fn series_color(&self, index: usize) -> Rgb {
let token = format!("series-{}", index % SERIES_COLORS + 1);
self.color(&token).unwrap_or_else(|| self.colors.get("accent").copied().unwrap_or(Rgb::new(0, 0, 0)))
}
pub fn colors(&self) -> impl Iterator<Item = (&str, Rgb)> {
self.colors.iter().map(|(name, color)| (name.as_str(), *color))
}
#[must_use]
pub fn motion(&self) -> Motion {
self.motion
}
#[must_use]
pub fn typography(&self, role: &str) -> Option<&StyleProps> {
self.typography.get(role)
}
#[must_use]
pub fn icon_set(&self) -> &str {
&self.icon_set
}
#[must_use]
pub fn icon_overrides(&self) -> &BTreeMap<String, IconGlyphs> {
&self.icons
}
#[must_use]
pub fn animation_overrides(&self) -> &BTreeMap<String, Arc<CellAnimation>> {
&self.animations
}
#[must_use]
pub fn style(&self, widget: &str, variant: Option<&str>, states: &[State]) -> StyleProps {
self.cache.get_or_insert(widget, variant, states, || self.layer_rules(widget, variant, states))
}
fn layer_rules(&self, widget: &str, variant: Option<&str>, states: &[State]) -> StyleProps {
let mut matching: Vec<(usize, &(Selector, StyleProps))> = self
.rules
.iter()
.enumerate()
.filter(|(_, (selector, _))| selector.matches(widget, variant, states))
.collect();
matching.sort_by_key(|(order, (selector, _))| (selector.specificity(), *order));
let mut props = StyleProps::default();
for (_, (_, rule)) in matching {
props.overlay(rule);
}
props
}
}