tauri_winutils/
window_manager.rs

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