1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5use chrono::{DateTime, Utc};
6use crate::workspace::Workspace;
7use crate::layout::LayoutType;
8use crate::config::Config;
9use crate::system_window::{SystemWindow, SystemWindowManager, PlatformWindowManager};
10
11use tauri::command;
12use once_cell::sync::Lazy;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ManagedWindow {
16 pub id: String,
17 pub handle: u64,
19 pub title: String,
20 pub app_name: String,
21 pub workspace_id: String,
22 pub position: WindowPosition,
23 pub size: WindowSize,
24 pub state: WindowState,
25 pub created_at: DateTime<Utc>,
26 pub last_focused: DateTime<Utc>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct WindowPosition {
31 pub x: i32,
32 pub y: i32,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct WindowSize {
37 pub width: u32,
38 pub height: u32,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub enum WindowState {
43 Normal,
44 Minimized,
45 Maximized,
46 Fullscreen,
47}
48
49pub struct WindowManager {
50 pub windows: Arc<Mutex<HashMap<String, ManagedWindow>>>,
51 pub workspaces: Arc<Mutex<HashMap<String, Workspace>>>,
52 pub active_workspace: Arc<Mutex<String>>,
53 pub config: Arc<Mutex<Config>>,
54 pub system_windows: Arc<Mutex<HashMap<u64, SystemWindow>>>,
55}
56
57impl WindowManager {
58 pub fn new() -> Self {
59 let mut workspaces = HashMap::new();
60 let default_workspace_id = Uuid::new_v4().to_string();
61
62 workspaces.insert(
63 default_workspace_id.clone(),
64 Workspace::new("Default".to_string(), LayoutType::Tiling)
65 );
66
67 Self {
68 windows: Arc::new(Mutex::new(HashMap::new())),
69 workspaces: Arc::new(Mutex::new(workspaces)),
70 active_workspace: Arc::new(Mutex::new(default_workspace_id)),
71 config: Arc::new(Mutex::new(Config::default())),
72 system_windows: Arc::new(Mutex::new(HashMap::new())),
73 }
74 }
75
76 pub fn add_window(&self, handle: u64, title: String, app_name: String) -> Result<String, String> {
77 let window_id = Uuid::new_v4().to_string();
78 let active_workspace = self.active_workspace.lock().unwrap().clone();
79
80 let window = ManagedWindow {
81 id: window_id.clone(),
82 handle,
83 title,
84 app_name,
85 workspace_id: active_workspace.clone(),
86 position: WindowPosition { x: 0, y: 0 },
87 size: WindowSize { width: 800, height: 600 },
88 state: WindowState::Normal,
89 created_at: Utc::now(),
90 last_focused: Utc::now(),
91 };
92
93 self.windows.lock().unwrap().insert(window_id.clone(), window);
94
95 if let Some(workspace) = self.workspaces.lock().unwrap().get_mut(&active_workspace) {
96 workspace.add_window(window_id.clone());
97 }
98
99 self.arrange_workspace(&active_workspace)?;
100
101 Ok(window_id)
102 }
103
104 pub fn remove_window(&self, window_id: &str) -> Result<(), String> {
105 if let Some(window) = self.windows.lock().unwrap().remove(window_id) {
106 if let Some(workspace) = self.workspaces.lock().unwrap().get_mut(&window.workspace_id) {
107 workspace.remove_window(window_id);
108 self.arrange_workspace(&window.workspace_id)?;
109 }
110 Ok(())
111 } else {
112 Err("Window not found".to_string())
113 }
114 }
115
116 pub fn get_windows(&self) -> Vec<ManagedWindow> {
117 self.windows.lock().unwrap().values().cloned().collect()
118 }
119
120 pub fn get_workspace_windows(&self, workspace_id: &str) -> Vec<ManagedWindow> {
121 self.windows
122 .lock()
123 .unwrap()
124 .values()
125 .filter(|w| w.workspace_id == workspace_id)
126 .cloned()
127 .collect()
128 }
129
130 pub fn focus_window(&self, window_id: &str) -> Result<(), String> {
131 if let Some(window) = self.windows.lock().unwrap().get_mut(window_id) {
132 window.last_focused = Utc::now();
133
134 if let Some(workspace) = self.workspaces.lock().unwrap().get_mut(&window.workspace_id) {
135 workspace.focus_window(window_id);
136 }
137
138 Ok(())
139 } else {
140 Err("Window not found".to_string())
141 }
142 }
143
144 pub fn arrange_workspace(&self, workspace_id: &str) -> Result<(), String> {
145 let workspace = self.workspaces.lock().unwrap();
146 let workspace = workspace.get(workspace_id).ok_or("Workspace not found")?;
147
148 let window_ids = workspace.get_windows();
149 let layout = workspace.layout.clone();
150
151 drop(workspace);
152
153 let config = self.config.lock().unwrap();
154 let screen_width = config.screen_width;
155 let screen_height = config.screen_height;
156 let gap = config.window_gap;
157 drop(config);
158
159 match layout {
160 LayoutType::Tiling => self.arrange_tiling(&window_ids, screen_width, screen_height, gap),
161 LayoutType::Floating => Ok(()),
162 LayoutType::Monocle => self.arrange_monocle(&window_ids, screen_width, screen_height),
163 }
164 }
165
166 fn arrange_tiling(&self, window_ids: &[String], screen_width: u32, screen_height: u32, gap: u32) -> Result<(), String> {
167 if window_ids.is_empty() {
168 return Ok(());
169 }
170
171 let mut windows = self.windows.lock().unwrap();
172 let count = window_ids.len();
173
174 if count == 1 {
175 if let Some(window) = windows.get_mut(&window_ids[0]) {
176 window.position = WindowPosition { x: gap as i32, y: gap as i32 };
177 window.size = WindowSize {
178 width: screen_width - (gap * 2),
179 height: screen_height - (gap * 2),
180 };
181 }
182 } else {
183 let cols = (count as f64).sqrt().ceil() as usize;
184 let rows = (count + cols - 1) / cols;
185
186 let window_width = (screen_width - gap * (cols as u32 + 1)) / cols as u32;
187 let window_height = (screen_height - gap * (rows as u32 + 1)) / rows as u32;
188
189 for (i, window_id) in window_ids.iter().enumerate() {
190 if let Some(window) = windows.get_mut(window_id) {
191 let col = i % cols;
192 let row = i / cols;
193
194 window.position = WindowPosition {
195 x: (gap + col as u32 * (window_width + gap)) as i32,
196 y: (gap + row as u32 * (window_height + gap)) as i32,
197 };
198 window.size = WindowSize {
199 width: window_width,
200 height: window_height,
201 };
202 }
203 }
204 }
205
206 Ok(())
207 }
208
209 fn arrange_monocle(&self, window_ids: &[String], screen_width: u32, screen_height: u32) -> Result<(), String> {
210 let mut windows = self.windows.lock().unwrap();
211
212 for window_id in window_ids {
213 if let Some(window) = windows.get_mut(window_id) {
214 window.position = WindowPosition { x: 0, y: 0 };
215 window.size = WindowSize {
216 width: screen_width,
217 height: screen_height,
218 };
219 }
220 }
221
222 Ok(())
223 }
224
225 pub fn create_workspace(&self, name: String, layout: LayoutType) -> String {
226 let workspace_id = Uuid::new_v4().to_string();
227 let workspace = Workspace::new(name, layout);
228
229 self.workspaces.lock().unwrap().insert(workspace_id.clone(), workspace);
230 workspace_id
231 }
232
233 pub fn switch_workspace(&self, workspace_id: &str) -> Result<(), String> {
234 if self.workspaces.lock().unwrap().contains_key(workspace_id) {
235 *self.active_workspace.lock().unwrap() = workspace_id.to_string();
236 Ok(())
237 } else {
238 Err("Workspace not found".to_string())
239 }
240 }
241
242 pub fn get_workspaces(&self) -> Vec<Workspace> {
243 self.workspaces.lock().unwrap().values().cloned().collect()
244 }
245
246 pub fn get_active_workspace(&self) -> String {
247 self.active_workspace.lock().unwrap().clone()
248 }
249
250 pub fn get_system_windows(&self) -> Result<Vec<SystemWindow>, String> {
252 let windows = PlatformWindowManager::get_all_windows()?;
253 *self.system_windows.lock().unwrap() = windows.iter()
254 .map(|w| (w.handle, w.clone()))
255 .collect();
256 Ok(windows)
257 }
258
259 pub fn move_system_window(&self, handle: u64, x: i32, y: i32) -> Result<(), String> {
260 PlatformWindowManager::move_window(handle, x, y)?;
261
262 if let Ok(Some(window)) = PlatformWindowManager::get_window_by_handle(handle) {
263 self.system_windows.lock().unwrap().insert(handle, window);
264 }
265
266 Ok(())
267 }
268
269 pub fn resize_system_window(&self, handle: u64, width: u32, height: u32) -> Result<(), String> {
270 PlatformWindowManager::resize_window(handle, width, height)?;
271
272 if let Ok(Some(window)) = PlatformWindowManager::get_window_by_handle(handle) {
273 self.system_windows.lock().unwrap().insert(handle, window);
274 }
275
276 Ok(())
277 }
278
279 pub fn set_system_window_bounds(&self, handle: u64, x: i32, y: i32, width: u32, height: u32) -> Result<(), String> {
280 PlatformWindowManager::set_window_position_and_size(handle, x, y, width, height)?;
281
282 if let Ok(Some(window)) = PlatformWindowManager::get_window_by_handle(handle) {
283 self.system_windows.lock().unwrap().insert(handle, window);
284 }
285
286 Ok(())
287 }
288
289 pub fn minimize_system_window(&self, handle: u64) -> Result<(), String> {
290 PlatformWindowManager::minimize_window(handle)
291 }
292
293 pub fn maximize_system_window(&self, handle: u64) -> Result<(), String> {
294 PlatformWindowManager::maximize_window(handle)
295 }
296
297 pub fn restore_system_window(&self, handle: u64) -> Result<(), String> {
298 PlatformWindowManager::restore_window(handle)
299 }
300
301 pub fn close_system_window(&self, handle: u64) -> Result<(), String> {
302 PlatformWindowManager::close_window(handle)?;
303 self.system_windows.lock().unwrap().remove(&handle);
304 Ok(())
305 }
306
307 pub fn focus_system_window(&self, handle: u64) -> Result<(), String> {
308 PlatformWindowManager::focus_window(handle)
309 }
310
311 pub fn hide_system_window(&self, handle: u64) -> Result<(), String> {
312 PlatformWindowManager::hide_window(handle)
313 }
314
315 pub fn show_system_window(&self, handle: u64) -> Result<(), String> {
316 PlatformWindowManager::show_window(handle)
317 }
318
319 pub fn arrange_system_windows(&self, window_handles: &[u64]) -> Result<(), String> {
320 let config = self.config.lock().unwrap();
321 let screen_width = config.screen_width;
322 let screen_height = config.screen_height;
323 let gap = config.window_gap;
324 drop(config);
325
326 if window_handles.is_empty() {
327 return Ok(());
328 }
329
330 let count = window_handles.len();
331
332 if count == 1 {
333 self.set_system_window_bounds(
334 window_handles[0],
335 gap as i32,
336 gap as i32,
337 screen_width - (gap * 2),
338 screen_height - (gap * 2),
339 )?;
340 } else {
341 let cols = (count as f64).sqrt().ceil() as usize;
342 let rows = (count + cols - 1) / cols;
343
344 let window_width = (screen_width - gap * (cols as u32 + 1)) / cols as u32;
345 let window_height = (screen_height - gap * (rows as u32 + 1)) / rows as u32;
346
347 for (i, &handle) in window_handles.iter().enumerate() {
348 let col = i % cols;
349 let row = i / cols;
350
351 let x = (gap + col as u32 * (window_width + gap)) as i32;
352 let y = (gap + row as u32 * (window_height + gap)) as i32;
353
354 self.set_system_window_bounds(handle, x, y, window_width, window_height)?;
355 }
356 }
357
358 Ok(())
359 }
360}
361