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 "minecraft:vanilla" => Some(Theme::from_static(VANILLA)),
60 _ => None,
61 }
62 }
63
64 pub const BUILTIN: &'static [&'static str] = &["minecraft:vanilla"];
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.iter().map(|(a, v)| (a.to_string(), v.to_string())).collect();
77 (k.to_string(), attrs)
78 })
79 .collect(),
80 }
81 }
82
83 pub fn attrs(&self, kind: &str) -> &[(String, String)] {
85 self.rules
86 .iter()
87 .find(|(k, _)| k == kind)
88 .map_or(&[], |(_, a)| a.as_slice())
89 }
90
91 pub fn apply(&self, kind: &str, attrs: &mut Vec<(String, String)>) {
94 for (k, v) in self.attrs(kind) {
95 if !attrs.iter().any(|(ak, _)| ak == k) {
96 attrs.push((k.clone(), v.clone()));
97 }
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn own_attributes_win() {
108 let t = Theme::builtin("minecraft:vanilla").unwrap();
109 let mut attrs = vec![("color".to_string(), "#FF0000".to_string())];
110 t.apply("Button", &mut attrs);
111 assert_eq!(attrs[0].1, "#FF0000");
112 assert!(attrs.iter().any(|(k, v)| k == "bg" && v == "sprite:button"));
113 assert!(t.attrs("Spacer").is_empty());
114 assert!(Theme::builtin("nope:nope").is_none());
115 }
116}