1use crate::core::{Rect, Size};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct WindowId(u64);
11
12impl WindowId {
13 pub fn new(id: u64) -> Self {
14 Self(id)
15 }
16
17 pub fn raw(&self) -> u64 {
18 self.0
19 }
20}
21
22impl std::fmt::Display for WindowId {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(f, "Window({})", self.0)
25 }
26}
27
28#[derive(Debug, Clone)]
30pub struct WindowConfig {
31 pub title: String,
32 pub size: Size,
33 pub resizable: bool,
34 pub decorations: bool,
35 pub transparent: bool,
36 pub always_on_top: bool,
37}
38
39impl WindowConfig {
40 pub fn new(title: impl Into<String>) -> Self {
41 Self {
42 title: title.into(),
43 size: Size::new(800.0, 600.0),
44 resizable: true,
45 decorations: true,
46 transparent: false,
47 always_on_top: false,
48 }
49 }
50
51 pub fn size(mut self, width: f32, height: f32) -> Self {
52 self.size = Size::new(width, height);
53 self
54 }
55
56 pub fn resizable(mut self, resizable: bool) -> Self {
57 self.resizable = resizable;
58 self
59 }
60
61 pub fn decorations(mut self, decorations: bool) -> Self {
62 self.decorations = decorations;
63 self
64 }
65
66 pub fn transparent(mut self, transparent: bool) -> Self {
67 self.transparent = transparent;
68 self
69 }
70
71 pub fn always_on_top(mut self, on_top: bool) -> Self {
72 self.always_on_top = on_top;
73 self
74 }
75}
76
77impl Default for WindowConfig {
78 fn default() -> Self {
79 Self::new("Untitled")
80 }
81}
82
83#[derive(Debug, Clone)]
85pub struct WindowState {
86 pub id: WindowId,
87 pub config: WindowConfig,
88 pub area: Rect,
89 pub focused: bool,
90 pub visible: bool,
91}
92
93pub struct WindowManager {
95 windows: Vec<WindowState>,
96 next_id: u64,
97 focused: Option<WindowId>,
98}
99
100impl WindowManager {
101 pub fn new() -> Self {
102 Self {
103 windows: Vec::new(),
104 next_id: 1,
105 focused: None,
106 }
107 }
108
109 pub fn create(&mut self, config: WindowConfig) -> WindowId {
111 let id = WindowId::new(self.next_id);
112 self.next_id += 1;
113 let area = Rect::new(0.0, 0.0, config.size.width, config.size.height);
114 self.windows.push(WindowState {
115 id,
116 config,
117 area,
118 focused: false,
119 visible: true,
120 });
121 id
122 }
123
124 pub fn close(&mut self, id: WindowId) -> bool {
126 let len_before = self.windows.len();
127 self.windows.retain(|w| w.id != id);
128 if self.focused == Some(id) {
129 self.focused = self.windows.last().map(|w| w.id);
130 }
131 self.windows.len() < len_before
132 }
133
134 pub fn focus(&mut self, id: WindowId) {
136 for w in &mut self.windows {
137 w.focused = w.id == id;
138 }
139 self.focused = Some(id);
140 }
141
142 pub fn focused(&self) -> Option<WindowId> {
144 self.focused
145 }
146
147 pub fn windows(&self) -> &[WindowState] {
149 &self.windows
150 }
151
152 pub fn get(&self, id: WindowId) -> Option<&WindowState> {
154 self.windows.iter().find(|w| w.id == id)
155 }
156
157 pub fn get_mut(&mut self, id: WindowId) -> Option<&mut WindowState> {
159 self.windows.iter_mut().find(|w| w.id == id)
160 }
161
162 pub fn count(&self) -> usize {
164 self.windows.len()
165 }
166}
167
168impl Default for WindowManager {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn window_id_display() {
180 let id = WindowId::new(42);
181 assert_eq!(format!("{id}"), "Window(42)");
182 }
183
184 #[test]
185 fn window_config_defaults() {
186 let cfg = WindowConfig::default();
187 assert!(cfg.resizable);
188 assert!(cfg.decorations);
189 assert!(!cfg.transparent);
190 }
191
192 #[test]
193 fn window_config_builder() {
194 let cfg = WindowConfig::new("Test")
195 .size(1024.0, 768.0)
196 .resizable(false)
197 .always_on_top(true);
198 assert_eq!(cfg.title, "Test");
199 assert!(!cfg.resizable);
200 assert!(cfg.always_on_top);
201 }
202
203 #[test]
204 fn window_manager_create() {
205 let mut wm = WindowManager::new();
206 let id = wm.create(WindowConfig::new("Main"));
207 assert_eq!(wm.count(), 1);
208 assert!(wm.get(id).is_some());
209 }
210
211 #[test]
212 fn window_manager_multiple() {
213 let mut wm = WindowManager::new();
214 let id1 = wm.create(WindowConfig::new("One"));
215 let id2 = wm.create(WindowConfig::new("Two"));
216 assert_ne!(id1, id2);
217 assert_eq!(wm.count(), 2);
218 }
219
220 #[test]
221 fn window_manager_close() {
222 let mut wm = WindowManager::new();
223 let id = wm.create(WindowConfig::new("X"));
224 assert!(wm.close(id));
225 assert_eq!(wm.count(), 0);
226 assert!(!wm.close(id)); }
228
229 #[test]
230 fn window_manager_focus() {
231 let mut wm = WindowManager::new();
232 let id1 = wm.create(WindowConfig::new("A"));
233 let id2 = wm.create(WindowConfig::new("B"));
234 wm.focus(id1);
235 assert_eq!(wm.focused(), Some(id1));
236 wm.focus(id2);
237 assert_eq!(wm.focused(), Some(id2));
238 assert!(!wm.get(id1).unwrap().focused);
239 assert!(wm.get(id2).unwrap().focused);
240 }
241
242 #[test]
243 fn window_manager_close_focused_shifts() {
244 let mut wm = WindowManager::new();
245 let id1 = wm.create(WindowConfig::new("A"));
246 let id2 = wm.create(WindowConfig::new("B"));
247 wm.focus(id1);
248 wm.close(id1);
249 assert_eq!(wm.focused(), Some(id2));
250 }
251}