1use pyo3::{prelude::*, types::PyString};
2use pyo3_utils::py_wrapper::{PyWrapper, PyWrapperT0};
3use tauri::webview;
4
5use crate::{
6 ext_mod::{
7 image::Image,
8 menu::{context_menu_impl, ImplContextMenu, Menu, MenuEvent},
9 window::Window,
10 Position, Theme, Url, WebviewEvent, WindowEvent,
11 },
12 tauri_runtime::Runtime,
13 utils::{delegate_inner, PyResultExt as _},
14};
15
16pub(crate) type TauriWebviewWindow = webview::WebviewWindow<Runtime>;
17type TauriWebview = webview::Webview<Runtime>;
18
19#[pyclass(frozen)]
21#[non_exhaustive]
22pub struct WebviewWindow(pub PyWrapper<PyWrapperT0<TauriWebviewWindow>>);
23
24impl WebviewWindow {
25 pub(crate) fn new(webview_window: TauriWebviewWindow) -> Self {
26 Self(PyWrapper::new0(webview_window))
27 }
28}
29
30#[pymethods]
31impl WebviewWindow {
32 fn run_on_main_thread(&self, py: Python<'_>, handler: PyObject) -> PyResult<()> {
33 py.allow_threads(|| {
34 delegate_inner!(self, run_on_main_thread, move || {
35 Python::with_gil(|py| {
36 let handler = handler.bind(py);
37 let result = handler.call0();
38 result.unwrap_unraisable_py_result(py, Some(handler), || {
39 "Python exception occurred in `WebviewWindow::run_on_main_thread`"
40 });
41 })
42 })
43 })
44 }
45
46 fn label<'py>(&self, py: Python<'py>) -> Bound<'py, PyString> {
47 let webview_window = self.0.inner_ref();
48 PyString::intern(py, webview_window.label())
50 }
51
52 fn on_window_event(&self, py: Python<'_>, handler: PyObject) {
53 py.allow_threads(|| {
54 self.0.inner_ref().on_window_event(move |window_event| {
55 Python::with_gil(|py| {
56 let window_event: WindowEvent = WindowEvent::from_tauri(py, window_event)
57 .expect("Failed to convert `WindowEvent` to pyobject");
59
60 let handler = handler.bind(py);
61 let result = handler.call1((window_event,));
62 result.unwrap_unraisable_py_result(py, Some(handler), || {
63 "Python exception occurred in `WebviewWindow::on_window_event` handler"
64 });
65 })
66 })
67 })
68 }
69
70 fn on_menu_event(slf: Py<Self>, py: Python<'_>, handler: PyObject) {
71 let moved_slf = slf.clone_ref(py);
72 py.allow_threads(|| {
73 slf.get()
74 .0
75 .inner_ref()
76 .on_menu_event(move |_window, menu_event| {
77 Python::with_gil(|py| {
78 let window: &Py<Self> = &moved_slf; debug_assert_eq!(
83 &*window.get().0.inner_ref().as_ref().window_ref(),
84 _window
85 );
86 let menu_event: Bound<'_, MenuEvent> =
87 MenuEvent::intern(py, &menu_event.id.0);
88
89 let handler = handler.bind(py);
90 let result = handler.call1((window, menu_event));
91 result.unwrap_unraisable_py_result(py, Some(handler), || {
92 "Python exception occurred in `WebviewWindow::on_menu_event` handler"
93 });
94 })
95 })
96 })
97 }
98
99 fn menu(&self, py: Python<'_>) -> Option<Menu> {
100 py.allow_threads(|| self.0.inner_ref().menu().map(Menu::new))
101 }
102
103 fn set_menu(&self, py: Python<'_>, menu: Py<Menu>) -> PyResult<Option<Menu>> {
104 py.allow_threads(|| {
105 let menu = menu.get().0.inner_ref().clone();
106 let returned_menu = delegate_inner!(self, set_menu, menu)?;
107 PyResult::Ok(returned_menu.map(Menu::new))
108 })
109 }
110
111 fn remove_menu(&self, py: Python<'_>) -> PyResult<Option<Menu>> {
112 py.allow_threads(|| {
113 let returned_menu = delegate_inner!(self, remove_menu,)?;
114 PyResult::Ok(returned_menu.map(Menu::new))
115 })
116 }
117
118 fn hide_menu(&self, py: Python<'_>) -> PyResult<()> {
119 py.allow_threads(|| delegate_inner!(self, hide_menu,))
120 }
121
122 fn show_menu(&self, py: Python<'_>) -> PyResult<()> {
123 py.allow_threads(|| delegate_inner!(self, show_menu,))
124 }
125
126 fn is_menu_visible(&self, py: Python<'_>) -> PyResult<bool> {
127 py.allow_threads(|| delegate_inner!(self, is_menu_visible,))
128 }
129
130 fn popup_menu(&self, py: Python<'_>, menu: ImplContextMenu) -> PyResult<()> {
131 py.allow_threads(|| {
132 context_menu_impl!(&menu, |menu| delegate_inner!(self, popup_menu, menu))
133 })
134 }
135
136 fn popup_menu_at(
137 &self,
138 py: Python<'_>,
139 menu: ImplContextMenu,
140 position: Py<Position>,
141 ) -> PyResult<()> {
142 let position = position.get().to_tauri(py)?;
143 py.allow_threads(|| {
144 context_menu_impl!(&menu, |menu| delegate_inner!(
145 self,
146 popup_menu_at,
147 menu,
148 position
149 ))
150 })
151 }
152
153 fn is_fullscreen(&self, py: Python<'_>) -> PyResult<bool> {
154 py.allow_threads(|| delegate_inner!(self, is_fullscreen,))
155 }
156
157 fn is_minimized(&self, py: Python<'_>) -> PyResult<bool> {
158 py.allow_threads(|| delegate_inner!(self, is_minimized,))
159 }
160
161 fn is_maximized(&self, py: Python<'_>) -> PyResult<bool> {
162 py.allow_threads(|| delegate_inner!(self, is_maximized,))
163 }
164
165 fn is_focused(&self, py: Python<'_>) -> PyResult<bool> {
166 py.allow_threads(|| delegate_inner!(self, is_focused,))
167 }
168
169 fn is_decorated(&self, py: Python<'_>) -> PyResult<bool> {
170 py.allow_threads(|| delegate_inner!(self, is_decorated,))
171 }
172
173 fn is_resizable(&self, py: Python<'_>) -> PyResult<bool> {
174 py.allow_threads(|| delegate_inner!(self, is_resizable,))
175 }
176
177 fn is_enabled(&self, py: Python<'_>) -> PyResult<bool> {
178 py.allow_threads(|| delegate_inner!(self, is_enabled,))
179 }
180
181 fn is_maximizable(&self, py: Python<'_>) -> PyResult<bool> {
182 py.allow_threads(|| delegate_inner!(self, is_maximizable,))
183 }
184
185 fn is_minimizable(&self, py: Python<'_>) -> PyResult<bool> {
186 py.allow_threads(|| delegate_inner!(self, is_minimizable,))
187 }
188
189 fn is_closable(&self, py: Python<'_>) -> PyResult<bool> {
190 py.allow_threads(|| delegate_inner!(self, is_closable,))
191 }
192
193 fn is_visible(&self, py: Python<'_>) -> PyResult<bool> {
194 py.allow_threads(|| delegate_inner!(self, is_visible,))
195 }
196
197 fn title(&self, py: Python<'_>) -> PyResult<String> {
198 py.allow_threads(|| delegate_inner!(self, title,))
199 }
200
201 fn theme(&self, py: Python<'_>) -> PyResult<Theme> {
202 py.allow_threads(|| delegate_inner!(self, theme,).map(Into::into))
203 }
204
205 fn center(&self, py: Python<'_>) -> PyResult<()> {
206 py.allow_threads(|| delegate_inner!(self, center,))
207 }
208
209 fn set_resizable(&self, py: Python<'_>, resizable: bool) -> PyResult<()> {
210 py.allow_threads(|| delegate_inner!(self, set_resizable, resizable))
211 }
212
213 fn set_enabled(&self, py: Python<'_>, enabled: bool) -> PyResult<()> {
214 py.allow_threads(|| delegate_inner!(self, set_enabled, enabled))
215 }
216
217 fn set_maximizable(&self, py: Python<'_>, maximizable: bool) -> PyResult<()> {
218 py.allow_threads(|| delegate_inner!(self, set_maximizable, maximizable))
219 }
220
221 fn set_minimizable(&self, py: Python<'_>, minimizable: bool) -> PyResult<()> {
222 py.allow_threads(|| delegate_inner!(self, set_minimizable, minimizable))
223 }
224
225 fn set_closable(&self, py: Python<'_>, closable: bool) -> PyResult<()> {
226 py.allow_threads(|| delegate_inner!(self, set_closable, closable))
227 }
228
229 fn set_title(&self, py: Python<'_>, title: &str) -> PyResult<()> {
230 py.allow_threads(|| delegate_inner!(self, set_title, title))
231 }
232
233 fn maximize(&self, py: Python<'_>) -> PyResult<()> {
234 py.allow_threads(|| delegate_inner!(self, maximize,))
235 }
236
237 fn unmaximize(&self, py: Python<'_>) -> PyResult<()> {
238 py.allow_threads(|| delegate_inner!(self, unmaximize,))
239 }
240
241 fn minimize(&self, py: Python<'_>) -> PyResult<()> {
242 py.allow_threads(|| delegate_inner!(self, minimize,))
243 }
244
245 fn unminimize(&self, py: Python<'_>) -> PyResult<()> {
246 py.allow_threads(|| delegate_inner!(self, unminimize,))
247 }
248
249 fn show(&self, py: Python<'_>) -> PyResult<()> {
250 py.allow_threads(|| delegate_inner!(self, show,))
251 }
252
253 fn hide(&self, py: Python<'_>) -> PyResult<()> {
254 py.allow_threads(|| delegate_inner!(self, hide,))
255 }
256
257 fn close(&self, py: Python<'_>) -> PyResult<()> {
258 py.allow_threads(|| delegate_inner!(self, close,))
259 }
260
261 fn destroy(&self, py: Python<'_>) -> PyResult<()> {
262 py.allow_threads(|| delegate_inner!(self, destroy,))
263 }
264
265 fn set_decorations(&self, py: Python<'_>, decorations: bool) -> PyResult<()> {
266 py.allow_threads(|| delegate_inner!(self, set_decorations, decorations))
267 }
268
269 fn set_shadow(&self, py: Python<'_>, shadow: bool) -> PyResult<()> {
270 py.allow_threads(|| delegate_inner!(self, set_shadow, shadow))
271 }
272
273 fn set_always_on_bottom(&self, py: Python<'_>, always_on_bottom: bool) -> PyResult<()> {
274 py.allow_threads(|| delegate_inner!(self, set_always_on_bottom, always_on_bottom))
275 }
276
277 fn set_always_on_top(&self, py: Python<'_>, always_on_top: bool) -> PyResult<()> {
278 py.allow_threads(|| delegate_inner!(self, set_always_on_top, always_on_top))
279 }
280
281 fn set_visible_on_all_workspaces(
282 &self,
283 py: Python<'_>,
284 visible_on_all_workspaces: bool,
285 ) -> PyResult<()> {
286 py.allow_threads(|| {
287 delegate_inner!(
288 self,
289 set_visible_on_all_workspaces,
290 visible_on_all_workspaces
291 )
292 })
293 }
294
295 fn set_content_protected(&self, py: Python<'_>, protected: bool) -> PyResult<()> {
296 py.allow_threads(|| delegate_inner!(self, set_content_protected, protected))
297 }
298
299 fn set_fullscreen(&self, py: Python<'_>, fullscreen: bool) -> PyResult<()> {
300 py.allow_threads(|| delegate_inner!(self, set_fullscreen, fullscreen))
301 }
302
303 fn set_focus(&self, py: Python<'_>) -> PyResult<()> {
304 py.allow_threads(|| delegate_inner!(self, set_focus,))
305 }
306
307 fn set_icon(&self, py: Python<'_>, icon: Py<Image>) -> PyResult<()> {
308 let icon = icon.get().to_tauri(py);
309 py.allow_threads(|| delegate_inner!(self, set_icon, icon))
310 }
311
312 fn set_skip_taskbar(&self, py: Python<'_>, skip: bool) -> PyResult<()> {
313 py.allow_threads(|| delegate_inner!(self, set_skip_taskbar, skip))
314 }
315
316 fn set_cursor_grab(&self, py: Python<'_>, grab: bool) -> PyResult<()> {
317 py.allow_threads(|| delegate_inner!(self, set_cursor_grab, grab))
318 }
319
320 fn set_cursor_visible(&self, py: Python<'_>, visible: bool) -> PyResult<()> {
321 py.allow_threads(|| delegate_inner!(self, set_cursor_visible, visible))
322 }
323
324 fn set_ignore_cursor_events(&self, py: Python<'_>, ignore: bool) -> PyResult<()> {
325 py.allow_threads(|| delegate_inner!(self, set_ignore_cursor_events, ignore))
326 }
327
328 fn start_dragging(&self, py: Python<'_>) -> PyResult<()> {
329 py.allow_threads(|| delegate_inner!(self, start_dragging,))
330 }
331
332 #[pyo3(signature = (count))]
333 fn set_badge_count(&self, py: Python<'_>, count: Option<i64>) -> PyResult<()> {
334 py.allow_threads(|| delegate_inner!(self, set_badge_count, count))
335 }
336
337 fn set_theme(&self, py: Python<'_>, theme: Option<Theme>) -> PyResult<()> {
338 py.allow_threads(|| delegate_inner!(self, set_theme, theme.map(Into::into)))
339 }
340
341 fn print(&self, py: Python<'_>) -> PyResult<()> {
342 py.allow_threads(|| delegate_inner!(self, print,))
343 }
344
345 fn url(&self, py: Python<'_>) -> PyResult<Url> {
346 let url = py.allow_threads(|| delegate_inner!(self, url,))?;
347 Ok(url.into())
348 }
349
350 fn navigate(&self, py: Python<'_>, url: Url<'_>) -> PyResult<()> {
351 py.allow_threads(|| delegate_inner!(self, navigate, url.into()))
352 }
353
354 fn eval(&self, py: Python<'_>, js: &str) -> PyResult<()> {
355 py.allow_threads(|| delegate_inner!(self, eval, js))
356 }
357
358 fn set_zoom(&self, py: Python<'_>, scale_factor: f64) -> PyResult<()> {
359 py.allow_threads(|| delegate_inner!(self, set_zoom, scale_factor))
360 }
361
362 fn clear_all_browsing_data(&self, py: Python<'_>) -> PyResult<()> {
363 py.allow_threads(|| delegate_inner!(self, clear_all_browsing_data,))
364 }
365
366 fn as_ref_webview(&self) -> Webview {
368 let webview = self.0.inner_ref().as_ref().clone();
369 Webview::new(webview)
370 }
371
372 fn on_webview_event(&self, py: Python<'_>, handler: PyObject) {
375 py.allow_threads(|| {
376 self.0
377 .inner_ref()
378 .as_ref()
379 .on_webview_event(move |webview_event| {
380 Python::with_gil(|py| {
381 let webview_event: WebviewEvent =
382 WebviewEvent::from_tauri(py, webview_event)
383 .expect("Failed to convert `WebviewEvent` to pyobject");
385
386 let handler = handler.bind(py);
387 let result = handler.call1((webview_event,));
388 result.unwrap_unraisable_py_result(py, Some(handler), || {
389 "Python exception occurred in `WebviewWindow::on_webview_event` handler"
390 });
391 })
392 })
393 })
394 }
395}
396
397#[pyclass(frozen)]
399#[non_exhaustive]
400pub struct Webview(pub PyWrapper<PyWrapperT0<TauriWebview>>);
401
402impl Webview {
403 pub(crate) fn new(webview: TauriWebview) -> Self {
404 Self(PyWrapper::new0(webview))
405 }
406}
407
408#[pymethods]
409impl Webview {
410 fn window(&self) -> Window {
411 let window = self.0.inner_ref().window();
412 Window::new(window)
413 }
414}