plushie_renderer_lib/
window_map.rs1use iced::{Theme, window};
9use plushie_widget_sdk::runtime::ThemeChrome;
10use std::collections::HashMap;
11
12struct WindowState {
14 decorated: bool,
17 theme: Option<Theme>,
20 theme_follows_system: bool,
21 theme_chrome: ThemeChrome,
22 scale_factor: Option<f32>,
24}
25
26impl Default for WindowState {
27 fn default() -> Self {
28 Self {
29 decorated: true,
30 theme: None,
31 theme_follows_system: false,
32 theme_chrome: ThemeChrome::default(),
33 scale_factor: None,
34 }
35 }
36}
37
38pub struct WindowMap {
42 forward: HashMap<String, (window::Id, WindowState)>,
44 reverse: HashMap<window::Id, String>,
46}
47
48impl Default for WindowMap {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl WindowMap {
55 pub fn new() -> Self {
56 Self {
57 forward: HashMap::new(),
58 reverse: HashMap::new(),
59 }
60 }
61
62 pub fn insert(&mut self, window_id: String, iced_id: window::Id) {
66 if let Some((old_iced_id, _)) = self.forward.get(&window_id) {
67 self.reverse.remove(old_iced_id);
68 }
69 self.forward
70 .insert(window_id.clone(), (iced_id, WindowState::default()));
71 self.reverse.insert(iced_id, window_id);
72 }
73
74 pub fn remove_by_iced(&mut self, iced_id: &window::Id) -> Option<String> {
75 if let Some(window_id) = self.reverse.remove(iced_id) {
76 self.forward.remove(&window_id);
77 Some(window_id)
78 } else {
79 None
80 }
81 }
82
83 pub fn remove_by_window(&mut self, window_id: &str) -> Option<window::Id> {
84 if let Some((iced_id, _)) = self.forward.remove(window_id) {
85 self.reverse.remove(&iced_id);
86 Some(iced_id)
87 } else {
88 None
89 }
90 }
91
92 pub fn contains_window(&self, window_id: &str) -> bool {
93 self.forward.contains_key(window_id)
94 }
95
96 pub fn get_iced(&self, window_id: &str) -> Option<&window::Id> {
97 self.forward.get(window_id).map(|(id, _)| id)
98 }
99
100 pub fn get_window_id(&self, iced_id: &window::Id) -> Option<&str> {
104 self.reverse.get(iced_id).map(String::as_str)
105 }
106
107 pub fn iced_ids(&self) -> impl Iterator<Item = &window::Id> {
108 self.reverse.keys()
109 }
110
111 pub fn window_ids(&self) -> impl Iterator<Item = &String> {
112 self.forward.keys()
113 }
114
115 pub fn is_empty(&self) -> bool {
116 self.forward.is_empty()
117 }
118
119 pub fn iter(&self) -> impl Iterator<Item = (&String, &window::Id)> {
120 self.forward.iter().map(|(jid, (iid, _))| (jid, iid))
121 }
122
123 pub fn clear(&mut self) {
124 self.forward.clear();
125 self.reverse.clear();
126 }
127
128 pub fn is_decorated(&self, window_id: &str) -> bool {
131 self.forward.get(window_id).is_none_or(|(_, s)| s.decorated)
132 }
133
134 pub fn set_decorated(&mut self, window_id: &str, decorated: bool) {
135 if let Some((_, state)) = self.forward.get_mut(window_id) {
136 state.decorated = decorated;
137 }
138 }
139
140 pub fn cached_theme(&self, window_id: &str) -> Option<&Theme> {
143 self.forward
144 .get(window_id)
145 .and_then(|(_, s)| s.theme.as_ref())
146 }
147
148 pub fn theme_follows_system(&self, window_id: &str) -> bool {
149 self.forward
150 .get(window_id)
151 .is_some_and(|(_, s)| s.theme_follows_system)
152 }
153
154 pub fn any_theme_follows_system(&self) -> bool {
155 self.forward
156 .values()
157 .any(|(_, state)| state.theme_follows_system)
158 }
159
160 pub fn cached_theme_chrome(&self, window_id: &str) -> Option<ThemeChrome> {
161 self.forward
162 .get(window_id)
163 .and_then(|(_, s)| s.theme.as_ref().map(|_| s.theme_chrome))
164 }
165
166 pub fn set_theme(&mut self, window_id: &str, theme: Theme, chrome: ThemeChrome) {
167 if let Some((_, state)) = self.forward.get_mut(window_id) {
168 state.theme = Some(theme);
169 state.theme_follows_system = false;
170 state.theme_chrome = chrome;
171 }
172 }
173
174 pub fn set_theme_follows_system(&mut self, window_id: &str) {
175 if let Some((_, state)) = self.forward.get_mut(window_id) {
176 state.theme = None;
177 state.theme_follows_system = true;
178 state.theme_chrome = ThemeChrome::default();
179 }
180 }
181
182 pub fn clear_theme_cache(&mut self) {
183 for (_, state) in self.forward.values_mut() {
184 state.theme = None;
185 state.theme_follows_system = false;
186 state.theme_chrome = ThemeChrome::default();
187 }
188 }
189
190 pub fn scale_factor(&self, window_id: &str) -> Option<f32> {
193 self.forward
194 .get(window_id)
195 .and_then(|(_, s)| s.scale_factor)
196 }
197
198 pub fn set_scale_factor(&mut self, window_id: &str, scale_factor: Option<f32>) {
199 if let Some((_, state)) = self.forward.get_mut(window_id) {
200 state.scale_factor = scale_factor;
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn system_theme_is_distinct_from_missing_and_cached_theme() {
211 let mut map = WindowMap::new();
212 map.insert("main".to_string(), window::Id::unique());
213
214 assert!(!map.theme_follows_system("main"));
215 assert!(!map.any_theme_follows_system());
216 assert!(map.cached_theme("main").is_none());
217
218 map.set_theme("main", Theme::Light, ThemeChrome::default());
219 assert!(!map.theme_follows_system("main"));
220 assert!(!map.any_theme_follows_system());
221 assert!(matches!(map.cached_theme("main"), Some(Theme::Light)));
222
223 map.set_theme_follows_system("main");
224 assert!(map.theme_follows_system("main"));
225 assert!(map.any_theme_follows_system());
226 assert!(map.cached_theme("main").is_none());
227
228 map.clear_theme_cache();
229 assert!(!map.theme_follows_system("main"));
230 assert!(!map.any_theme_follows_system());
231 assert!(map.cached_theme("main").is_none());
232 }
233}