use std::collections::HashMap;
use ron::Value;
use crate::theming::{
config::{ThemeConfig, RESOURCE_KEY},
Selector, Style, ThemeState,
};
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Theme {
styles: HashMap<String, Style>,
fonts: HashMap<String, &'static [u8]>,
}
impl Theme {
pub fn from_config(config: ThemeConfig) -> Self {
let mut styles = HashMap::new();
for style_key in config.styles.keys() {
Theme::read_config(style_key, style_key, &config, &mut styles)
}
Theme {
styles,
fonts: HashMap::new(),
}
}
pub fn register_font(mut self, key: &str, font: &'static [u8]) -> Self {
self.fonts.insert(key.to_string(), font);
self
}
pub fn fonts(&self) -> &HashMap<String, &'static [u8]> {
&self.fonts
}
pub fn style(&self, key: &str) -> Option<&Style> {
self.styles.get(key)
}
pub fn properties(&self, selector: &Selector) -> Option<HashMap<String, Value>> {
if !selector.dirty() {
return None;
}
if let Some(style) = &selector.style {
let mut properties = HashMap::new();
if let Some(style) = self.styles.get(style) {
for (key, value) in &style.properties {
properties.insert(key.clone(), value.clone());
}
for state in style.states.iter().rev() {
if selector.states().contains(&state.key) {
for (key, value) in &state.properties {
properties.insert(key.clone(), value.clone());
}
break;
}
}
}
return Some(properties);
}
None
}
fn read_config(
style_key: &str,
base_key: &str,
config: &ThemeConfig,
styles: &mut HashMap<String, Style>,
) {
if style_key.is_empty() {
return;
}
if !styles.contains_key(style_key) {
styles.insert(style_key.to_string(), Style::new());
}
if let Some(style) = config.styles.get(base_key) {
if !style.base.is_empty() && style.base != *style_key {
Theme::read_config(style_key, &style.base, config, styles);
}
}
if let Some(style_config) = config.styles.get(base_key) {
if let Some(style) = styles.get_mut(style_key) {
for (property_key, property_value) in &style_config.properties {
style.properties.insert(
property_key.clone(),
Theme::read_value(property_value, &config.resources),
);
}
for state in &style_config.states {
let mut new_state = ThemeState::new(state.key.clone());
for (property_key, property_value) in &state.properties {
new_state.properties.insert(
property_key.clone(),
Theme::read_value(property_value, &config.resources),
);
}
if let Some(pos) = style.states.iter().position(|s| s.key == new_state.key) {
style.states.remove(pos);
style.states.insert(pos, new_state)
} else {
style.states.push(new_state);
}
}
}
}
}
fn read_value(property_value: &Value, resources: &HashMap<String, Value>) -> Value {
if let Ok(value) = property_value.clone().into_rust::<String>() {
if let Some(replace_value) = resources.get(&value.replace(RESOURCE_KEY, "")) {
return replace_value.clone();
}
}
property_value.clone()
}
}