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
11use tauri::command;
12use once_cell::sync::Lazy;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ManagedWindow {
16    pub id: String,
17    /// OS handle so we can actually talk to the real window
18    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    // System window management methods
251    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