use super::{Color, Style, VisualState, mergeStyle};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Component {
Container,
Text,
Button,
Image,
Slider,
Scroll,
TextInput,
Checkbox,
ProgressBar,
Modal,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ThemeClass {
pub component: Component,
pub name: String,
pub style: Style,
}
#[derive(Clone, Debug, PartialEq)]
pub struct AppTheme {
pub backgroundColor: Color,
pub classes: Vec<ThemeClass>,
}
impl AppTheme {
pub fn new() -> Self {
Self::default()
}
pub fn backgroundColor(mut self, color: Color) -> Self {
self.backgroundColor = color;
self
}
pub fn containerColor(mut self, color: Color) -> Self {
self = self.class(Component::Container, "default", |style| {
style.background(color);
});
self
}
pub fn accentColor(mut self, color: Color) -> Self {
self = self.class(Component::Container, "accent", |style| {
style.background(color);
});
self
}
pub fn class(
mut self,
component: Component,
name: impl Into<String>,
configure: impl FnOnce(&mut Style),
) -> Self {
let name = name.into();
let mut style = Style::default();
configure(&mut style);
if let Some(existing) = self
.classes
.iter_mut()
.find(|item| item.component == component && item.name == name)
{
existing.style = style;
} else {
self.classes.push(ThemeClass {
component,
name,
style,
});
}
self
}
pub fn styleFor(&self, component: Component, class: Option<&str>) -> Style {
self.styleForDepth(component, class, 0)
}
pub fn styleForState(
&self,
component: Component,
class: Option<&str>,
state: VisualState,
) -> Style {
let mut style = self.styleFor(component.clone(), class);
if state == VisualState::Normal {
return style;
}
if let Some(class) = class {
let pseudo = format!("{class}:{}", state.name());
let pseudoStyle = self.styleFor(component, Some(&pseudo));
mergeStyle(&mut style, &pseudoStyle);
}
style
}
fn styleForDepth(&self, component: Component, class: Option<&str>, depth: usize) -> Style {
if depth > 16 {
return Style::default();
}
let Some(class) = class else {
return Style::default();
};
let Some(item) = self
.classes
.iter()
.find(|item| item.component == component && item.name == class)
else {
return Style::default();
};
let mut style =
self.styleForDepth(component.clone(), item.style.extends.as_deref(), depth + 1);
mergeStyle(&mut style, &item.style);
style
}
}
impl VisualState {
pub fn name(&self) -> &'static str {
match self {
VisualState::Normal => "normal",
VisualState::Hover => "hover",
VisualState::Pressed => "pressed",
VisualState::Focus => "focus",
VisualState::Checked => "checked",
VisualState::Disabled => "disabled",
}
}
}
impl Default for AppTheme {
fn default() -> Self {
Self {
backgroundColor: Color::rgb(0.08, 0.09, 0.11),
classes: Vec::new(),
}
}
}