tauri_plugin_window_system/
lib.rs1mod commands;
2mod events;
3mod lifecycle;
4mod message;
5mod registry;
6
7use crate::lifecycle::capture_window_geometry;
8use crate::registry::{window_system_error, WindowSystemErrorKind};
9use std::cmp::Ordering;
10use tauri::plugin::{Builder, TauriPlugin};
11use tauri::{Manager, Runtime, WebviewWindow};
12
13pub use commands::{RestoreSkipReason, RestoreSkippedWindow, RestoreWindowsResult};
14pub use message::{
15 BroadcastWindowMessageRequest, SendWindowMessageRequest, WindowMessageDispatchResult,
16 WindowMessageEnvelope, WindowMessageKind, WindowMessageScope,
17};
18pub use registry::{WindowDescriptor, WindowGeometry, WindowRegistry, WindowStateStore};
19
20pub fn init<R: Runtime>() -> TauriPlugin<R> {
21 Builder::new("window-system")
22 .setup(|app, _api| {
23 let app_data_dir = app.path().app_data_dir().map_err(|err| err.to_string())?;
24 let state_path = app_data_dir.join("window-system/windows.json");
25 let registry = WindowRegistry::new(WindowStateStore::new(state_path));
26
27 sync_live_windows(app, ®istry)?;
32 app.manage(registry);
33 Ok(())
34 })
35 .on_drop(|app| {
36 if let Err(err) = app.state::<WindowRegistry>().flush() {
37 eprintln!("window-system: drop flush failed: {err}");
38 }
39 })
40 .invoke_handler(tauri::generate_handler![
41 commands::open_window,
42 commands::close_window,
43 commands::list_windows,
44 commands::emit_to_window,
45 commands::restore_windows,
46 commands::send_window_message,
47 commands::broadcast_window_message
48 ])
49 .build()
50}
51
52pub(crate) fn sync_live_windows<R: Runtime>(
53 app: &tauri::AppHandle<R>,
54 registry: &WindowRegistry,
55) -> Result<(), String> {
56 let mut windows: Vec<_> = app.webview_windows().into_values().collect();
57 windows.sort_by(compare_window_labels);
58
59 for window in windows {
60 sync_live_webview_window(registry, &window)?;
61 }
62
63 Ok(())
64}
65
66pub(crate) fn sync_live_window<R: Runtime>(
67 app: &tauri::AppHandle<R>,
68 registry: &WindowRegistry,
69 label: &str,
70) -> Result<(), String> {
71 if registry.get(label)?.is_some() {
72 return Ok(());
75 }
76
77 let window = app.get_webview_window(label).ok_or_else(|| {
78 window_system_error(
79 WindowSystemErrorKind::WindowNotFound,
80 format!("window not found: {label}"),
81 )
82 })?;
83
84 sync_live_webview_window(registry, &window)
85}
86
87fn sync_live_webview_window<R: Runtime>(
88 registry: &WindowRegistry,
89 window: &WebviewWindow<R>,
90) -> Result<(), String> {
91 registry.upsert_live_window(live_window_descriptor(window)?)?;
92 Ok(())
93}
94
95fn live_window_descriptor<R: Runtime>(
96 window: &WebviewWindow<R>,
97) -> Result<WindowDescriptor, String> {
98 Ok(WindowDescriptor {
99 label: window.label().to_string(),
100 url: window.url().map_err(|err| err.to_string())?.to_string(),
101 parent: None,
102 title: window.title().ok(),
103 geometry: capture_window_geometry(window).ok(),
104 })
105}
106
107fn compare_window_labels<R: Runtime>(
108 left: &WebviewWindow<R>,
109 right: &WebviewWindow<R>,
110) -> Ordering {
111 left.label().cmp(right.label())
112}