victauri_plugin/
bridge.rs1use tauri::{Manager, Runtime};
2use victauri_core::WindowState;
3
4pub trait WebviewBridge: Send + Sync {
6 fn eval_webview(&self, label: Option<&str>, script: &str) -> Result<(), String>;
8 fn get_window_states(&self, label: Option<&str>) -> Vec<WindowState>;
10 fn list_window_labels(&self) -> Vec<String>;
12 fn get_native_handle(&self, label: Option<&str>) -> Result<isize, String>;
14 fn manage_window(&self, label: Option<&str>, action: &str) -> Result<String, String>;
16 fn resize_window(&self, label: Option<&str>, width: u32, height: u32) -> Result<(), String>;
18 fn move_window(&self, label: Option<&str>, x: i32, y: i32) -> Result<(), String>;
20 fn set_window_title(&self, label: Option<&str>, title: &str) -> Result<(), String>;
22}
23
24impl<R: Runtime> WebviewBridge for tauri::AppHandle<R> {
25 fn eval_webview(&self, label: Option<&str>, script: &str) -> Result<(), String> {
26 let windows = self.webview_windows();
27 let webview = match label {
28 Some(l) => windows
29 .get(l)
30 .ok_or_else(|| format!("window not found: {l}"))?,
31 None => windows
32 .get("main")
33 .or_else(|| windows.values().find(|w| w.is_visible().unwrap_or(false)))
34 .or_else(|| windows.values().next())
35 .ok_or_else(|| "no webview available".to_string())?,
36 };
37 webview.eval(script).map_err(|e| e.to_string())
38 }
39
40 fn get_window_states(&self, label: Option<&str>) -> Vec<WindowState> {
41 let windows = self.webview_windows();
42 let mut states = Vec::new();
43
44 for (win_label, window) in &windows {
45 if let Some(filter) = label
46 && win_label != filter
47 {
48 continue;
49 }
50
51 let pos = window.outer_position().unwrap_or_default();
52 let size = window.inner_size().unwrap_or_default();
53
54 states.push(WindowState {
55 label: win_label.clone(),
56 title: window.title().unwrap_or_default(),
57 url: window.url().map(|u| u.to_string()).unwrap_or_default(),
58 visible: window.is_visible().unwrap_or(false),
59 focused: window.is_focused().unwrap_or(false),
60 maximized: window.is_maximized().unwrap_or(false),
61 minimized: window.is_minimized().unwrap_or(false),
62 fullscreen: window.is_fullscreen().unwrap_or(false),
63 position: (pos.x, pos.y),
64 size: (size.width, size.height),
65 });
66 }
67
68 states
69 }
70
71 fn list_window_labels(&self) -> Vec<String> {
72 self.webview_windows().keys().cloned().collect()
73 }
74
75 fn get_native_handle(&self, label: Option<&str>) -> Result<isize, String> {
76 let windows = self.webview_windows();
77 let _webview = match label {
78 Some(l) => windows
79 .get(l)
80 .ok_or_else(|| format!("window not found: {l}"))?,
81 None => windows
82 .get("main")
83 .or_else(|| windows.values().find(|w| w.is_visible().unwrap_or(false)))
84 .or_else(|| windows.values().next())
85 .ok_or_else(|| "no webview available".to_string())?,
86 };
87
88 #[cfg(windows)]
89 {
90 use raw_window_handle::{HasWindowHandle, RawWindowHandle};
91 let handle = _webview.window_handle().map_err(|e| e.to_string())?;
92 match handle.as_raw() {
93 RawWindowHandle::Win32(h) => Ok(h.hwnd.get()),
94 _ => Err("unexpected window handle type".to_string()),
95 }
96 }
97
98 #[cfg(not(windows))]
99 {
100 Err("native handle not yet supported on this platform".to_string())
101 }
102 }
103
104 fn manage_window(&self, label: Option<&str>, action: &str) -> Result<String, String> {
105 let windows = self.webview_windows();
106 let window = match label {
107 Some(l) => windows
108 .get(l)
109 .ok_or_else(|| format!("window not found: {l}"))?,
110 None => windows
111 .get("main")
112 .or_else(|| windows.values().find(|w| w.is_visible().unwrap_or(false)))
113 .or_else(|| windows.values().next())
114 .ok_or_else(|| "no window available".to_string())?,
115 };
116
117 match action {
118 "minimize" => window.minimize().map_err(|e| e.to_string())?,
119 "unminimize" => window.unminimize().map_err(|e| e.to_string())?,
120 "maximize" => window.maximize().map_err(|e| e.to_string())?,
121 "unmaximize" => window.unmaximize().map_err(|e| e.to_string())?,
122 "close" => window.close().map_err(|e| e.to_string())?,
123 "focus" => window.set_focus().map_err(|e| e.to_string())?,
124 "show" => window.show().map_err(|e| e.to_string())?,
125 "hide" => window.hide().map_err(|e| e.to_string())?,
126 "fullscreen" => window.set_fullscreen(true).map_err(|e| e.to_string())?,
127 "unfullscreen" => window.set_fullscreen(false).map_err(|e| e.to_string())?,
128 "always_on_top" => window.set_always_on_top(true).map_err(|e| e.to_string())?,
129 "not_always_on_top" => window.set_always_on_top(false).map_err(|e| e.to_string())?,
130 _ => return Err(format!("unknown action: {action}")),
131 }
132
133 Ok(format!("{action} executed"))
134 }
135
136 fn resize_window(&self, label: Option<&str>, width: u32, height: u32) -> Result<(), String> {
137 let windows = self.webview_windows();
138 let window = match label {
139 Some(l) => windows
140 .get(l)
141 .ok_or_else(|| format!("window not found: {l}"))?,
142 None => windows
143 .get("main")
144 .or_else(|| windows.values().find(|w| w.is_visible().unwrap_or(false)))
145 .or_else(|| windows.values().next())
146 .ok_or_else(|| "no window available".to_string())?,
147 };
148
149 window
150 .set_size(tauri::LogicalSize::new(width, height))
151 .map_err(|e| e.to_string())
152 }
153
154 fn move_window(&self, label: Option<&str>, x: i32, y: i32) -> Result<(), String> {
155 let windows = self.webview_windows();
156 let window = match label {
157 Some(l) => windows
158 .get(l)
159 .ok_or_else(|| format!("window not found: {l}"))?,
160 None => windows
161 .get("main")
162 .or_else(|| windows.values().find(|w| w.is_visible().unwrap_or(false)))
163 .or_else(|| windows.values().next())
164 .ok_or_else(|| "no window available".to_string())?,
165 };
166
167 window
168 .set_position(tauri::LogicalPosition::new(x, y))
169 .map_err(|e| e.to_string())
170 }
171
172 fn set_window_title(&self, label: Option<&str>, title: &str) -> Result<(), String> {
173 let windows = self.webview_windows();
174 let window = match label {
175 Some(l) => windows
176 .get(l)
177 .ok_or_else(|| format!("window not found: {l}"))?,
178 None => windows
179 .get("main")
180 .or_else(|| windows.values().find(|w| w.is_visible().unwrap_or(false)))
181 .or_else(|| windows.values().next())
182 .ok_or_else(|| "no window available".to_string())?,
183 };
184
185 window.set_title(title).map_err(|e| e.to_string())
186 }
187}