orbtk_theming/
theme.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::collections::HashMap;

use ron::Value;

use crate::{
    config::{ThemeConfig, RESOURCE_KEY},
    Selector, Style,
};

#[derive(Debug, Clone, Default, PartialEq)]
pub struct Theme {
    styles: HashMap<String, Style>,
}

impl Theme {
    pub fn from_config(theme: ThemeConfig) -> Self {
        let mut styles = HashMap::new();

        for style_key in theme.styles.keys() {
            let mut properties = HashMap::new();
            Theme::read_properties(style_key, &theme, &mut properties);

            let mut states = HashMap::new();

            let base_key = theme.styles.get(style_key).unwrap().base.clone();

            if let Some(base) = theme.styles.get(&base_key) {
                for state_key in base.states.keys() {
                    let mut state = HashMap::new();
                    Theme::read_states(&base_key, state_key, &theme, &mut state);
                    states.insert(state_key.clone(), state);
                }
            }

            for state_key in theme.styles.get(style_key).unwrap().states.keys() {
                let mut state = HashMap::new();
                Theme::read_states(style_key, state_key, &theme, &mut state);
                states.insert(state_key.clone(), state);
            }

            styles.insert(style_key.clone(), Style { properties, states });
        }

        Theme { styles }
    }

    pub fn style(&self, key: &str) -> Option<&Style> {
        self.styles.get(key)
    }

    pub fn properties<'a>(&'a self, selector: &Selector) -> Option<&'a HashMap<String, Value>> {
        if !selector.dirty() {
            return None;
        }

        if let Some(style) = &selector.style {
            if let Some(state) = &selector.state {
                return self.styles.get(style)?.states.get(state);
            }

            return Some(&self.styles.get(style)?.properties);
        }

        None
    }

    fn read_properties(key: &str, theme: &ThemeConfig, properties: &mut HashMap<String, Value>) {
        if key.is_empty() {
            return;
        }

        if let Some(style) = theme.styles.get(key) {
            Theme::read_properties(&style.base, theme, properties);

            for (key, value) in &style.properties {
                Theme::read_property(key, value, theme, properties);
            }
        }
    }

    fn read_states(
        style_key: &str,
        state_key: &str,
        theme: &ThemeConfig,
        states: &mut HashMap<String, Value>,
    ) {
        if style_key.is_empty() || state_key.is_empty() {
            return;
        }

        if let Some(style) = theme.styles.get(style_key) {
            for (key, value) in &style.properties {
                Theme::read_property(key, value, theme, states);
            }

            if let Some(state) = style.states.get(state_key) {
                for (key, value) in state {
                    Theme::read_property(key, value, theme, states);
                }
            }
        }
    }

    fn read_property(
        key: &str,
        value: &Value,
        theme: &ThemeConfig,
        map: &mut HashMap<String, Value>,
    ) {
        if let Ok(value) = value.clone().into_rust::<String>() {
            if value.starts_with(RESOURCE_KEY) {
                if let Some(value) = theme.resources.get(&value.replace(RESOURCE_KEY, "")) {
                    map.insert(key.to_string(), value.clone());
                }
            } else {
                map.insert(key.to_string(), Value::String(value));
            }
        } else {
            map.insert(key.to_string(), value.clone());
        }
    }
}