orbtk_theming/config/
theme_config.rs1use std::collections::HashMap;
2
3use ron::{de::from_str, Value};
4use serde_derive::{Deserialize, Serialize};
5
6use crate::{config::StyleConfig, Selector};
7
8pub static BASE_STYLE: &str = "base";
9pub static RESOURCE_KEY: &str = "$";
10
11#[derive(Default, Clone, Debug, Serialize, Deserialize)]
13#[serde(rename = "Theme")]
14pub struct ThemeConfig {
15 #[serde(default)]
16 pub styles: HashMap<String, StyleConfig>,
17 #[serde(default)]
18 pub resources: HashMap<String, Value>,
19}
20
21impl<'a> ThemeConfig {
22 pub fn extend(mut self, other: ThemeConfig) -> Self {
25 let mut other = other;
26
27 for style in other.styles.drain() {
28 self.styles.insert(style.0, style.1);
29 }
30
31 for resource in other.resources.drain() {
32 self.resources.insert(resource.0, resource.1);
33 }
34
35 self
36 }
37
38 pub fn property(&'a self, property: &str, selector: &Selector) -> Option<Value> {
40 if let Some(style) = &selector.style {
41 if let Some(style) = self.styles.get(style) {
42 return self.get_property(property, style, selector);
43 }
44 }
45
46 if let Some(base_style) = self.styles.get(BASE_STYLE) {
48 return self.get_property(property, base_style, selector);
49 }
50
51 None
52 }
53
54 fn get_property(
55 &'a self,
56 property: &str,
57 style: &'a StyleConfig,
58 selector: &Selector,
59 ) -> Option<Value> {
60 if let Some(state) = &selector.state {
62 if let Some(properties) = style.states.get(state) {
63 return self.get_property_value(property, properties);
64 }
65
66 if style.base.is_empty() {
68 return None;
69 }
70
71 if let Some(base_style) = self.styles.get(&style.base) {
72 if let Some(properties) = base_style.states.get(state) {
73 return self.get_property_value(property, properties);
74 }
75 }
76 }
77
78 let value = self.get_property_value(property, &style.properties);
79
80 if value.is_some() {
81 return value;
82 }
83
84 if style.base.is_empty() {
86 return None;
87 }
88
89 if let Some(base_style) = self.styles.get(&style.base) {
90 return self.get_property(property, base_style, selector);
91 }
92
93 None
94 }
95
96 fn get_property_value(
97 &self,
98 property: &str,
99 properties: &'a HashMap<String, Value>,
100 ) -> Option<Value> {
101 let property = properties.get(property)?;
102
103 if let Ok(key) = property.clone().into_rust::<String>() {
105 if key.starts_with(RESOURCE_KEY) {
106 return Some(self.resources.get(&key.replace(RESOURCE_KEY, ""))?.clone());
107 }
108 }
109
110 Some(property.clone())
111 }
112}
113
114impl From<&str> for ThemeConfig {
115 fn from(s: &str) -> Self {
116 from_str(s).unwrap()
117 }
118}