1pub type Rule = (String, Vec<(String, String)>);
15
16#[derive(Debug, Clone, Default, PartialEq)]
18pub struct Theme {
19 rules: Vec<Rule>,
20}
21
22pub const VANILLA: &[(&str, &[(&str, &str)])] = &[
25 ("Screen", &[("bg", "sprite:panel")]),
26 ("Label", &[("color", "#404040"), ("shadow", "false")]),
27 (
28 "Button",
29 &[
30 ("bg", "sprite:button"),
31 ("color", "#FFFFFF"),
32 ("shadow", "true"),
33 ("hover:bg", "sprite:button_hover"),
34 ("disabled:bg", "sprite:button_disabled"),
35 ("disabled:color", "#A0A0A0"),
36 ],
37 ),
38 (
39 "ToggleButton",
40 &[
41 ("bg", "sprite:button"),
42 ("color", "#FFFFFF"),
43 ("shadow", "true"),
44 ("hover:bg", "sprite:button_hover"),
45 ("checked:bg", "sprite:button_disabled"),
46 ("checked:color", "#FFFFA0"),
47 ("disabled:bg", "sprite:button_disabled"),
48 ("disabled:color", "#A0A0A0"),
49 ],
50 ),
51 ("ListView", &[("bg", "sprite:slot")]),
52 ("ScrollView", &[("bg", "sprite:slot")]),
53];
54
55impl Theme {
56 pub fn builtin(id: &str) -> Option<Theme> {
58 match id {
59 yog_ids::game::VANILLA_THEME => Some(Theme::from_static(VANILLA)),
60 _ => None,
61 }
62 }
63
64 pub const BUILTIN: &'static [&'static str] = &[yog_ids::game::VANILLA_THEME];
66
67 pub fn from_rules(rules: Vec<Rule>) -> Theme {
68 Theme { rules }
69 }
70
71 fn from_static(rules: &[(&str, &[(&str, &str)])]) -> Theme {
72 Theme {
73 rules: rules
74 .iter()
75 .map(|(k, attrs)| {
76 let attrs = attrs
77 .iter()
78 .map(|(a, v)| (a.to_string(), v.to_string()))
79 .collect();
80 (k.to_string(), attrs)
81 })
82 .collect(),
83 }
84 }
85
86 pub fn attrs(&self, kind: &str) -> &[(String, String)] {
88 self.rules
89 .iter()
90 .find(|(k, _)| k == kind)
91 .map_or(&[], |(_, a)| a.as_slice())
92 }
93
94 pub fn apply(&self, kind: &str, attrs: &mut Vec<(String, String)>) {
97 for (k, v) in self.attrs(kind) {
98 if !attrs.iter().any(|(ak, _)| ak == k) {
99 attrs.push((k.clone(), v.clone()));
100 }
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn own_attributes_win() {
111 let t = Theme::builtin("minecraft:vanilla").unwrap();
112 let mut attrs = vec![("color".to_string(), "#FF0000".to_string())];
113 t.apply("Button", &mut attrs);
114 assert_eq!(attrs[0].1, "#FF0000");
115 assert!(attrs.iter().any(|(k, v)| k == "bg" && v == "sprite:button"));
116 assert!(t.attrs("Spacer").is_empty());
117 assert!(Theme::builtin("nope:nope").is_none());
118 }
119}