1#![doc(
11 html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
12 html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
13)]
14
15use self::monitor::MonitorExt;
16use http::Request;
17#[cfg(target_os = "macos")]
18use objc2::ClassType;
19use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
20
21#[cfg(windows)]
22use tauri_runtime::webview::ScrollBarStyle;
23use tauri_runtime::{
24 dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
25 monitor::Monitor,
26 webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler},
27 window::{
28 CursorIcon, DetachedWindow, DetachedWindowWebview, DragDropEvent, PendingWindow, RawWindow,
29 WebviewEvent, WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints,
30 },
31 Cookie, DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon,
32 ProgressBarState, ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs,
33 UserAttentionType, UserEvent, WebviewDispatch, WebviewEventId, WindowDispatch, WindowEventId,
34};
35
36#[cfg(target_vendor = "apple")]
37use objc2::rc::Retained;
38#[cfg(target_os = "macos")]
39use tao::platform::macos::{EventLoopWindowTargetExtMacOS, WindowBuilderExtMacOS};
40#[cfg(target_os = "linux")]
41use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
42#[cfg(windows)]
43use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
44#[cfg(windows)]
45use webview2_com::{ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler};
46#[cfg(windows)]
47use windows::Win32::Foundation::HWND;
48#[cfg(target_os = "ios")]
49use wry::WebViewBuilderExtIos;
50#[cfg(target_os = "macos")]
51use wry::WebViewBuilderExtMacos;
52#[cfg(windows)]
53use wry::WebViewBuilderExtWindows;
54#[cfg(target_vendor = "apple")]
55use wry::{WebViewBuilderExtDarwin, WebViewExtDarwin};
56
57use tao::{
58 dpi::{
59 LogicalPosition as TaoLogicalPosition, LogicalSize as TaoLogicalSize,
60 PhysicalPosition as TaoPhysicalPosition, PhysicalSize as TaoPhysicalSize,
61 Position as TaoPosition, Size as TaoSize,
62 },
63 event::{Event, StartCause, WindowEvent as TaoWindowEvent},
64 event_loop::{
65 ControlFlow, DeviceEventFilter as TaoDeviceEventFilter, EventLoop, EventLoopBuilder,
66 EventLoopProxy as TaoEventLoopProxy, EventLoopWindowTarget,
67 },
68 monitor::MonitorHandle,
69 window::{
70 CursorIcon as TaoCursorIcon, Fullscreen, Icon as TaoWindowIcon,
71 ProgressBarState as TaoProgressBarState, ProgressState as TaoProgressState, Theme as TaoTheme,
72 UserAttentionType as TaoUserAttentionType,
73 },
74};
75#[cfg(desktop)]
76use tauri_utils::config::PreventOverflowConfig;
77#[cfg(target_os = "macos")]
78use tauri_utils::TitleBarStyle;
79use tauri_utils::{
80 config::{Color, WindowConfig},
81 Theme,
82};
83use url::Url;
84#[cfg(windows)]
85use wry::ScrollBarStyle as WryScrollBarStyle;
86use wry::{
87 DragDropEvent as WryDragDropEvent, ProxyConfig, ProxyEndpoint, WebContext as WryWebContext,
88 WebView, WebViewBuilder,
89};
90
91pub use tao;
92pub use tao::window::{Window, WindowBuilder as TaoWindowBuilder, WindowId as TaoWindowId};
93pub use wry;
94pub use wry::webview_version;
95
96#[cfg(windows)]
97use wry::WebViewExtWindows;
98#[cfg(target_os = "android")]
99use wry::{
100 prelude::{dispatch, find_class},
101 WebViewBuilderExtAndroid, WebViewExtAndroid,
102};
103#[cfg(not(any(
104 target_os = "windows",
105 target_os = "macos",
106 target_os = "ios",
107 target_os = "android"
108)))]
109use wry::{WebViewBuilderExtUnix, WebViewExtUnix};
110
111#[cfg(target_os = "ios")]
112pub use tao::platform::ios::WindowExtIOS;
113#[cfg(target_os = "macos")]
114pub use tao::platform::macos::{
115 ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS,
116};
117#[cfg(target_os = "macos")]
118use tauri_runtime::ActivationPolicy;
119
120use std::{
121 cell::RefCell,
122 collections::{
123 hash_map::Entry::{Occupied, Vacant},
124 BTreeMap, HashMap, HashSet,
125 },
126 fmt,
127 ops::Deref,
128 path::PathBuf,
129 rc::Rc,
130 sync::{
131 atomic::{AtomicBool, AtomicU32, Ordering},
132 mpsc::{channel, Sender},
133 Arc, Mutex, Weak,
134 },
135 thread::{current as current_thread, ThreadId},
136};
137
138pub type WebviewId = u32;
139type IpcHandler = dyn Fn(Request<String>) + 'static;
140
141#[cfg(not(debug_assertions))]
142mod dialog;
143mod monitor;
144#[cfg(any(
145 windows,
146 target_os = "linux",
147 target_os = "dragonfly",
148 target_os = "freebsd",
149 target_os = "netbsd",
150 target_os = "openbsd"
151))]
152mod undecorated_resizing;
153mod util;
154mod webview;
155mod window;
156
157pub use webview::Webview;
158use window::WindowExt as _;
159
160#[derive(Debug)]
161pub struct WebContext {
162 pub inner: WryWebContext,
163 pub referenced_by_webviews: HashSet<String>,
164 pub registered_custom_protocols: HashSet<String>,
167}
168
169pub type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
170pub type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
172pub type WindowEventListeners = Arc<Mutex<HashMap<WindowEventId, WindowEventHandler>>>;
173pub type WebviewEventHandler = Box<dyn Fn(&WebviewEvent) + Send>;
174pub type WebviewEventListeners = Arc<Mutex<HashMap<WebviewEventId, WebviewEventHandler>>>;
175
176#[derive(Debug, Clone, Default)]
177pub struct WindowIdStore(Arc<Mutex<HashMap<TaoWindowId, WindowId>>>);
178
179impl WindowIdStore {
180 pub fn insert(&self, w: TaoWindowId, id: WindowId) {
181 self.0.lock().unwrap().insert(w, id);
182 }
183
184 pub fn get(&self, w: &TaoWindowId) -> Option<WindowId> {
185 self.0.lock().unwrap().get(w).copied()
186 }
187}
188
189#[macro_export]
190macro_rules! getter {
191 ($self: ident, $rx: expr, $message: expr) => {{
192 $crate::send_user_message(&$self.context, $message)?;
193 $rx
194 .recv()
195 .map_err(|_| $crate::Error::FailedToReceiveMessage)
196 }};
197}
198
199macro_rules! window_getter {
200 ($self: ident, $message: expr) => {{
201 let (tx, rx) = channel();
202 getter!($self, rx, Message::Window($self.window_id, $message(tx)))
203 }};
204}
205
206macro_rules! event_loop_window_getter {
207 ($self: ident, $message: expr) => {{
208 let (tx, rx) = channel();
209 getter!($self, rx, Message::EventLoopWindowTarget($message(tx)))
210 }};
211}
212
213macro_rules! webview_getter {
214 ($self: ident, $message: expr) => {{
215 let (tx, rx) = channel();
216 getter!(
217 $self,
218 rx,
219 Message::Webview(
220 *$self.window_id.lock().unwrap(),
221 $self.webview_id,
222 $message(tx)
223 )
224 )
225 }};
226}
227
228pub(crate) fn send_user_message<T: UserEvent>(
229 context: &Context<T>,
230 message: Message<T>,
231) -> Result<()> {
232 if current_thread().id() == context.main_thread_id {
233 handle_user_message(
234 &context.main_thread.window_target,
235 message,
236 UserMessageContext {
237 window_id_map: context.window_id_map.clone(),
238 windows: context.main_thread.windows.clone(),
239 },
240 );
241 Ok(())
242 } else {
243 context
244 .proxy
245 .send_event(message)
246 .map_err(|_| Error::FailedToSendMessage)
247 }
248}
249
250#[derive(Clone)]
251pub struct Context<T: UserEvent> {
252 pub window_id_map: WindowIdStore,
253 main_thread_id: ThreadId,
254 pub proxy: TaoEventLoopProxy<Message<T>>,
255 main_thread: DispatcherMainThreadContext<T>,
256 plugins: Arc<Mutex<Vec<Box<dyn Plugin<T> + Send>>>>,
257 next_window_id: Arc<AtomicU32>,
258 next_webview_id: Arc<AtomicU32>,
259 next_window_event_id: Arc<AtomicU32>,
260 next_webview_event_id: Arc<AtomicU32>,
261 webview_runtime_installed: bool,
262}
263
264impl<T: UserEvent> Context<T> {
265 pub fn run_threaded<R, F>(&self, f: F) -> R
266 where
267 F: FnOnce(Option<&DispatcherMainThreadContext<T>>) -> R,
268 {
269 f(if current_thread().id() == self.main_thread_id {
270 Some(&self.main_thread)
271 } else {
272 None
273 })
274 }
275
276 fn next_window_id(&self) -> WindowId {
277 self.next_window_id.fetch_add(1, Ordering::Relaxed).into()
278 }
279
280 fn next_webview_id(&self) -> WebviewId {
281 self.next_webview_id.fetch_add(1, Ordering::Relaxed)
282 }
283
284 fn next_window_event_id(&self) -> u32 {
285 self.next_window_event_id.fetch_add(1, Ordering::Relaxed)
286 }
287
288 fn next_webview_event_id(&self) -> u32 {
289 self.next_webview_event_id.fetch_add(1, Ordering::Relaxed)
290 }
291}
292
293impl<T: UserEvent> Context<T> {
294 fn create_window<F: Fn(RawWindow) + Send + 'static>(
295 &self,
296 pending: PendingWindow<T, Wry<T>>,
297 after_window_creation: Option<F>,
298 ) -> Result<DetachedWindow<T, Wry<T>>> {
299 let label = pending.label.clone();
300 let context = self.clone();
301 let window_id = self.next_window_id();
302 let (webview_id, use_https_scheme) = pending
303 .webview
304 .as_ref()
305 .map(|w| {
306 (
307 Some(context.next_webview_id()),
308 w.webview_attributes.use_https_scheme,
309 )
310 })
311 .unwrap_or((None, false));
312
313 send_user_message(
314 self,
315 Message::CreateWindow(
316 window_id,
317 Box::new(move |event_loop| {
318 create_window(
319 window_id,
320 webview_id.unwrap_or_default(),
321 event_loop,
322 &context,
323 pending,
324 after_window_creation,
325 )
326 }),
327 ),
328 )?;
329
330 let dispatcher = WryWindowDispatcher {
331 window_id,
332 context: self.clone(),
333 };
334
335 let detached_webview = webview_id.map(|id| {
336 let webview = DetachedWebview {
337 label: label.clone(),
338 dispatcher: WryWebviewDispatcher {
339 window_id: Arc::new(Mutex::new(window_id)),
340 webview_id: id,
341 context: self.clone(),
342 },
343 };
344 DetachedWindowWebview {
345 webview,
346 use_https_scheme,
347 }
348 });
349
350 Ok(DetachedWindow {
351 id: window_id,
352 label,
353 dispatcher,
354 webview: detached_webview,
355 })
356 }
357
358 fn create_webview(
359 &self,
360 window_id: WindowId,
361 pending: PendingWebview<T, Wry<T>>,
362 ) -> Result<DetachedWebview<T, Wry<T>>> {
363 let label = pending.label.clone();
364 let context = self.clone();
365
366 let webview_id = self.next_webview_id();
367
368 let window_id_wrapper = Arc::new(Mutex::new(window_id));
369 let window_id_wrapper_ = window_id_wrapper.clone();
370
371 send_user_message(
372 self,
373 Message::CreateWebview(
374 window_id,
375 Box::new(move |window, options| {
376 create_webview(
377 WebviewKind::WindowChild,
378 window,
379 window_id_wrapper_,
380 webview_id,
381 &context,
382 pending,
383 options.focused_webview,
384 )
385 }),
386 ),
387 )?;
388
389 let dispatcher = WryWebviewDispatcher {
390 window_id: window_id_wrapper,
391 webview_id,
392 context: self.clone(),
393 };
394
395 Ok(DetachedWebview { label, dispatcher })
396 }
397}
398
399#[cfg(feature = "tracing")]
400#[derive(Debug, Clone, Default)]
401pub struct ActiveTraceSpanStore(Rc<RefCell<Vec<ActiveTracingSpan>>>);
402
403#[cfg(feature = "tracing")]
404impl ActiveTraceSpanStore {
405 pub fn remove_window_draw(&self) {
406 self
407 .0
408 .borrow_mut()
409 .retain(|t| !matches!(t, ActiveTracingSpan::WindowDraw { id: _, span: _ }));
410 }
411}
412
413#[cfg(feature = "tracing")]
414#[derive(Debug)]
415pub enum ActiveTracingSpan {
416 WindowDraw {
417 id: TaoWindowId,
418 span: tracing::span::EnteredSpan,
419 },
420}
421
422#[derive(Debug)]
423pub struct WindowsStore(pub RefCell<BTreeMap<WindowId, WindowWrapper>>);
424
425#[allow(clippy::non_send_fields_in_send_ty)]
427unsafe impl Send for WindowsStore {}
428
429#[allow(clippy::non_send_fields_in_send_ty)]
431unsafe impl Sync for WindowsStore {}
432
433#[derive(Debug, Clone)]
434pub struct DispatcherMainThreadContext<T: UserEvent> {
435 pub window_target: EventLoopWindowTarget<Message<T>>,
436 pub web_context: WebContextStore,
437 pub windows: Arc<WindowsStore>,
439 #[cfg(feature = "tracing")]
440 pub active_tracing_spans: ActiveTraceSpanStore,
441}
442
443#[allow(clippy::non_send_fields_in_send_ty)]
445unsafe impl<T: UserEvent> Send for DispatcherMainThreadContext<T> {}
446
447#[allow(clippy::non_send_fields_in_send_ty)]
449unsafe impl<T: UserEvent> Sync for DispatcherMainThreadContext<T> {}
450
451impl<T: UserEvent> fmt::Debug for Context<T> {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 f.debug_struct("Context")
454 .field("main_thread_id", &self.main_thread_id)
455 .field("proxy", &self.proxy)
456 .field("main_thread", &self.main_thread)
457 .finish()
458 }
459}
460
461pub struct DeviceEventFilterWrapper(pub TaoDeviceEventFilter);
462
463impl From<DeviceEventFilter> for DeviceEventFilterWrapper {
464 fn from(item: DeviceEventFilter) -> Self {
465 match item {
466 DeviceEventFilter::Always => Self(TaoDeviceEventFilter::Always),
467 DeviceEventFilter::Never => Self(TaoDeviceEventFilter::Never),
468 DeviceEventFilter::Unfocused => Self(TaoDeviceEventFilter::Unfocused),
469 }
470 }
471}
472
473pub struct RectWrapper(pub wry::Rect);
474impl From<tauri_runtime::dpi::Rect> for RectWrapper {
475 fn from(value: tauri_runtime::dpi::Rect) -> Self {
476 RectWrapper(wry::Rect {
477 position: value.position,
478 size: value.size,
479 })
480 }
481}
482
483pub struct TaoIcon(pub TaoWindowIcon);
485
486impl TryFrom<Icon<'_>> for TaoIcon {
487 type Error = Error;
488 fn try_from(icon: Icon<'_>) -> std::result::Result<Self, Self::Error> {
489 TaoWindowIcon::from_rgba(icon.rgba.to_vec(), icon.width, icon.height)
490 .map(Self)
491 .map_err(|e| Error::InvalidIcon(Box::new(e)))
492 }
493}
494
495pub struct WindowEventWrapper(pub Option<WindowEvent>);
496
497impl WindowEventWrapper {
498 fn map_from_tao(
499 event: &TaoWindowEvent<'_>,
500 #[allow(unused_variables)] window: &WindowWrapper,
501 ) -> Self {
502 let event = match event {
503 TaoWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
504 TaoWindowEvent::Moved(position) => {
505 WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
506 }
507 TaoWindowEvent::Destroyed => WindowEvent::Destroyed,
508 TaoWindowEvent::ScaleFactorChanged {
509 scale_factor,
510 new_inner_size,
511 } => WindowEvent::ScaleFactorChanged {
512 scale_factor: *scale_factor,
513 new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
514 },
515 TaoWindowEvent::Focused(focused) => {
516 #[cfg(not(windows))]
517 return Self(Some(WindowEvent::Focused(*focused)));
518 #[cfg(windows)]
522 if window.has_children.load(Ordering::Relaxed) {
523 const FOCUSED_WEBVIEW_MARKER: &str = "__tauriWindow?";
524 let mut focused_webview = window.focused_webview.lock().unwrap();
525 if !*focused
528 && focused_webview
529 .as_deref()
530 .is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER)
531 {
532 return Self(None);
533 }
534
535 *focused_webview = (*focused).then(|| FOCUSED_WEBVIEW_MARKER.to_string());
538
539 return Self(Some(WindowEvent::Focused(*focused)));
540 } else {
541 return Self(None);
543 }
544 }
545 TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
546 _ => return Self(None),
547 };
548 Self(Some(event))
549 }
550
551 fn parse(window: &WindowWrapper, event: &TaoWindowEvent<'_>) -> Self {
552 match event {
553 TaoWindowEvent::Resized(_) => {
556 if let Some(w) = &window.inner {
557 let size = inner_size(
558 w,
559 &window.webviews,
560 window.has_children.load(Ordering::Relaxed),
561 );
562 Self(Some(WindowEvent::Resized(PhysicalSizeWrapper(size).into())))
563 } else {
564 Self(None)
565 }
566 }
567 e => Self::map_from_tao(e, window),
568 }
569 }
570}
571
572pub fn map_theme(theme: &TaoTheme) -> Theme {
573 match theme {
574 TaoTheme::Light => Theme::Light,
575 TaoTheme::Dark => Theme::Dark,
576 _ => Theme::Light,
577 }
578}
579
580#[cfg(target_os = "macos")]
581fn tao_activation_policy(activation_policy: ActivationPolicy) -> TaoActivationPolicy {
582 match activation_policy {
583 ActivationPolicy::Regular => TaoActivationPolicy::Regular,
584 ActivationPolicy::Accessory => TaoActivationPolicy::Accessory,
585 ActivationPolicy::Prohibited => TaoActivationPolicy::Prohibited,
586 _ => unimplemented!(),
587 }
588}
589
590pub struct MonitorHandleWrapper(pub MonitorHandle);
591
592impl From<MonitorHandleWrapper> for Monitor {
593 fn from(monitor: MonitorHandleWrapper) -> Monitor {
594 Self {
595 name: monitor.0.name(),
596 position: PhysicalPositionWrapper(monitor.0.position()).into(),
597 size: PhysicalSizeWrapper(monitor.0.size()).into(),
598 work_area: monitor.0.work_area(),
599 scale_factor: monitor.0.scale_factor(),
600 }
601 }
602}
603
604pub struct PhysicalPositionWrapper<T>(pub TaoPhysicalPosition<T>);
605
606impl<T> From<PhysicalPositionWrapper<T>> for PhysicalPosition<T> {
607 fn from(position: PhysicalPositionWrapper<T>) -> Self {
608 Self {
609 x: position.0.x,
610 y: position.0.y,
611 }
612 }
613}
614
615impl<T> From<PhysicalPosition<T>> for PhysicalPositionWrapper<T> {
616 fn from(position: PhysicalPosition<T>) -> Self {
617 Self(TaoPhysicalPosition {
618 x: position.x,
619 y: position.y,
620 })
621 }
622}
623
624struct LogicalPositionWrapper<T>(TaoLogicalPosition<T>);
625
626impl<T> From<LogicalPosition<T>> for LogicalPositionWrapper<T> {
627 fn from(position: LogicalPosition<T>) -> Self {
628 Self(TaoLogicalPosition {
629 x: position.x,
630 y: position.y,
631 })
632 }
633}
634
635pub struct PhysicalSizeWrapper<T>(pub TaoPhysicalSize<T>);
636
637impl<T> From<PhysicalSizeWrapper<T>> for PhysicalSize<T> {
638 fn from(size: PhysicalSizeWrapper<T>) -> Self {
639 Self {
640 width: size.0.width,
641 height: size.0.height,
642 }
643 }
644}
645
646impl<T> From<PhysicalSize<T>> for PhysicalSizeWrapper<T> {
647 fn from(size: PhysicalSize<T>) -> Self {
648 Self(TaoPhysicalSize {
649 width: size.width,
650 height: size.height,
651 })
652 }
653}
654
655struct LogicalSizeWrapper<T>(TaoLogicalSize<T>);
656
657impl<T> From<LogicalSize<T>> for LogicalSizeWrapper<T> {
658 fn from(size: LogicalSize<T>) -> Self {
659 Self(TaoLogicalSize {
660 width: size.width,
661 height: size.height,
662 })
663 }
664}
665
666pub struct SizeWrapper(pub TaoSize);
667
668impl From<Size> for SizeWrapper {
669 fn from(size: Size) -> Self {
670 match size {
671 Size::Logical(s) => Self(TaoSize::Logical(LogicalSizeWrapper::from(s).0)),
672 Size::Physical(s) => Self(TaoSize::Physical(PhysicalSizeWrapper::from(s).0)),
673 }
674 }
675}
676
677pub struct PositionWrapper(pub TaoPosition);
678
679impl From<Position> for PositionWrapper {
680 fn from(position: Position) -> Self {
681 match position {
682 Position::Logical(s) => Self(TaoPosition::Logical(LogicalPositionWrapper::from(s).0)),
683 Position::Physical(s) => Self(TaoPosition::Physical(PhysicalPositionWrapper::from(s).0)),
684 }
685 }
686}
687
688#[derive(Debug, Clone)]
689pub struct UserAttentionTypeWrapper(pub TaoUserAttentionType);
690
691impl From<UserAttentionType> for UserAttentionTypeWrapper {
692 fn from(request_type: UserAttentionType) -> Self {
693 let o = match request_type {
694 UserAttentionType::Critical => TaoUserAttentionType::Critical,
695 UserAttentionType::Informational => TaoUserAttentionType::Informational,
696 };
697 Self(o)
698 }
699}
700
701#[derive(Debug)]
702pub struct CursorIconWrapper(pub TaoCursorIcon);
703
704impl From<CursorIcon> for CursorIconWrapper {
705 fn from(icon: CursorIcon) -> Self {
706 use CursorIcon::*;
707 let i = match icon {
708 Default => TaoCursorIcon::Default,
709 Crosshair => TaoCursorIcon::Crosshair,
710 Hand => TaoCursorIcon::Hand,
711 Arrow => TaoCursorIcon::Arrow,
712 Move => TaoCursorIcon::Move,
713 Text => TaoCursorIcon::Text,
714 Wait => TaoCursorIcon::Wait,
715 Help => TaoCursorIcon::Help,
716 Progress => TaoCursorIcon::Progress,
717 NotAllowed => TaoCursorIcon::NotAllowed,
718 ContextMenu => TaoCursorIcon::ContextMenu,
719 Cell => TaoCursorIcon::Cell,
720 VerticalText => TaoCursorIcon::VerticalText,
721 Alias => TaoCursorIcon::Alias,
722 Copy => TaoCursorIcon::Copy,
723 NoDrop => TaoCursorIcon::NoDrop,
724 Grab => TaoCursorIcon::Grab,
725 Grabbing => TaoCursorIcon::Grabbing,
726 AllScroll => TaoCursorIcon::AllScroll,
727 ZoomIn => TaoCursorIcon::ZoomIn,
728 ZoomOut => TaoCursorIcon::ZoomOut,
729 EResize => TaoCursorIcon::EResize,
730 NResize => TaoCursorIcon::NResize,
731 NeResize => TaoCursorIcon::NeResize,
732 NwResize => TaoCursorIcon::NwResize,
733 SResize => TaoCursorIcon::SResize,
734 SeResize => TaoCursorIcon::SeResize,
735 SwResize => TaoCursorIcon::SwResize,
736 WResize => TaoCursorIcon::WResize,
737 EwResize => TaoCursorIcon::EwResize,
738 NsResize => TaoCursorIcon::NsResize,
739 NeswResize => TaoCursorIcon::NeswResize,
740 NwseResize => TaoCursorIcon::NwseResize,
741 ColResize => TaoCursorIcon::ColResize,
742 RowResize => TaoCursorIcon::RowResize,
743 _ => TaoCursorIcon::Default,
744 };
745 Self(i)
746 }
747}
748
749pub struct ProgressStateWrapper(pub TaoProgressState);
750
751impl From<ProgressBarStatus> for ProgressStateWrapper {
752 fn from(status: ProgressBarStatus) -> Self {
753 let state = match status {
754 ProgressBarStatus::None => TaoProgressState::None,
755 ProgressBarStatus::Normal => TaoProgressState::Normal,
756 ProgressBarStatus::Indeterminate => TaoProgressState::Indeterminate,
757 ProgressBarStatus::Paused => TaoProgressState::Paused,
758 ProgressBarStatus::Error => TaoProgressState::Error,
759 };
760 Self(state)
761 }
762}
763
764pub struct ProgressBarStateWrapper(pub TaoProgressBarState);
765
766impl From<ProgressBarState> for ProgressBarStateWrapper {
767 fn from(progress_state: ProgressBarState) -> Self {
768 Self(TaoProgressBarState {
769 progress: progress_state.progress,
770 state: progress_state
771 .status
772 .map(|state| ProgressStateWrapper::from(state).0),
773 desktop_filename: progress_state.desktop_filename,
774 })
775 }
776}
777
778#[derive(Clone, Default)]
779pub struct WindowBuilderWrapper {
780 inner: TaoWindowBuilder,
781 center: bool,
782 prevent_overflow: Option<Size>,
783 #[cfg(target_os = "macos")]
784 tabbing_identifier: Option<String>,
785}
786
787impl std::fmt::Debug for WindowBuilderWrapper {
788 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
789 let mut s = f.debug_struct("WindowBuilderWrapper");
790 s.field("inner", &self.inner)
791 .field("center", &self.center)
792 .field("prevent_overflow", &self.prevent_overflow);
793 #[cfg(target_os = "macos")]
794 {
795 s.field("tabbing_identifier", &self.tabbing_identifier);
796 }
797 s.finish()
798 }
799}
800
801#[allow(clippy::non_send_fields_in_send_ty)]
803unsafe impl Send for WindowBuilderWrapper {}
804
805impl WindowBuilderBase for WindowBuilderWrapper {}
806impl WindowBuilder for WindowBuilderWrapper {
807 fn new() -> Self {
808 #[allow(unused_mut)]
809 let mut builder = Self::default().focused(true);
810
811 #[cfg(target_os = "macos")]
812 {
813 builder = builder.title_bar_style(TitleBarStyle::Visible);
819 }
820
821 builder = builder.title("Tauri App");
822
823 #[cfg(windows)]
824 {
825 builder = builder.window_classname("Tauri Window");
826 }
827
828 builder
829 }
830
831 fn with_config(config: &WindowConfig) -> Self {
832 let mut window = WindowBuilderWrapper::new();
833
834 #[cfg(target_os = "macos")]
835 {
836 window = window
837 .hidden_title(config.hidden_title)
838 .title_bar_style(config.title_bar_style);
839 if let Some(identifier) = &config.tabbing_identifier {
840 window = window.tabbing_identifier(identifier);
841 }
842 if let Some(position) = &config.traffic_light_position {
843 window = window.traffic_light_position(tauri_runtime::dpi::LogicalPosition::new(
844 position.x, position.y,
845 ));
846 }
847 }
848
849 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
850 {
851 window = window.transparent(config.transparent);
852 }
853 #[cfg(all(
854 target_os = "macos",
855 not(feature = "macos-private-api"),
856 debug_assertions
857 ))]
858 if config.transparent {
859 eprintln!(
860 "The window is set to be transparent but the `macos-private-api` is not enabled.
861 This can be enabled via the `tauri.macOSPrivateApi` configuration property <https://v2.tauri.app/reference/config/#macosprivateapi>
862 ");
863 }
864
865 #[cfg(target_os = "linux")]
866 {
867 window.inner = window.inner.with_cursor_moved_event(false);
869 }
870
871 #[cfg(desktop)]
872 {
873 window = window
874 .title(config.title.to_string())
875 .inner_size(config.width, config.height)
876 .focused(config.focus)
877 .focusable(config.focusable)
878 .visible(config.visible)
879 .resizable(config.resizable)
880 .fullscreen(config.fullscreen)
881 .decorations(config.decorations)
882 .maximized(config.maximized)
883 .always_on_bottom(config.always_on_bottom)
884 .always_on_top(config.always_on_top)
885 .visible_on_all_workspaces(config.visible_on_all_workspaces)
886 .content_protected(config.content_protected)
887 .skip_taskbar(config.skip_taskbar)
888 .theme(config.theme)
889 .closable(config.closable)
890 .maximizable(config.maximizable)
891 .minimizable(config.minimizable)
892 .shadow(config.shadow);
893
894 let mut constraints = WindowSizeConstraints::default();
895
896 if let Some(min_width) = config.min_width {
897 constraints.min_width = Some(tao::dpi::LogicalUnit::new(min_width).into());
898 }
899 if let Some(min_height) = config.min_height {
900 constraints.min_height = Some(tao::dpi::LogicalUnit::new(min_height).into());
901 }
902 if let Some(max_width) = config.max_width {
903 constraints.max_width = Some(tao::dpi::LogicalUnit::new(max_width).into());
904 }
905 if let Some(max_height) = config.max_height {
906 constraints.max_height = Some(tao::dpi::LogicalUnit::new(max_height).into());
907 }
908 if let Some(color) = config.background_color {
909 window = window.background_color(color);
910 }
911 window = window.inner_size_constraints(constraints);
912
913 if let (Some(x), Some(y)) = (config.x, config.y) {
914 window = window.position(x, y);
915 }
916
917 if config.center {
918 window = window.center();
919 }
920
921 if let Some(window_classname) = &config.window_classname {
922 window = window.window_classname(window_classname);
923 }
924
925 if let Some(prevent_overflow) = &config.prevent_overflow {
926 window = match prevent_overflow {
927 PreventOverflowConfig::Enable(true) => window.prevent_overflow(),
928 PreventOverflowConfig::Margin(margin) => window
929 .prevent_overflow_with_margin(TaoPhysicalSize::new(margin.width, margin.height).into()),
930 _ => window,
931 };
932 }
933 }
934
935 window
936 }
937
938 fn center(mut self) -> Self {
939 self.center = true;
940 self
941 }
942
943 fn position(mut self, x: f64, y: f64) -> Self {
944 self.inner = self.inner.with_position(TaoLogicalPosition::new(x, y));
945 self
946 }
947
948 fn inner_size(mut self, width: f64, height: f64) -> Self {
949 self.inner = self
950 .inner
951 .with_inner_size(TaoLogicalSize::new(width, height));
952 self
953 }
954
955 fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
956 self.inner = self
957 .inner
958 .with_min_inner_size(TaoLogicalSize::new(min_width, min_height));
959 self
960 }
961
962 fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
963 self.inner = self
964 .inner
965 .with_max_inner_size(TaoLogicalSize::new(max_width, max_height));
966 self
967 }
968
969 fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
970 self.inner.window.inner_size_constraints = tao::window::WindowSizeConstraints {
971 min_width: constraints.min_width,
972 min_height: constraints.min_height,
973 max_width: constraints.max_width,
974 max_height: constraints.max_height,
975 };
976 self
977 }
978
979 fn prevent_overflow(mut self) -> Self {
985 self
986 .prevent_overflow
987 .replace(PhysicalSize::new(0, 0).into());
988 self
989 }
990
991 fn prevent_overflow_with_margin(mut self, margin: Size) -> Self {
998 self.prevent_overflow.replace(margin);
999 self
1000 }
1001
1002 fn resizable(mut self, resizable: bool) -> Self {
1003 self.inner = self.inner.with_resizable(resizable);
1004 self
1005 }
1006
1007 fn maximizable(mut self, maximizable: bool) -> Self {
1008 self.inner = self.inner.with_maximizable(maximizable);
1009 self
1010 }
1011
1012 fn minimizable(mut self, minimizable: bool) -> Self {
1013 self.inner = self.inner.with_minimizable(minimizable);
1014 self
1015 }
1016
1017 fn closable(mut self, closable: bool) -> Self {
1018 self.inner = self.inner.with_closable(closable);
1019 self
1020 }
1021
1022 fn title<S: Into<String>>(mut self, title: S) -> Self {
1023 self.inner = self.inner.with_title(title.into());
1024 self
1025 }
1026
1027 fn fullscreen(mut self, fullscreen: bool) -> Self {
1028 self.inner = if fullscreen {
1029 self
1030 .inner
1031 .with_fullscreen(Some(Fullscreen::Borderless(None)))
1032 } else {
1033 self.inner.with_fullscreen(None)
1034 };
1035 self
1036 }
1037
1038 fn focused(mut self, focused: bool) -> Self {
1039 self.inner = self.inner.with_focused(focused);
1040 self
1041 }
1042
1043 fn focusable(mut self, focusable: bool) -> Self {
1044 self.inner = self.inner.with_focusable(focusable);
1045 self
1046 }
1047
1048 fn maximized(mut self, maximized: bool) -> Self {
1049 self.inner = self.inner.with_maximized(maximized);
1050 self
1051 }
1052
1053 fn visible(mut self, visible: bool) -> Self {
1054 self.inner = self.inner.with_visible(visible);
1055 self
1056 }
1057
1058 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
1059 fn transparent(mut self, transparent: bool) -> Self {
1060 self.inner = self.inner.with_transparent(transparent);
1061 self
1062 }
1063
1064 fn decorations(mut self, decorations: bool) -> Self {
1065 self.inner = self.inner.with_decorations(decorations);
1066 self
1067 }
1068
1069 fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
1070 self.inner = self.inner.with_always_on_bottom(always_on_bottom);
1071 self
1072 }
1073
1074 fn always_on_top(mut self, always_on_top: bool) -> Self {
1075 self.inner = self.inner.with_always_on_top(always_on_top);
1076 self
1077 }
1078
1079 fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
1080 self.inner = self
1081 .inner
1082 .with_visible_on_all_workspaces(visible_on_all_workspaces);
1083 self
1084 }
1085
1086 fn content_protected(mut self, protected: bool) -> Self {
1087 self.inner = self.inner.with_content_protection(protected);
1088 self
1089 }
1090
1091 fn shadow(#[allow(unused_mut)] mut self, _enable: bool) -> Self {
1092 #[cfg(windows)]
1093 {
1094 self.inner = self.inner.with_undecorated_shadow(_enable);
1095 }
1096 #[cfg(target_os = "macos")]
1097 {
1098 self.inner = self.inner.with_has_shadow(_enable);
1099 }
1100 self
1101 }
1102
1103 #[cfg(windows)]
1104 fn owner(mut self, owner: HWND) -> Self {
1105 self.inner = self.inner.with_owner_window(owner.0 as _);
1106 self
1107 }
1108
1109 #[cfg(windows)]
1110 fn parent(mut self, parent: HWND) -> Self {
1111 self.inner = self.inner.with_parent_window(parent.0 as _);
1112 self
1113 }
1114
1115 #[cfg(target_os = "macos")]
1116 fn parent(mut self, parent: *mut std::ffi::c_void) -> Self {
1117 self.inner = self.inner.with_parent_window(parent);
1118 self
1119 }
1120
1121 #[cfg(any(
1122 target_os = "linux",
1123 target_os = "dragonfly",
1124 target_os = "freebsd",
1125 target_os = "netbsd",
1126 target_os = "openbsd"
1127 ))]
1128 fn transient_for(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
1129 self.inner = self.inner.with_transient_for(parent);
1130 self
1131 }
1132
1133 #[cfg(windows)]
1134 fn drag_and_drop(mut self, enabled: bool) -> Self {
1135 self.inner = self.inner.with_drag_and_drop(enabled);
1136 self
1137 }
1138
1139 #[cfg(target_os = "macos")]
1140 fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
1141 match style {
1142 TitleBarStyle::Visible => {
1143 self.inner = self.inner.with_titlebar_transparent(false);
1144 self.inner = self.inner.with_fullsize_content_view(true);
1146 }
1147 TitleBarStyle::Transparent => {
1148 self.inner = self.inner.with_titlebar_transparent(true);
1149 self.inner = self.inner.with_fullsize_content_view(false);
1150 }
1151 TitleBarStyle::Overlay => {
1152 self.inner = self.inner.with_titlebar_transparent(true);
1153 self.inner = self.inner.with_fullsize_content_view(true);
1154 }
1155 unknown => {
1156 #[cfg(feature = "tracing")]
1157 tracing::warn!("unknown title bar style applied: {unknown}");
1158
1159 #[cfg(not(feature = "tracing"))]
1160 eprintln!("unknown title bar style applied: {unknown}");
1161 }
1162 }
1163 self
1164 }
1165
1166 #[cfg(target_os = "macos")]
1167 fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
1168 self.inner = self.inner.with_traffic_light_inset(position.into());
1169 self
1170 }
1171
1172 #[cfg(target_os = "macos")]
1173 fn hidden_title(mut self, hidden: bool) -> Self {
1174 self.inner = self.inner.with_title_hidden(hidden);
1175 self
1176 }
1177
1178 #[cfg(target_os = "macos")]
1179 fn tabbing_identifier(mut self, identifier: &str) -> Self {
1180 self.inner = self.inner.with_tabbing_identifier(identifier);
1181 self.tabbing_identifier.replace(identifier.into());
1182 self
1183 }
1184
1185 fn icon(mut self, icon: Icon) -> Result<Self> {
1186 self.inner = self
1187 .inner
1188 .with_window_icon(Some(TaoIcon::try_from(icon)?.0));
1189 Ok(self)
1190 }
1191
1192 fn background_color(mut self, color: Color) -> Self {
1193 self.inner = self.inner.with_background_color(color.into());
1194 self
1195 }
1196
1197 #[cfg(any(windows, target_os = "linux"))]
1198 fn skip_taskbar(mut self, skip: bool) -> Self {
1199 self.inner = self.inner.with_skip_taskbar(skip);
1200 self
1201 }
1202
1203 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
1204 fn skip_taskbar(self, _skip: bool) -> Self {
1205 self
1206 }
1207
1208 fn theme(mut self, theme: Option<Theme>) -> Self {
1209 self.inner = self.inner.with_theme(if let Some(t) = theme {
1210 match t {
1211 Theme::Dark => Some(TaoTheme::Dark),
1212 _ => Some(TaoTheme::Light),
1213 }
1214 } else {
1215 None
1216 });
1217
1218 self
1219 }
1220
1221 fn has_icon(&self) -> bool {
1222 self.inner.window.window_icon.is_some()
1223 }
1224
1225 fn get_theme(&self) -> Option<Theme> {
1226 self.inner.window.preferred_theme.map(|theme| match theme {
1227 TaoTheme::Dark => Theme::Dark,
1228 _ => Theme::Light,
1229 })
1230 }
1231
1232 #[cfg(windows)]
1233 fn window_classname<S: Into<String>>(mut self, window_classname: S) -> Self {
1234 self.inner = self.inner.with_window_classname(window_classname);
1235 self
1236 }
1237 #[cfg(not(windows))]
1238 fn window_classname<S: Into<String>>(self, _window_classname: S) -> Self {
1239 self
1240 }
1241}
1242
1243#[cfg(any(
1244 target_os = "linux",
1245 target_os = "dragonfly",
1246 target_os = "freebsd",
1247 target_os = "netbsd",
1248 target_os = "openbsd"
1249))]
1250pub struct GtkWindow(pub gtk::ApplicationWindow);
1251#[cfg(any(
1252 target_os = "linux",
1253 target_os = "dragonfly",
1254 target_os = "freebsd",
1255 target_os = "netbsd",
1256 target_os = "openbsd"
1257))]
1258#[allow(clippy::non_send_fields_in_send_ty)]
1259unsafe impl Send for GtkWindow {}
1260
1261#[cfg(any(
1262 target_os = "linux",
1263 target_os = "dragonfly",
1264 target_os = "freebsd",
1265 target_os = "netbsd",
1266 target_os = "openbsd"
1267))]
1268pub struct GtkBox(pub gtk::Box);
1269#[cfg(any(
1270 target_os = "linux",
1271 target_os = "dragonfly",
1272 target_os = "freebsd",
1273 target_os = "netbsd",
1274 target_os = "openbsd"
1275))]
1276#[allow(clippy::non_send_fields_in_send_ty)]
1277unsafe impl Send for GtkBox {}
1278
1279pub struct SendRawWindowHandle(pub raw_window_handle::RawWindowHandle);
1280unsafe impl Send for SendRawWindowHandle {}
1281
1282pub enum ApplicationMessage {
1283 #[cfg(target_os = "macos")]
1284 Show,
1285 #[cfg(target_os = "macos")]
1286 Hide,
1287 #[cfg(any(target_os = "macos", target_os = "ios"))]
1288 FetchDataStoreIdentifiers(Box<dyn FnOnce(Vec<[u8; 16]>) + Send + 'static>),
1289 #[cfg(any(target_os = "macos", target_os = "ios"))]
1290 RemoveDataStore([u8; 16], Box<dyn FnOnce(Result<()>) + Send + 'static>),
1291}
1292
1293pub enum WindowMessage {
1294 AddEventListener(WindowEventId, Box<dyn Fn(&WindowEvent) + Send>),
1295 ScaleFactor(Sender<f64>),
1297 InnerPosition(Sender<Result<PhysicalPosition<i32>>>),
1298 OuterPosition(Sender<Result<PhysicalPosition<i32>>>),
1299 InnerSize(Sender<PhysicalSize<u32>>),
1300 OuterSize(Sender<PhysicalSize<u32>>),
1301 IsFullscreen(Sender<bool>),
1302 IsMinimized(Sender<bool>),
1303 IsMaximized(Sender<bool>),
1304 IsFocused(Sender<bool>),
1305 IsDecorated(Sender<bool>),
1306 IsResizable(Sender<bool>),
1307 IsMaximizable(Sender<bool>),
1308 IsMinimizable(Sender<bool>),
1309 IsClosable(Sender<bool>),
1310 IsVisible(Sender<bool>),
1311 Title(Sender<String>),
1312 CurrentMonitor(Sender<Option<MonitorHandle>>),
1313 PrimaryMonitor(Sender<Option<MonitorHandle>>),
1314 MonitorFromPoint(Sender<Option<MonitorHandle>>, (f64, f64)),
1315 AvailableMonitors(Sender<Vec<MonitorHandle>>),
1316 #[cfg(any(
1317 target_os = "linux",
1318 target_os = "dragonfly",
1319 target_os = "freebsd",
1320 target_os = "netbsd",
1321 target_os = "openbsd"
1322 ))]
1323 GtkWindow(Sender<GtkWindow>),
1324 #[cfg(any(
1325 target_os = "linux",
1326 target_os = "dragonfly",
1327 target_os = "freebsd",
1328 target_os = "netbsd",
1329 target_os = "openbsd"
1330 ))]
1331 GtkBox(Sender<GtkBox>),
1332 RawWindowHandle(Sender<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>>),
1333 Theme(Sender<Theme>),
1334 IsEnabled(Sender<bool>),
1335 IsAlwaysOnTop(Sender<bool>),
1336 Center,
1338 RequestUserAttention(Option<UserAttentionTypeWrapper>),
1339 SetEnabled(bool),
1340 SetResizable(bool),
1341 SetMaximizable(bool),
1342 SetMinimizable(bool),
1343 SetClosable(bool),
1344 SetTitle(String),
1345 Maximize,
1346 Unmaximize,
1347 Minimize,
1348 Unminimize,
1349 Show,
1350 Hide,
1351 Close,
1352 Destroy,
1353 SetDecorations(bool),
1354 SetShadow(bool),
1355 SetAlwaysOnBottom(bool),
1356 SetAlwaysOnTop(bool),
1357 SetVisibleOnAllWorkspaces(bool),
1358 SetContentProtected(bool),
1359 SetSize(Size),
1360 SetMinSize(Option<Size>),
1361 SetMaxSize(Option<Size>),
1362 SetSizeConstraints(WindowSizeConstraints),
1363 SetPosition(Position),
1364 SetFullscreen(bool),
1365 #[cfg(target_os = "macos")]
1366 SetSimpleFullscreen(bool),
1367 SetFocus,
1368 SetFocusable(bool),
1369 SetIcon(TaoWindowIcon),
1370 SetSkipTaskbar(bool),
1371 SetCursorGrab(bool),
1372 SetCursorVisible(bool),
1373 SetCursorIcon(CursorIcon),
1374 SetCursorPosition(Position),
1375 SetIgnoreCursorEvents(bool),
1376 SetBadgeCount(Option<i64>, Option<String>),
1377 SetBadgeLabel(Option<String>),
1378 SetOverlayIcon(Option<TaoIcon>),
1379 SetProgressBar(ProgressBarState),
1380 SetTitleBarStyle(tauri_utils::TitleBarStyle),
1381 SetTrafficLightPosition(Position),
1382 SetTheme(Option<Theme>),
1383 SetBackgroundColor(Option<Color>),
1384 DragWindow,
1385 ResizeDragWindow(tauri_runtime::ResizeDirection),
1386 RequestRedraw,
1387}
1388
1389#[derive(Debug, Clone)]
1390pub enum SynthesizedWindowEvent {
1391 Focused(bool),
1392 DragDrop(DragDropEvent),
1393}
1394
1395impl From<SynthesizedWindowEvent> for WindowEventWrapper {
1396 fn from(event: SynthesizedWindowEvent) -> Self {
1397 let event = match event {
1398 SynthesizedWindowEvent::Focused(focused) => WindowEvent::Focused(focused),
1399 SynthesizedWindowEvent::DragDrop(event) => WindowEvent::DragDrop(event),
1400 };
1401 Self(Some(event))
1402 }
1403}
1404
1405pub enum WebviewMessage {
1406 AddEventListener(WebviewEventId, Box<dyn Fn(&WebviewEvent) + Send>),
1407 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1408 EvaluateScript(String),
1409 #[cfg(all(feature = "tracing", not(target_os = "android")))]
1410 EvaluateScript(String, Sender<()>, tracing::Span),
1411 CookiesForUrl(Url, Sender<Result<Vec<tauri_runtime::Cookie<'static>>>>),
1412 Cookies(Sender<Result<Vec<tauri_runtime::Cookie<'static>>>>),
1413 SetCookie(tauri_runtime::Cookie<'static>),
1414 DeleteCookie(tauri_runtime::Cookie<'static>),
1415 WebviewEvent(WebviewEvent),
1416 SynthesizedWindowEvent(SynthesizedWindowEvent),
1417 Navigate(Url),
1418 Reload,
1419 Print,
1420 Close,
1421 Show,
1422 Hide,
1423 SetPosition(Position),
1424 SetSize(Size),
1425 SetBounds(tauri_runtime::dpi::Rect),
1426 SetFocus,
1427 Reparent(WindowId, Sender<Result<()>>),
1428 SetAutoResize(bool),
1429 SetZoom(f64),
1430 SetBackgroundColor(Option<Color>),
1431 ClearAllBrowsingData,
1432 Url(Sender<Result<String>>),
1434 Bounds(Sender<Result<tauri_runtime::dpi::Rect>>),
1435 Position(Sender<Result<PhysicalPosition<i32>>>),
1436 Size(Sender<Result<PhysicalSize<u32>>>),
1437 WithWebview(Box<dyn FnOnce(Webview) + Send>),
1438 #[cfg(any(debug_assertions, feature = "devtools"))]
1440 OpenDevTools,
1441 #[cfg(any(debug_assertions, feature = "devtools"))]
1442 CloseDevTools,
1443 #[cfg(any(debug_assertions, feature = "devtools"))]
1444 IsDevToolsOpen(Sender<bool>),
1445}
1446
1447pub enum EventLoopWindowTargetMessage {
1448 CursorPosition(Sender<Result<PhysicalPosition<f64>>>),
1449 SetTheme(Option<Theme>),
1450 SetDeviceEventFilter(DeviceEventFilter),
1451}
1452
1453pub type CreateWindowClosure<T> =
1454 Box<dyn FnOnce(&EventLoopWindowTarget<Message<T>>) -> Result<WindowWrapper> + Send>;
1455
1456pub type CreateWebviewClosure =
1457 Box<dyn FnOnce(&Window, CreateWebviewOptions) -> Result<WebviewWrapper> + Send>;
1458
1459pub struct CreateWebviewOptions {
1460 pub focused_webview: Arc<Mutex<Option<String>>>,
1461}
1462
1463pub enum Message<T: 'static> {
1464 Task(Box<dyn FnOnce() + Send>),
1465 #[cfg(target_os = "macos")]
1466 SetActivationPolicy(ActivationPolicy),
1467 #[cfg(target_os = "macos")]
1468 SetDockVisibility(bool),
1469 RequestExit(i32),
1470 Application(ApplicationMessage),
1471 Window(WindowId, WindowMessage),
1472 Webview(WindowId, WebviewId, WebviewMessage),
1473 EventLoopWindowTarget(EventLoopWindowTargetMessage),
1474 CreateWebview(WindowId, CreateWebviewClosure),
1475 CreateWindow(WindowId, CreateWindowClosure<T>),
1476 CreateRawWindow(
1477 WindowId,
1478 Box<dyn FnOnce() -> (String, TaoWindowBuilder) + Send>,
1479 Sender<Result<Weak<Window>>>,
1480 ),
1481 UserEvent(T),
1482}
1483
1484impl<T: UserEvent> Clone for Message<T> {
1485 fn clone(&self) -> Self {
1486 match self {
1487 Self::UserEvent(t) => Self::UserEvent(t.clone()),
1488 _ => unimplemented!(),
1489 }
1490 }
1491}
1492
1493#[derive(Debug, Clone)]
1495pub struct WryWebviewDispatcher<T: UserEvent> {
1496 window_id: Arc<Mutex<WindowId>>,
1497 webview_id: WebviewId,
1498 context: Context<T>,
1499}
1500
1501impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
1502 type Runtime = Wry<T>;
1503
1504 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1505 send_user_message(&self.context, Message::Task(Box::new(f)))
1506 }
1507
1508 fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1509 let id = self.context.next_webview_event_id();
1510 let _ = self.context.proxy.send_event(Message::Webview(
1511 *self.window_id.lock().unwrap(),
1512 self.webview_id,
1513 WebviewMessage::AddEventListener(id, Box::new(f)),
1514 ));
1515 id
1516 }
1517
1518 fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
1519 send_user_message(
1520 &self.context,
1521 Message::Webview(
1522 *self.window_id.lock().unwrap(),
1523 self.webview_id,
1524 WebviewMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))),
1525 ),
1526 )
1527 }
1528
1529 #[cfg(any(debug_assertions, feature = "devtools"))]
1530 fn open_devtools(&self) {
1531 let _ = send_user_message(
1532 &self.context,
1533 Message::Webview(
1534 *self.window_id.lock().unwrap(),
1535 self.webview_id,
1536 WebviewMessage::OpenDevTools,
1537 ),
1538 );
1539 }
1540
1541 #[cfg(any(debug_assertions, feature = "devtools"))]
1542 fn close_devtools(&self) {
1543 let _ = send_user_message(
1544 &self.context,
1545 Message::Webview(
1546 *self.window_id.lock().unwrap(),
1547 self.webview_id,
1548 WebviewMessage::CloseDevTools,
1549 ),
1550 );
1551 }
1552
1553 #[cfg(any(debug_assertions, feature = "devtools"))]
1555 fn is_devtools_open(&self) -> Result<bool> {
1556 webview_getter!(self, WebviewMessage::IsDevToolsOpen)
1557 }
1558
1559 fn url(&self) -> Result<String> {
1562 webview_getter!(self, WebviewMessage::Url)?
1563 }
1564
1565 fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
1566 webview_getter!(self, WebviewMessage::Bounds)?
1567 }
1568
1569 fn position(&self) -> Result<PhysicalPosition<i32>> {
1570 webview_getter!(self, WebviewMessage::Position)?
1571 }
1572
1573 fn size(&self) -> Result<PhysicalSize<u32>> {
1574 webview_getter!(self, WebviewMessage::Size)?
1575 }
1576
1577 fn navigate(&self, url: Url) -> Result<()> {
1580 send_user_message(
1581 &self.context,
1582 Message::Webview(
1583 *self.window_id.lock().unwrap(),
1584 self.webview_id,
1585 WebviewMessage::Navigate(url),
1586 ),
1587 )
1588 }
1589
1590 fn reload(&self) -> Result<()> {
1591 send_user_message(
1592 &self.context,
1593 Message::Webview(
1594 *self.window_id.lock().unwrap(),
1595 self.webview_id,
1596 WebviewMessage::Reload,
1597 ),
1598 )
1599 }
1600
1601 fn print(&self) -> Result<()> {
1602 send_user_message(
1603 &self.context,
1604 Message::Webview(
1605 *self.window_id.lock().unwrap(),
1606 self.webview_id,
1607 WebviewMessage::Print,
1608 ),
1609 )
1610 }
1611
1612 fn close(&self) -> Result<()> {
1613 send_user_message(
1614 &self.context,
1615 Message::Webview(
1616 *self.window_id.lock().unwrap(),
1617 self.webview_id,
1618 WebviewMessage::Close,
1619 ),
1620 )
1621 }
1622
1623 fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
1624 send_user_message(
1625 &self.context,
1626 Message::Webview(
1627 *self.window_id.lock().unwrap(),
1628 self.webview_id,
1629 WebviewMessage::SetBounds(bounds),
1630 ),
1631 )
1632 }
1633
1634 fn set_size(&self, size: Size) -> Result<()> {
1635 send_user_message(
1636 &self.context,
1637 Message::Webview(
1638 *self.window_id.lock().unwrap(),
1639 self.webview_id,
1640 WebviewMessage::SetSize(size),
1641 ),
1642 )
1643 }
1644
1645 fn set_position(&self, position: Position) -> Result<()> {
1646 send_user_message(
1647 &self.context,
1648 Message::Webview(
1649 *self.window_id.lock().unwrap(),
1650 self.webview_id,
1651 WebviewMessage::SetPosition(position),
1652 ),
1653 )
1654 }
1655
1656 fn set_focus(&self) -> Result<()> {
1657 send_user_message(
1658 &self.context,
1659 Message::Webview(
1660 *self.window_id.lock().unwrap(),
1661 self.webview_id,
1662 WebviewMessage::SetFocus,
1663 ),
1664 )
1665 }
1666
1667 fn reparent(&self, window_id: WindowId) -> Result<()> {
1668 let mut current_window_id = self.window_id.lock().unwrap();
1669 let (tx, rx) = channel();
1670 send_user_message(
1671 &self.context,
1672 Message::Webview(
1673 *current_window_id,
1674 self.webview_id,
1675 WebviewMessage::Reparent(window_id, tx),
1676 ),
1677 )?;
1678
1679 rx.recv().unwrap()?;
1680
1681 *current_window_id = window_id;
1682 Ok(())
1683 }
1684
1685 fn cookies_for_url(&self, url: Url) -> Result<Vec<Cookie<'static>>> {
1686 let current_window_id = self.window_id.lock().unwrap();
1687 let (tx, rx) = channel();
1688 send_user_message(
1689 &self.context,
1690 Message::Webview(
1691 *current_window_id,
1692 self.webview_id,
1693 WebviewMessage::CookiesForUrl(url, tx),
1694 ),
1695 )?;
1696
1697 rx.recv().unwrap()
1698 }
1699
1700 fn cookies(&self) -> Result<Vec<Cookie<'static>>> {
1701 webview_getter!(self, WebviewMessage::Cookies)?
1702 }
1703
1704 fn set_cookie(&self, cookie: Cookie<'_>) -> Result<()> {
1705 send_user_message(
1706 &self.context,
1707 Message::Webview(
1708 *self.window_id.lock().unwrap(),
1709 self.webview_id,
1710 WebviewMessage::SetCookie(cookie.into_owned()),
1711 ),
1712 )?;
1713 Ok(())
1714 }
1715
1716 fn delete_cookie(&self, cookie: Cookie<'_>) -> Result<()> {
1717 send_user_message(
1718 &self.context,
1719 Message::Webview(
1720 *self.window_id.lock().unwrap(),
1721 self.webview_id,
1722 WebviewMessage::DeleteCookie(cookie.into_owned()),
1723 ),
1724 )?;
1725 Ok(())
1726 }
1727
1728 fn set_auto_resize(&self, auto_resize: bool) -> Result<()> {
1729 send_user_message(
1730 &self.context,
1731 Message::Webview(
1732 *self.window_id.lock().unwrap(),
1733 self.webview_id,
1734 WebviewMessage::SetAutoResize(auto_resize),
1735 ),
1736 )
1737 }
1738
1739 #[cfg(all(feature = "tracing", not(target_os = "android")))]
1740 fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
1741 let (tx, rx) = channel();
1743 getter!(
1744 self,
1745 rx,
1746 Message::Webview(
1747 *self.window_id.lock().unwrap(),
1748 self.webview_id,
1749 WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()),
1750 )
1751 )
1752 }
1753
1754 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1755 fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
1756 send_user_message(
1757 &self.context,
1758 Message::Webview(
1759 *self.window_id.lock().unwrap(),
1760 self.webview_id,
1761 WebviewMessage::EvaluateScript(script.into()),
1762 ),
1763 )
1764 }
1765
1766 fn set_zoom(&self, scale_factor: f64) -> Result<()> {
1767 send_user_message(
1768 &self.context,
1769 Message::Webview(
1770 *self.window_id.lock().unwrap(),
1771 self.webview_id,
1772 WebviewMessage::SetZoom(scale_factor),
1773 ),
1774 )
1775 }
1776
1777 fn clear_all_browsing_data(&self) -> Result<()> {
1778 send_user_message(
1779 &self.context,
1780 Message::Webview(
1781 *self.window_id.lock().unwrap(),
1782 self.webview_id,
1783 WebviewMessage::ClearAllBrowsingData,
1784 ),
1785 )
1786 }
1787
1788 fn hide(&self) -> Result<()> {
1789 send_user_message(
1790 &self.context,
1791 Message::Webview(
1792 *self.window_id.lock().unwrap(),
1793 self.webview_id,
1794 WebviewMessage::Hide,
1795 ),
1796 )
1797 }
1798
1799 fn show(&self) -> Result<()> {
1800 send_user_message(
1801 &self.context,
1802 Message::Webview(
1803 *self.window_id.lock().unwrap(),
1804 self.webview_id,
1805 WebviewMessage::Show,
1806 ),
1807 )
1808 }
1809
1810 fn set_background_color(&self, color: Option<Color>) -> Result<()> {
1811 send_user_message(
1812 &self.context,
1813 Message::Webview(
1814 *self.window_id.lock().unwrap(),
1815 self.webview_id,
1816 WebviewMessage::SetBackgroundColor(color),
1817 ),
1818 )
1819 }
1820}
1821
1822#[derive(Debug, Clone)]
1824pub struct WryWindowDispatcher<T: UserEvent> {
1825 window_id: WindowId,
1826 context: Context<T>,
1827}
1828
1829#[allow(clippy::non_send_fields_in_send_ty)]
1831unsafe impl<T: UserEvent> Sync for WryWindowDispatcher<T> {}
1832
1833fn get_raw_window_handle<T: UserEvent>(
1834 dispatcher: &WryWindowDispatcher<T>,
1835) -> Result<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>> {
1836 window_getter!(dispatcher, WindowMessage::RawWindowHandle)
1837}
1838
1839impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
1840 type Runtime = Wry<T>;
1841 type WindowBuilder = WindowBuilderWrapper;
1842
1843 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1844 send_user_message(&self.context, Message::Task(Box::new(f)))
1845 }
1846
1847 fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1848 let id = self.context.next_window_event_id();
1849 let _ = self.context.proxy.send_event(Message::Window(
1850 self.window_id,
1851 WindowMessage::AddEventListener(id, Box::new(f)),
1852 ));
1853 id
1854 }
1855
1856 fn scale_factor(&self) -> Result<f64> {
1859 window_getter!(self, WindowMessage::ScaleFactor)
1860 }
1861
1862 fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
1863 window_getter!(self, WindowMessage::InnerPosition)?
1864 }
1865
1866 fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
1867 window_getter!(self, WindowMessage::OuterPosition)?
1868 }
1869
1870 fn inner_size(&self) -> Result<PhysicalSize<u32>> {
1871 window_getter!(self, WindowMessage::InnerSize)
1872 }
1873
1874 fn outer_size(&self) -> Result<PhysicalSize<u32>> {
1875 window_getter!(self, WindowMessage::OuterSize)
1876 }
1877
1878 fn is_fullscreen(&self) -> Result<bool> {
1879 window_getter!(self, WindowMessage::IsFullscreen)
1880 }
1881
1882 fn is_minimized(&self) -> Result<bool> {
1883 window_getter!(self, WindowMessage::IsMinimized)
1884 }
1885
1886 fn is_maximized(&self) -> Result<bool> {
1887 window_getter!(self, WindowMessage::IsMaximized)
1888 }
1889
1890 fn is_focused(&self) -> Result<bool> {
1891 window_getter!(self, WindowMessage::IsFocused)
1892 }
1893
1894 fn is_decorated(&self) -> Result<bool> {
1896 window_getter!(self, WindowMessage::IsDecorated)
1897 }
1898
1899 fn is_resizable(&self) -> Result<bool> {
1901 window_getter!(self, WindowMessage::IsResizable)
1902 }
1903
1904 fn is_maximizable(&self) -> Result<bool> {
1906 window_getter!(self, WindowMessage::IsMaximizable)
1907 }
1908
1909 fn is_minimizable(&self) -> Result<bool> {
1911 window_getter!(self, WindowMessage::IsMinimizable)
1912 }
1913
1914 fn is_closable(&self) -> Result<bool> {
1916 window_getter!(self, WindowMessage::IsClosable)
1917 }
1918
1919 fn is_visible(&self) -> Result<bool> {
1920 window_getter!(self, WindowMessage::IsVisible)
1921 }
1922
1923 fn title(&self) -> Result<String> {
1924 window_getter!(self, WindowMessage::Title)
1925 }
1926
1927 fn current_monitor(&self) -> Result<Option<Monitor>> {
1928 Ok(window_getter!(self, WindowMessage::CurrentMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
1929 }
1930
1931 fn primary_monitor(&self) -> Result<Option<Monitor>> {
1932 Ok(window_getter!(self, WindowMessage::PrimaryMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
1933 }
1934
1935 fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
1936 let (tx, rx) = channel();
1937
1938 let _ = send_user_message(
1939 &self.context,
1940 Message::Window(self.window_id, WindowMessage::MonitorFromPoint(tx, (x, y))),
1941 );
1942
1943 Ok(
1944 rx.recv()
1945 .map_err(|_| crate::Error::FailedToReceiveMessage)?
1946 .map(|m| MonitorHandleWrapper(m).into()),
1947 )
1948 }
1949
1950 fn available_monitors(&self) -> Result<Vec<Monitor>> {
1951 Ok(
1952 window_getter!(self, WindowMessage::AvailableMonitors)?
1953 .into_iter()
1954 .map(|m| MonitorHandleWrapper(m).into())
1955 .collect(),
1956 )
1957 }
1958
1959 fn theme(&self) -> Result<Theme> {
1960 window_getter!(self, WindowMessage::Theme)
1961 }
1962
1963 fn is_enabled(&self) -> Result<bool> {
1964 window_getter!(self, WindowMessage::IsEnabled)
1965 }
1966
1967 fn is_always_on_top(&self) -> Result<bool> {
1968 window_getter!(self, WindowMessage::IsAlwaysOnTop)
1969 }
1970
1971 #[cfg(any(
1972 target_os = "linux",
1973 target_os = "dragonfly",
1974 target_os = "freebsd",
1975 target_os = "netbsd",
1976 target_os = "openbsd"
1977 ))]
1978 fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
1979 window_getter!(self, WindowMessage::GtkWindow).map(|w| w.0)
1980 }
1981
1982 #[cfg(any(
1983 target_os = "linux",
1984 target_os = "dragonfly",
1985 target_os = "freebsd",
1986 target_os = "netbsd",
1987 target_os = "openbsd"
1988 ))]
1989 fn default_vbox(&self) -> Result<gtk::Box> {
1990 window_getter!(self, WindowMessage::GtkBox).map(|w| w.0)
1991 }
1992
1993 fn window_handle(
1994 &self,
1995 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
1996 get_raw_window_handle(self)
1997 .map_err(|_| raw_window_handle::HandleError::Unavailable)
1998 .and_then(|r| r.map(|h| unsafe { raw_window_handle::WindowHandle::borrow_raw(h.0) }))
1999 }
2000
2001 fn center(&self) -> Result<()> {
2004 send_user_message(
2005 &self.context,
2006 Message::Window(self.window_id, WindowMessage::Center),
2007 )
2008 }
2009
2010 fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
2011 send_user_message(
2012 &self.context,
2013 Message::Window(
2014 self.window_id,
2015 WindowMessage::RequestUserAttention(request_type.map(Into::into)),
2016 ),
2017 )
2018 }
2019
2020 fn create_window<F: Fn(RawWindow) + Send + 'static>(
2023 &mut self,
2024 pending: PendingWindow<T, Self::Runtime>,
2025 after_window_creation: Option<F>,
2026 ) -> Result<DetachedWindow<T, Self::Runtime>> {
2027 self.context.create_window(pending, after_window_creation)
2028 }
2029
2030 fn create_webview(
2033 &mut self,
2034 pending: PendingWebview<T, Self::Runtime>,
2035 ) -> Result<DetachedWebview<T, Self::Runtime>> {
2036 self.context.create_webview(self.window_id, pending)
2037 }
2038
2039 fn set_resizable(&self, resizable: bool) -> Result<()> {
2040 send_user_message(
2041 &self.context,
2042 Message::Window(self.window_id, WindowMessage::SetResizable(resizable)),
2043 )
2044 }
2045
2046 fn set_enabled(&self, enabled: bool) -> Result<()> {
2047 send_user_message(
2048 &self.context,
2049 Message::Window(self.window_id, WindowMessage::SetEnabled(enabled)),
2050 )
2051 }
2052
2053 fn set_maximizable(&self, maximizable: bool) -> Result<()> {
2054 send_user_message(
2055 &self.context,
2056 Message::Window(self.window_id, WindowMessage::SetMaximizable(maximizable)),
2057 )
2058 }
2059
2060 fn set_minimizable(&self, minimizable: bool) -> Result<()> {
2061 send_user_message(
2062 &self.context,
2063 Message::Window(self.window_id, WindowMessage::SetMinimizable(minimizable)),
2064 )
2065 }
2066
2067 fn set_closable(&self, closable: bool) -> Result<()> {
2068 send_user_message(
2069 &self.context,
2070 Message::Window(self.window_id, WindowMessage::SetClosable(closable)),
2071 )
2072 }
2073
2074 fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
2075 send_user_message(
2076 &self.context,
2077 Message::Window(self.window_id, WindowMessage::SetTitle(title.into())),
2078 )
2079 }
2080
2081 fn maximize(&self) -> Result<()> {
2082 send_user_message(
2083 &self.context,
2084 Message::Window(self.window_id, WindowMessage::Maximize),
2085 )
2086 }
2087
2088 fn unmaximize(&self) -> Result<()> {
2089 send_user_message(
2090 &self.context,
2091 Message::Window(self.window_id, WindowMessage::Unmaximize),
2092 )
2093 }
2094
2095 fn minimize(&self) -> Result<()> {
2096 send_user_message(
2097 &self.context,
2098 Message::Window(self.window_id, WindowMessage::Minimize),
2099 )
2100 }
2101
2102 fn unminimize(&self) -> Result<()> {
2103 send_user_message(
2104 &self.context,
2105 Message::Window(self.window_id, WindowMessage::Unminimize),
2106 )
2107 }
2108
2109 fn show(&self) -> Result<()> {
2110 send_user_message(
2111 &self.context,
2112 Message::Window(self.window_id, WindowMessage::Show),
2113 )
2114 }
2115
2116 fn hide(&self) -> Result<()> {
2117 send_user_message(
2118 &self.context,
2119 Message::Window(self.window_id, WindowMessage::Hide),
2120 )
2121 }
2122
2123 fn close(&self) -> Result<()> {
2124 self
2126 .context
2127 .proxy
2128 .send_event(Message::Window(self.window_id, WindowMessage::Close))
2129 .map_err(|_| Error::FailedToSendMessage)
2130 }
2131
2132 fn destroy(&self) -> Result<()> {
2133 self
2135 .context
2136 .proxy
2137 .send_event(Message::Window(self.window_id, WindowMessage::Destroy))
2138 .map_err(|_| Error::FailedToSendMessage)
2139 }
2140
2141 fn set_decorations(&self, decorations: bool) -> Result<()> {
2142 send_user_message(
2143 &self.context,
2144 Message::Window(self.window_id, WindowMessage::SetDecorations(decorations)),
2145 )
2146 }
2147
2148 fn set_shadow(&self, enable: bool) -> Result<()> {
2149 send_user_message(
2150 &self.context,
2151 Message::Window(self.window_id, WindowMessage::SetShadow(enable)),
2152 )
2153 }
2154
2155 fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> {
2156 send_user_message(
2157 &self.context,
2158 Message::Window(
2159 self.window_id,
2160 WindowMessage::SetAlwaysOnBottom(always_on_bottom),
2161 ),
2162 )
2163 }
2164
2165 fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
2166 send_user_message(
2167 &self.context,
2168 Message::Window(self.window_id, WindowMessage::SetAlwaysOnTop(always_on_top)),
2169 )
2170 }
2171
2172 fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> {
2173 send_user_message(
2174 &self.context,
2175 Message::Window(
2176 self.window_id,
2177 WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces),
2178 ),
2179 )
2180 }
2181
2182 fn set_content_protected(&self, protected: bool) -> Result<()> {
2183 send_user_message(
2184 &self.context,
2185 Message::Window(
2186 self.window_id,
2187 WindowMessage::SetContentProtected(protected),
2188 ),
2189 )
2190 }
2191
2192 fn set_size(&self, size: Size) -> Result<()> {
2193 send_user_message(
2194 &self.context,
2195 Message::Window(self.window_id, WindowMessage::SetSize(size)),
2196 )
2197 }
2198
2199 fn set_min_size(&self, size: Option<Size>) -> Result<()> {
2200 send_user_message(
2201 &self.context,
2202 Message::Window(self.window_id, WindowMessage::SetMinSize(size)),
2203 )
2204 }
2205
2206 fn set_max_size(&self, size: Option<Size>) -> Result<()> {
2207 send_user_message(
2208 &self.context,
2209 Message::Window(self.window_id, WindowMessage::SetMaxSize(size)),
2210 )
2211 }
2212
2213 fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> {
2214 send_user_message(
2215 &self.context,
2216 Message::Window(
2217 self.window_id,
2218 WindowMessage::SetSizeConstraints(constraints),
2219 ),
2220 )
2221 }
2222
2223 fn set_position(&self, position: Position) -> Result<()> {
2224 send_user_message(
2225 &self.context,
2226 Message::Window(self.window_id, WindowMessage::SetPosition(position)),
2227 )
2228 }
2229
2230 fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
2231 send_user_message(
2232 &self.context,
2233 Message::Window(self.window_id, WindowMessage::SetFullscreen(fullscreen)),
2234 )
2235 }
2236
2237 #[cfg(target_os = "macos")]
2238 fn set_simple_fullscreen(&self, enable: bool) -> Result<()> {
2239 send_user_message(
2240 &self.context,
2241 Message::Window(self.window_id, WindowMessage::SetSimpleFullscreen(enable)),
2242 )
2243 }
2244
2245 fn set_focus(&self) -> Result<()> {
2246 send_user_message(
2247 &self.context,
2248 Message::Window(self.window_id, WindowMessage::SetFocus),
2249 )
2250 }
2251
2252 fn set_focusable(&self, focusable: bool) -> Result<()> {
2253 send_user_message(
2254 &self.context,
2255 Message::Window(self.window_id, WindowMessage::SetFocusable(focusable)),
2256 )
2257 }
2258
2259 fn set_icon(&self, icon: Icon) -> Result<()> {
2260 send_user_message(
2261 &self.context,
2262 Message::Window(
2263 self.window_id,
2264 WindowMessage::SetIcon(TaoIcon::try_from(icon)?.0),
2265 ),
2266 )
2267 }
2268
2269 fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
2270 send_user_message(
2271 &self.context,
2272 Message::Window(self.window_id, WindowMessage::SetSkipTaskbar(skip)),
2273 )
2274 }
2275
2276 fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2277 send_user_message(
2278 &self.context,
2279 Message::Window(self.window_id, WindowMessage::SetCursorGrab(grab)),
2280 )
2281 }
2282
2283 fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2284 send_user_message(
2285 &self.context,
2286 Message::Window(self.window_id, WindowMessage::SetCursorVisible(visible)),
2287 )
2288 }
2289
2290 fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2291 send_user_message(
2292 &self.context,
2293 Message::Window(self.window_id, WindowMessage::SetCursorIcon(icon)),
2294 )
2295 }
2296
2297 fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2298 send_user_message(
2299 &self.context,
2300 Message::Window(
2301 self.window_id,
2302 WindowMessage::SetCursorPosition(position.into()),
2303 ),
2304 )
2305 }
2306
2307 fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2308 send_user_message(
2309 &self.context,
2310 Message::Window(self.window_id, WindowMessage::SetIgnoreCursorEvents(ignore)),
2311 )
2312 }
2313
2314 fn start_dragging(&self) -> Result<()> {
2315 send_user_message(
2316 &self.context,
2317 Message::Window(self.window_id, WindowMessage::DragWindow),
2318 )
2319 }
2320
2321 fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
2322 send_user_message(
2323 &self.context,
2324 Message::Window(self.window_id, WindowMessage::ResizeDragWindow(direction)),
2325 )
2326 }
2327
2328 fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
2329 send_user_message(
2330 &self.context,
2331 Message::Window(
2332 self.window_id,
2333 WindowMessage::SetBadgeCount(count, desktop_filename),
2334 ),
2335 )
2336 }
2337
2338 fn set_badge_label(&self, label: Option<String>) -> Result<()> {
2339 send_user_message(
2340 &self.context,
2341 Message::Window(self.window_id, WindowMessage::SetBadgeLabel(label)),
2342 )
2343 }
2344
2345 fn set_overlay_icon(&self, icon: Option<Icon>) -> Result<()> {
2346 let icon: Result<Option<TaoIcon>> = icon.map_or(Ok(None), |x| Ok(Some(TaoIcon::try_from(x)?)));
2347
2348 send_user_message(
2349 &self.context,
2350 Message::Window(self.window_id, WindowMessage::SetOverlayIcon(icon?)),
2351 )
2352 }
2353
2354 fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
2355 send_user_message(
2356 &self.context,
2357 Message::Window(
2358 self.window_id,
2359 WindowMessage::SetProgressBar(progress_state),
2360 ),
2361 )
2362 }
2363
2364 fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
2365 send_user_message(
2366 &self.context,
2367 Message::Window(self.window_id, WindowMessage::SetTitleBarStyle(style)),
2368 )
2369 }
2370
2371 fn set_traffic_light_position(&self, position: Position) -> Result<()> {
2372 send_user_message(
2373 &self.context,
2374 Message::Window(
2375 self.window_id,
2376 WindowMessage::SetTrafficLightPosition(position),
2377 ),
2378 )
2379 }
2380
2381 fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
2382 send_user_message(
2383 &self.context,
2384 Message::Window(self.window_id, WindowMessage::SetTheme(theme)),
2385 )
2386 }
2387
2388 fn set_background_color(&self, color: Option<Color>) -> Result<()> {
2389 send_user_message(
2390 &self.context,
2391 Message::Window(self.window_id, WindowMessage::SetBackgroundColor(color)),
2392 )
2393 }
2394}
2395
2396#[derive(Clone)]
2397pub struct WebviewWrapper {
2398 label: String,
2399 id: WebviewId,
2400 inner: Rc<WebView>,
2401 context_store: WebContextStore,
2402 webview_event_listeners: WebviewEventListeners,
2403 context_key: Option<PathBuf>,
2405 bounds: Arc<Mutex<Option<WebviewBounds>>>,
2406}
2407
2408impl Deref for WebviewWrapper {
2409 type Target = WebView;
2410
2411 #[inline(always)]
2412 fn deref(&self) -> &Self::Target {
2413 &self.inner
2414 }
2415}
2416
2417impl Drop for WebviewWrapper {
2418 fn drop(&mut self) {
2419 if Rc::get_mut(&mut self.inner).is_some() {
2420 let mut context_store = self.context_store.lock().unwrap();
2421
2422 if let Some(web_context) = context_store.get_mut(&self.context_key) {
2423 web_context.referenced_by_webviews.remove(&self.label);
2424
2425 if web_context.referenced_by_webviews.is_empty() {
2426 context_store.remove(&self.context_key);
2427 }
2428 }
2429 }
2430 }
2431}
2432
2433pub struct WindowWrapper {
2434 label: String,
2435 inner: Option<Arc<Window>>,
2436 has_children: AtomicBool,
2439 webviews: Vec<WebviewWrapper>,
2440 window_event_listeners: WindowEventListeners,
2441 #[cfg(windows)]
2442 background_color: Option<tao::window::RGBA>,
2443 #[cfg(windows)]
2444 is_window_transparent: bool,
2445 #[cfg(windows)]
2446 surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
2447 focused_webview: Arc<Mutex<Option<String>>>,
2448}
2449
2450impl WindowWrapper {
2451 pub fn label(&self) -> &str {
2452 &self.label
2453 }
2454}
2455
2456impl fmt::Debug for WindowWrapper {
2457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2458 f.debug_struct("WindowWrapper")
2459 .field("label", &self.label)
2460 .field("inner", &self.inner)
2461 .finish()
2462 }
2463}
2464
2465#[derive(Debug, Clone)]
2466pub struct EventProxy<T: UserEvent>(TaoEventLoopProxy<Message<T>>);
2467
2468#[cfg(target_os = "ios")]
2469#[allow(clippy::non_send_fields_in_send_ty)]
2470unsafe impl<T: UserEvent> Sync for EventProxy<T> {}
2471
2472impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
2473 fn send_event(&self, event: T) -> Result<()> {
2474 self
2475 .0
2476 .send_event(Message::UserEvent(event))
2477 .map_err(|_| Error::EventLoopClosed)
2478 }
2479}
2480
2481pub trait PluginBuilder<T: UserEvent> {
2482 type Plugin: Plugin<T>;
2483 fn build(self, context: Context<T>) -> Self::Plugin;
2484}
2485
2486pub trait Plugin<T: UserEvent> {
2487 fn on_event(
2488 &mut self,
2489 event: &Event<Message<T>>,
2490 event_loop: &EventLoopWindowTarget<Message<T>>,
2491 proxy: &TaoEventLoopProxy<Message<T>>,
2492 control_flow: &mut ControlFlow,
2493 context: EventLoopIterationContext<'_, T>,
2494 web_context: &WebContextStore,
2495 ) -> bool;
2496}
2497
2498pub struct Wry<T: UserEvent> {
2500 context: Context<T>,
2501 event_loop: EventLoop<Message<T>>,
2502}
2503
2504impl<T: UserEvent> fmt::Debug for Wry<T> {
2505 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2506 f.debug_struct("Wry")
2507 .field("main_thread_id", &self.context.main_thread_id)
2508 .field("event_loop", &self.event_loop)
2509 .field("windows", &self.context.main_thread.windows)
2510 .field("web_context", &self.context.main_thread.web_context)
2511 .finish()
2512 }
2513}
2514
2515#[derive(Debug, Clone)]
2517pub struct WryHandle<T: UserEvent> {
2518 context: Context<T>,
2519}
2520
2521#[allow(clippy::non_send_fields_in_send_ty)]
2523unsafe impl<T: UserEvent> Sync for WryHandle<T> {}
2524
2525impl<T: UserEvent> WryHandle<T> {
2526 pub fn create_tao_window<F: FnOnce() -> (String, TaoWindowBuilder) + Send + 'static>(
2528 &self,
2529 f: F,
2530 ) -> Result<Weak<Window>> {
2531 let id = self.context.next_window_id();
2532 let (tx, rx) = channel();
2533 send_user_message(&self.context, Message::CreateRawWindow(id, Box::new(f), tx))?;
2534 rx.recv().unwrap()
2535 }
2536
2537 pub fn window_id(&self, window_id: TaoWindowId) -> WindowId {
2539 *self
2540 .context
2541 .window_id_map
2542 .0
2543 .lock()
2544 .unwrap()
2545 .get(&window_id)
2546 .unwrap()
2547 }
2548
2549 pub fn send_event(&self, message: Message<T>) -> Result<()> {
2551 self
2552 .context
2553 .proxy
2554 .send_event(message)
2555 .map_err(|_| Error::FailedToSendMessage)?;
2556 Ok(())
2557 }
2558
2559 pub fn plugin<P: PluginBuilder<T> + 'static>(&mut self, plugin: P)
2560 where
2561 <P as PluginBuilder<T>>::Plugin: Send,
2562 {
2563 self
2564 .context
2565 .plugins
2566 .lock()
2567 .unwrap()
2568 .push(Box::new(plugin.build(self.context.clone())));
2569 }
2570}
2571
2572impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
2573 type Runtime = Wry<T>;
2574
2575 fn create_proxy(&self) -> EventProxy<T> {
2576 EventProxy(self.context.proxy.clone())
2577 }
2578
2579 #[cfg(target_os = "macos")]
2580 fn set_activation_policy(&self, activation_policy: ActivationPolicy) -> Result<()> {
2581 send_user_message(
2582 &self.context,
2583 Message::SetActivationPolicy(activation_policy),
2584 )
2585 }
2586
2587 #[cfg(target_os = "macos")]
2588 fn set_dock_visibility(&self, visible: bool) -> Result<()> {
2589 send_user_message(&self.context, Message::SetDockVisibility(visible))
2590 }
2591
2592 fn request_exit(&self, code: i32) -> Result<()> {
2593 self
2595 .context
2596 .proxy
2597 .send_event(Message::RequestExit(code))
2598 .map_err(|_| Error::FailedToSendMessage)
2599 }
2600
2601 fn create_window<F: Fn(RawWindow) + Send + 'static>(
2604 &self,
2605 pending: PendingWindow<T, Self::Runtime>,
2606 after_window_creation: Option<F>,
2607 ) -> Result<DetachedWindow<T, Self::Runtime>> {
2608 self.context.create_window(pending, after_window_creation)
2609 }
2610
2611 fn create_webview(
2614 &self,
2615 window_id: WindowId,
2616 pending: PendingWebview<T, Self::Runtime>,
2617 ) -> Result<DetachedWebview<T, Self::Runtime>> {
2618 self.context.create_webview(window_id, pending)
2619 }
2620
2621 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
2622 send_user_message(&self.context, Message::Task(Box::new(f)))
2623 }
2624
2625 fn display_handle(
2626 &self,
2627 ) -> std::result::Result<DisplayHandle<'_>, raw_window_handle::HandleError> {
2628 self.context.main_thread.window_target.display_handle()
2629 }
2630
2631 fn primary_monitor(&self) -> Option<Monitor> {
2632 self
2633 .context
2634 .main_thread
2635 .window_target
2636 .primary_monitor()
2637 .map(|m| MonitorHandleWrapper(m).into())
2638 }
2639
2640 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
2641 self
2642 .context
2643 .main_thread
2644 .window_target
2645 .monitor_from_point(x, y)
2646 .map(|m| MonitorHandleWrapper(m).into())
2647 }
2648
2649 fn available_monitors(&self) -> Vec<Monitor> {
2650 self
2651 .context
2652 .main_thread
2653 .window_target
2654 .available_monitors()
2655 .map(|m| MonitorHandleWrapper(m).into())
2656 .collect()
2657 }
2658
2659 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
2660 event_loop_window_getter!(self, EventLoopWindowTargetMessage::CursorPosition)?
2661 .map(PhysicalPositionWrapper)
2662 .map(Into::into)
2663 .map_err(|_| Error::FailedToGetCursorPosition)
2664 }
2665
2666 fn set_theme(&self, theme: Option<Theme>) {
2667 let _ = send_user_message(
2668 &self.context,
2669 Message::EventLoopWindowTarget(EventLoopWindowTargetMessage::SetTheme(theme)),
2670 );
2671 }
2672
2673 #[cfg(target_os = "macos")]
2674 fn show(&self) -> tauri_runtime::Result<()> {
2675 send_user_message(
2676 &self.context,
2677 Message::Application(ApplicationMessage::Show),
2678 )
2679 }
2680
2681 #[cfg(target_os = "macos")]
2682 fn hide(&self) -> tauri_runtime::Result<()> {
2683 send_user_message(
2684 &self.context,
2685 Message::Application(ApplicationMessage::Hide),
2686 )
2687 }
2688
2689 fn set_device_event_filter(&self, filter: DeviceEventFilter) {
2690 let _ = send_user_message(
2691 &self.context,
2692 Message::EventLoopWindowTarget(EventLoopWindowTargetMessage::SetDeviceEventFilter(filter)),
2693 );
2694 }
2695
2696 #[cfg(target_os = "android")]
2697 fn find_class<'a>(
2698 &self,
2699 env: &mut jni::JNIEnv<'a>,
2700 activity: &jni::objects::JObject<'_>,
2701 name: impl Into<String>,
2702 ) -> std::result::Result<jni::objects::JClass<'a>, jni::errors::Error> {
2703 find_class(env, activity, name.into())
2704 }
2705
2706 #[cfg(target_os = "android")]
2707 fn run_on_android_context<F>(&self, f: F)
2708 where
2709 F: FnOnce(&mut jni::JNIEnv, &jni::objects::JObject, &jni::objects::JObject) + Send + 'static,
2710 {
2711 dispatch(f)
2712 }
2713
2714 #[cfg(any(target_os = "macos", target_os = "ios"))]
2715 fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
2716 &self,
2717 cb: F,
2718 ) -> Result<()> {
2719 send_user_message(
2720 &self.context,
2721 Message::Application(ApplicationMessage::FetchDataStoreIdentifiers(Box::new(cb))),
2722 )
2723 }
2724
2725 #[cfg(any(target_os = "macos", target_os = "ios"))]
2726 fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
2727 &self,
2728 uuid: [u8; 16],
2729 cb: F,
2730 ) -> Result<()> {
2731 send_user_message(
2732 &self.context,
2733 Message::Application(ApplicationMessage::RemoveDataStore(uuid, Box::new(cb))),
2734 )
2735 }
2736}
2737
2738impl<T: UserEvent> Wry<T> {
2739 fn init_with_builder(
2740 mut event_loop_builder: EventLoopBuilder<Message<T>>,
2741 #[allow(unused_variables)] args: RuntimeInitArgs,
2742 ) -> Result<Self> {
2743 #[cfg(windows)]
2744 if let Some(hook) = args.msg_hook {
2745 use tao::platform::windows::EventLoopBuilderExtWindows;
2746 event_loop_builder.with_msg_hook(hook);
2747 }
2748
2749 #[cfg(any(
2750 target_os = "linux",
2751 target_os = "dragonfly",
2752 target_os = "freebsd",
2753 target_os = "netbsd",
2754 target_os = "openbsd"
2755 ))]
2756 if let Some(app_id) = args.app_id {
2757 use tao::platform::unix::EventLoopBuilderExtUnix;
2758 event_loop_builder.with_app_id(app_id);
2759 }
2760 Self::init(event_loop_builder.build())
2761 }
2762
2763 fn init(event_loop: EventLoop<Message<T>>) -> Result<Self> {
2764 let main_thread_id = current_thread().id();
2765 let web_context = WebContextStore::default();
2766
2767 let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default())));
2768 let window_id_map = WindowIdStore::default();
2769
2770 let context = Context {
2771 window_id_map,
2772 main_thread_id,
2773 proxy: event_loop.create_proxy(),
2774 main_thread: DispatcherMainThreadContext {
2775 window_target: event_loop.deref().clone(),
2776 web_context,
2777 windows,
2778 #[cfg(feature = "tracing")]
2779 active_tracing_spans: Default::default(),
2780 },
2781 plugins: Default::default(),
2782 next_window_id: Default::default(),
2783 next_webview_id: Default::default(),
2784 next_window_event_id: Default::default(),
2785 next_webview_event_id: Default::default(),
2786 webview_runtime_installed: wry::webview_version().is_ok(),
2787 };
2788
2789 Ok(Self {
2790 context,
2791 event_loop,
2792 })
2793 }
2794}
2795
2796impl<T: UserEvent> Runtime<T> for Wry<T> {
2797 type WindowDispatcher = WryWindowDispatcher<T>;
2798 type WebviewDispatcher = WryWebviewDispatcher<T>;
2799 type Handle = WryHandle<T>;
2800
2801 type EventLoopProxy = EventProxy<T>;
2802
2803 fn new(args: RuntimeInitArgs) -> Result<Self> {
2804 Self::init_with_builder(EventLoopBuilder::<Message<T>>::with_user_event(), args)
2805 }
2806 #[cfg(any(
2807 target_os = "linux",
2808 target_os = "dragonfly",
2809 target_os = "freebsd",
2810 target_os = "netbsd",
2811 target_os = "openbsd"
2812 ))]
2813 fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
2814 use tao::platform::unix::EventLoopBuilderExtUnix;
2815 let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
2816 event_loop_builder.with_any_thread(true);
2817 Self::init_with_builder(event_loop_builder, args)
2818 }
2819
2820 #[cfg(windows)]
2821 fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
2822 use tao::platform::windows::EventLoopBuilderExtWindows;
2823 let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
2824 event_loop_builder.with_any_thread(true);
2825 Self::init_with_builder(event_loop_builder, args)
2826 }
2827
2828 fn create_proxy(&self) -> EventProxy<T> {
2829 EventProxy(self.event_loop.create_proxy())
2830 }
2831
2832 fn handle(&self) -> Self::Handle {
2833 WryHandle {
2834 context: self.context.clone(),
2835 }
2836 }
2837
2838 fn create_window<F: Fn(RawWindow) + Send + 'static>(
2839 &self,
2840 pending: PendingWindow<T, Self>,
2841 after_window_creation: Option<F>,
2842 ) -> Result<DetachedWindow<T, Self>> {
2843 let label = pending.label.clone();
2844 let window_id = self.context.next_window_id();
2845 let (webview_id, use_https_scheme) = pending
2846 .webview
2847 .as_ref()
2848 .map(|w| {
2849 (
2850 Some(self.context.next_webview_id()),
2851 w.webview_attributes.use_https_scheme,
2852 )
2853 })
2854 .unwrap_or((None, false));
2855
2856 let window = create_window(
2857 window_id,
2858 webview_id.unwrap_or_default(),
2859 &self.event_loop,
2860 &self.context,
2861 pending,
2862 after_window_creation,
2863 )?;
2864
2865 let dispatcher = WryWindowDispatcher {
2866 window_id,
2867 context: self.context.clone(),
2868 };
2869
2870 self
2871 .context
2872 .main_thread
2873 .windows
2874 .0
2875 .borrow_mut()
2876 .insert(window_id, window);
2877
2878 let detached_webview = webview_id.map(|id| {
2879 let webview = DetachedWebview {
2880 label: label.clone(),
2881 dispatcher: WryWebviewDispatcher {
2882 window_id: Arc::new(Mutex::new(window_id)),
2883 webview_id: id,
2884 context: self.context.clone(),
2885 },
2886 };
2887 DetachedWindowWebview {
2888 webview,
2889 use_https_scheme,
2890 }
2891 });
2892
2893 Ok(DetachedWindow {
2894 id: window_id,
2895 label,
2896 dispatcher,
2897 webview: detached_webview,
2898 })
2899 }
2900
2901 fn create_webview(
2902 &self,
2903 window_id: WindowId,
2904 pending: PendingWebview<T, Self>,
2905 ) -> Result<DetachedWebview<T, Self>> {
2906 let label = pending.label.clone();
2907
2908 let window = self
2909 .context
2910 .main_thread
2911 .windows
2912 .0
2913 .borrow()
2914 .get(&window_id)
2915 .map(|w| (w.inner.clone(), w.focused_webview.clone()));
2916 if let Some((Some(window), focused_webview)) = window {
2917 let window_id_wrapper = Arc::new(Mutex::new(window_id));
2918
2919 let webview_id = self.context.next_webview_id();
2920
2921 let webview = create_webview(
2922 WebviewKind::WindowChild,
2923 &window,
2924 window_id_wrapper.clone(),
2925 webview_id,
2926 &self.context,
2927 pending,
2928 focused_webview,
2929 )?;
2930
2931 #[allow(unknown_lints, clippy::manual_inspect)]
2932 self
2933 .context
2934 .main_thread
2935 .windows
2936 .0
2937 .borrow_mut()
2938 .get_mut(&window_id)
2939 .map(|w| {
2940 w.webviews.push(webview);
2941 w.has_children.store(true, Ordering::Relaxed);
2942 w
2943 });
2944
2945 let dispatcher = WryWebviewDispatcher {
2946 window_id: window_id_wrapper,
2947 webview_id,
2948 context: self.context.clone(),
2949 };
2950
2951 Ok(DetachedWebview { label, dispatcher })
2952 } else {
2953 Err(Error::WindowNotFound)
2954 }
2955 }
2956
2957 fn primary_monitor(&self) -> Option<Monitor> {
2958 self
2959 .context
2960 .main_thread
2961 .window_target
2962 .primary_monitor()
2963 .map(|m| MonitorHandleWrapper(m).into())
2964 }
2965
2966 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
2967 self
2968 .context
2969 .main_thread
2970 .window_target
2971 .monitor_from_point(x, y)
2972 .map(|m| MonitorHandleWrapper(m).into())
2973 }
2974
2975 fn available_monitors(&self) -> Vec<Monitor> {
2976 self
2977 .context
2978 .main_thread
2979 .window_target
2980 .available_monitors()
2981 .map(|m| MonitorHandleWrapper(m).into())
2982 .collect()
2983 }
2984
2985 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
2986 self
2987 .context
2988 .main_thread
2989 .window_target
2990 .cursor_position()
2991 .map(PhysicalPositionWrapper)
2992 .map(Into::into)
2993 .map_err(|_| Error::FailedToGetCursorPosition)
2994 }
2995
2996 fn set_theme(&self, theme: Option<Theme>) {
2997 self.event_loop.set_theme(to_tao_theme(theme));
2998 }
2999
3000 #[cfg(target_os = "macos")]
3001 fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
3002 self
3003 .event_loop
3004 .set_activation_policy(tao_activation_policy(activation_policy));
3005 }
3006
3007 #[cfg(target_os = "macos")]
3008 fn set_dock_visibility(&mut self, visible: bool) {
3009 self.event_loop.set_dock_visibility(visible);
3010 }
3011
3012 #[cfg(target_os = "macos")]
3013 fn show(&self) {
3014 self.event_loop.show_application();
3015 }
3016
3017 #[cfg(target_os = "macos")]
3018 fn hide(&self) {
3019 self.event_loop.hide_application();
3020 }
3021
3022 fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
3023 self
3024 .event_loop
3025 .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
3026 }
3027
3028 #[cfg(desktop)]
3029 fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, mut callback: F) {
3030 use tao::platform::run_return::EventLoopExtRunReturn;
3031 let windows = self.context.main_thread.windows.clone();
3032 let window_id_map = self.context.window_id_map.clone();
3033 let web_context = &self.context.main_thread.web_context;
3034 let plugins = self.context.plugins.clone();
3035
3036 #[cfg(feature = "tracing")]
3037 let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();
3038
3039 let proxy = self.event_loop.create_proxy();
3040
3041 self
3042 .event_loop
3043 .run_return(|event, event_loop, control_flow| {
3044 *control_flow = ControlFlow::Wait;
3045 if let Event::MainEventsCleared = &event {
3046 *control_flow = ControlFlow::Exit;
3047 }
3048
3049 for p in plugins.lock().unwrap().iter_mut() {
3050 let prevent_default = p.on_event(
3051 &event,
3052 event_loop,
3053 &proxy,
3054 control_flow,
3055 EventLoopIterationContext {
3056 callback: &mut callback,
3057 window_id_map: window_id_map.clone(),
3058 windows: windows.clone(),
3059 #[cfg(feature = "tracing")]
3060 active_tracing_spans: active_tracing_spans.clone(),
3061 },
3062 web_context,
3063 );
3064 if prevent_default {
3065 return;
3066 }
3067 }
3068
3069 handle_event_loop(
3070 event,
3071 event_loop,
3072 control_flow,
3073 EventLoopIterationContext {
3074 callback: &mut callback,
3075 windows: windows.clone(),
3076 window_id_map: window_id_map.clone(),
3077 #[cfg(feature = "tracing")]
3078 active_tracing_spans: active_tracing_spans.clone(),
3079 },
3080 );
3081 });
3082 }
3083
3084 fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) {
3085 let event_handler = make_event_handler(&self, callback);
3086
3087 self.event_loop.run(event_handler)
3088 }
3089
3090 #[cfg(not(target_os = "ios"))]
3091 fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, callback: F) -> i32 {
3092 use tao::platform::run_return::EventLoopExtRunReturn;
3093
3094 let event_handler = make_event_handler(&self, callback);
3095
3096 self.event_loop.run_return(event_handler)
3097 }
3098
3099 #[cfg(target_os = "ios")]
3100 fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
3101 self.run(callback);
3102 0
3103 }
3104}
3105
3106fn make_event_handler<T, F>(
3107 runtime: &Wry<T>,
3108 mut callback: F,
3109) -> impl FnMut(Event<'_, Message<T>>, &EventLoopWindowTarget<Message<T>>, &mut ControlFlow)
3110where
3111 T: UserEvent,
3112 F: FnMut(RunEvent<T>) + 'static,
3113{
3114 let windows = runtime.context.main_thread.windows.clone();
3115 let window_id_map = runtime.context.window_id_map.clone();
3116 let web_context = runtime.context.main_thread.web_context.clone();
3117 let plugins = runtime.context.plugins.clone();
3118
3119 #[cfg(feature = "tracing")]
3120 let active_tracing_spans = runtime.context.main_thread.active_tracing_spans.clone();
3121 let proxy = runtime.event_loop.create_proxy();
3122
3123 move |event, event_loop, control_flow| {
3124 for p in plugins.lock().unwrap().iter_mut() {
3125 let prevent_default = p.on_event(
3126 &event,
3127 event_loop,
3128 &proxy,
3129 control_flow,
3130 EventLoopIterationContext {
3131 callback: &mut callback,
3132 window_id_map: window_id_map.clone(),
3133 windows: windows.clone(),
3134 #[cfg(feature = "tracing")]
3135 active_tracing_spans: active_tracing_spans.clone(),
3136 },
3137 &web_context,
3138 );
3139 if prevent_default {
3140 return;
3141 }
3142 }
3143 handle_event_loop(
3144 event,
3145 event_loop,
3146 control_flow,
3147 EventLoopIterationContext {
3148 callback: &mut callback,
3149 window_id_map: window_id_map.clone(),
3150 windows: windows.clone(),
3151 #[cfg(feature = "tracing")]
3152 active_tracing_spans: active_tracing_spans.clone(),
3153 },
3154 );
3155 }
3156}
3157
3158pub struct EventLoopIterationContext<'a, T: UserEvent> {
3159 pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
3160 pub window_id_map: WindowIdStore,
3161 pub windows: Arc<WindowsStore>,
3162 #[cfg(feature = "tracing")]
3163 pub active_tracing_spans: ActiveTraceSpanStore,
3164}
3165
3166struct UserMessageContext {
3167 windows: Arc<WindowsStore>,
3168 window_id_map: WindowIdStore,
3169}
3170
3171fn handle_user_message<T: UserEvent>(
3172 event_loop: &EventLoopWindowTarget<Message<T>>,
3173 message: Message<T>,
3174 context: UserMessageContext,
3175) {
3176 let UserMessageContext {
3177 window_id_map,
3178 windows,
3179 } = context;
3180 match message {
3181 Message::Task(task) => task(),
3182 #[cfg(target_os = "macos")]
3183 Message::SetActivationPolicy(activation_policy) => {
3184 event_loop.set_activation_policy_at_runtime(tao_activation_policy(activation_policy))
3185 }
3186 #[cfg(target_os = "macos")]
3187 Message::SetDockVisibility(visible) => event_loop.set_dock_visibility(visible),
3188 Message::RequestExit(_code) => panic!("cannot handle RequestExit on the main thread"),
3189 Message::Application(application_message) => match application_message {
3190 #[cfg(target_os = "macos")]
3191 ApplicationMessage::Show => {
3192 event_loop.show_application();
3193 }
3194 #[cfg(target_os = "macos")]
3195 ApplicationMessage::Hide => {
3196 event_loop.hide_application();
3197 }
3198 #[cfg(any(target_os = "macos", target_os = "ios"))]
3199 ApplicationMessage::FetchDataStoreIdentifiers(cb) => {
3200 if let Err(e) = WebView::fetch_data_store_identifiers(cb) {
3201 log::error!("failed to fetch data store identifiers: {e}");
3204 }
3205 }
3206 #[cfg(any(target_os = "macos", target_os = "ios"))]
3207 ApplicationMessage::RemoveDataStore(uuid, cb) => {
3208 WebView::remove_data_store(&uuid, move |res| {
3209 cb(res.map_err(|_| Error::FailedToRemoveDataStore))
3210 })
3211 }
3212 },
3213 Message::Window(id, window_message) => {
3214 let w = windows.0.borrow().get(&id).map(|w| {
3215 (
3216 w.inner.clone(),
3217 w.webviews.clone(),
3218 w.has_children.load(Ordering::Relaxed),
3219 w.window_event_listeners.clone(),
3220 )
3221 });
3222 if let Some((Some(window), webviews, has_children, window_event_listeners)) = w {
3223 match window_message {
3224 WindowMessage::AddEventListener(id, listener) => {
3225 window_event_listeners.lock().unwrap().insert(id, listener);
3226 }
3227
3228 WindowMessage::ScaleFactor(tx) => tx.send(window.scale_factor()).unwrap(),
3230 WindowMessage::InnerPosition(tx) => tx
3231 .send(
3232 window
3233 .inner_position()
3234 .map(|p| PhysicalPositionWrapper(p).into())
3235 .map_err(|_| Error::FailedToSendMessage),
3236 )
3237 .unwrap(),
3238 WindowMessage::OuterPosition(tx) => tx
3239 .send(
3240 window
3241 .outer_position()
3242 .map(|p| PhysicalPositionWrapper(p).into())
3243 .map_err(|_| Error::FailedToSendMessage),
3244 )
3245 .unwrap(),
3246 WindowMessage::InnerSize(tx) => tx
3247 .send(PhysicalSizeWrapper(inner_size(&window, &webviews, has_children)).into())
3248 .unwrap(),
3249 WindowMessage::OuterSize(tx) => tx
3250 .send(PhysicalSizeWrapper(window.outer_size()).into())
3251 .unwrap(),
3252 WindowMessage::IsFullscreen(tx) => tx.send(window.fullscreen().is_some()).unwrap(),
3253 WindowMessage::IsMinimized(tx) => tx.send(window.is_minimized()).unwrap(),
3254 WindowMessage::IsMaximized(tx) => tx.send(window.is_maximized()).unwrap(),
3255 WindowMessage::IsFocused(tx) => tx.send(window.is_focused()).unwrap(),
3256 WindowMessage::IsDecorated(tx) => tx.send(window.is_decorated()).unwrap(),
3257 WindowMessage::IsResizable(tx) => tx.send(window.is_resizable()).unwrap(),
3258 WindowMessage::IsMaximizable(tx) => tx.send(window.is_maximizable()).unwrap(),
3259 WindowMessage::IsMinimizable(tx) => tx.send(window.is_minimizable()).unwrap(),
3260 WindowMessage::IsClosable(tx) => tx.send(window.is_closable()).unwrap(),
3261 WindowMessage::IsVisible(tx) => tx.send(window.is_visible()).unwrap(),
3262 WindowMessage::Title(tx) => tx.send(window.title()).unwrap(),
3263 WindowMessage::CurrentMonitor(tx) => tx.send(window.current_monitor()).unwrap(),
3264 WindowMessage::PrimaryMonitor(tx) => tx.send(window.primary_monitor()).unwrap(),
3265 WindowMessage::MonitorFromPoint(tx, (x, y)) => {
3266 tx.send(window.monitor_from_point(x, y)).unwrap()
3267 }
3268 WindowMessage::AvailableMonitors(tx) => {
3269 tx.send(window.available_monitors().collect()).unwrap()
3270 }
3271 #[cfg(any(
3272 target_os = "linux",
3273 target_os = "dragonfly",
3274 target_os = "freebsd",
3275 target_os = "netbsd",
3276 target_os = "openbsd"
3277 ))]
3278 WindowMessage::GtkWindow(tx) => tx.send(GtkWindow(window.gtk_window().clone())).unwrap(),
3279 #[cfg(any(
3280 target_os = "linux",
3281 target_os = "dragonfly",
3282 target_os = "freebsd",
3283 target_os = "netbsd",
3284 target_os = "openbsd"
3285 ))]
3286 WindowMessage::GtkBox(tx) => tx
3287 .send(GtkBox(window.default_vbox().unwrap().clone()))
3288 .unwrap(),
3289 WindowMessage::RawWindowHandle(tx) => tx
3290 .send(
3291 window
3292 .window_handle()
3293 .map(|h| SendRawWindowHandle(h.as_raw())),
3294 )
3295 .unwrap(),
3296 WindowMessage::Theme(tx) => {
3297 tx.send(map_theme(&window.theme())).unwrap();
3298 }
3299 WindowMessage::IsEnabled(tx) => tx.send(window.is_enabled()).unwrap(),
3300 WindowMessage::IsAlwaysOnTop(tx) => tx.send(window.is_always_on_top()).unwrap(),
3301 WindowMessage::Center => window.center(),
3303 WindowMessage::RequestUserAttention(request_type) => {
3304 window.request_user_attention(request_type.map(|r| r.0));
3305 }
3306 WindowMessage::SetResizable(resizable) => {
3307 window.set_resizable(resizable);
3308 #[cfg(windows)]
3309 if !resizable {
3310 undecorated_resizing::detach_resize_handler(window.hwnd());
3311 } else if !window.is_decorated() {
3312 undecorated_resizing::attach_resize_handler(
3313 window.hwnd(),
3314 window.has_undecorated_shadow(),
3315 );
3316 }
3317 }
3318 WindowMessage::SetMaximizable(maximizable) => window.set_maximizable(maximizable),
3319 WindowMessage::SetMinimizable(minimizable) => window.set_minimizable(minimizable),
3320 WindowMessage::SetClosable(closable) => window.set_closable(closable),
3321 WindowMessage::SetTitle(title) => window.set_title(&title),
3322 WindowMessage::Maximize => window.set_maximized(true),
3323 WindowMessage::Unmaximize => window.set_maximized(false),
3324 WindowMessage::Minimize => window.set_minimized(true),
3325 WindowMessage::Unminimize => window.set_minimized(false),
3326 WindowMessage::SetEnabled(enabled) => window.set_enabled(enabled),
3327 WindowMessage::Show => window.set_visible(true),
3328 WindowMessage::Hide => window.set_visible(false),
3329 WindowMessage::Close => {
3330 panic!("cannot handle `WindowMessage::Close` on the main thread")
3331 }
3332 WindowMessage::Destroy => {
3333 panic!("cannot handle `WindowMessage::Destroy` on the main thread")
3334 }
3335 WindowMessage::SetDecorations(decorations) => {
3336 window.set_decorations(decorations);
3337 #[cfg(windows)]
3338 if decorations {
3339 undecorated_resizing::detach_resize_handler(window.hwnd());
3340 } else if window.is_resizable() {
3341 undecorated_resizing::attach_resize_handler(
3342 window.hwnd(),
3343 window.has_undecorated_shadow(),
3344 );
3345 }
3346 }
3347 WindowMessage::SetShadow(_enable) => {
3348 #[cfg(windows)]
3349 {
3350 window.set_undecorated_shadow(_enable);
3351 undecorated_resizing::update_drag_hwnd_rgn_for_undecorated(window.hwnd(), _enable);
3352 }
3353 #[cfg(target_os = "macos")]
3354 window.set_has_shadow(_enable);
3355 }
3356 WindowMessage::SetAlwaysOnBottom(always_on_bottom) => {
3357 window.set_always_on_bottom(always_on_bottom)
3358 }
3359 WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top),
3360 WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces) => {
3361 window.set_visible_on_all_workspaces(visible_on_all_workspaces)
3362 }
3363 WindowMessage::SetContentProtected(protected) => window.set_content_protection(protected),
3364 WindowMessage::SetSize(size) => {
3365 window.set_inner_size(SizeWrapper::from(size).0);
3366 }
3367 WindowMessage::SetMinSize(size) => {
3368 window.set_min_inner_size(size.map(|s| SizeWrapper::from(s).0));
3369 }
3370 WindowMessage::SetMaxSize(size) => {
3371 window.set_max_inner_size(size.map(|s| SizeWrapper::from(s).0));
3372 }
3373 WindowMessage::SetSizeConstraints(constraints) => {
3374 window.set_inner_size_constraints(tao::window::WindowSizeConstraints {
3375 min_width: constraints.min_width,
3376 min_height: constraints.min_height,
3377 max_width: constraints.max_width,
3378 max_height: constraints.max_height,
3379 });
3380 }
3381 WindowMessage::SetPosition(position) => {
3382 window.set_outer_position(PositionWrapper::from(position).0)
3383 }
3384 WindowMessage::SetFullscreen(fullscreen) => {
3385 if fullscreen {
3386 window.set_fullscreen(Some(Fullscreen::Borderless(None)))
3387 } else {
3388 window.set_fullscreen(None)
3389 }
3390 }
3391
3392 #[cfg(target_os = "macos")]
3393 WindowMessage::SetSimpleFullscreen(enable) => {
3394 window.set_simple_fullscreen(enable);
3395 }
3396
3397 WindowMessage::SetFocus => {
3398 window.set_focus();
3399 }
3400 WindowMessage::SetFocusable(focusable) => {
3401 window.set_focusable(focusable);
3402 }
3403 WindowMessage::SetIcon(icon) => {
3404 window.set_window_icon(Some(icon));
3405 }
3406 #[allow(unused_variables)]
3407 WindowMessage::SetSkipTaskbar(skip) => {
3408 #[cfg(any(windows, target_os = "linux"))]
3409 let _ = window.set_skip_taskbar(skip);
3410 }
3411 WindowMessage::SetCursorGrab(grab) => {
3412 let _ = window.set_cursor_grab(grab);
3413 }
3414 WindowMessage::SetCursorVisible(visible) => {
3415 window.set_cursor_visible(visible);
3416 }
3417 WindowMessage::SetCursorIcon(icon) => {
3418 window.set_cursor_icon(CursorIconWrapper::from(icon).0);
3419 }
3420 WindowMessage::SetCursorPosition(position) => {
3421 let _ = window.set_cursor_position(PositionWrapper::from(position).0);
3422 }
3423 WindowMessage::SetIgnoreCursorEvents(ignore) => {
3424 let _ = window.set_ignore_cursor_events(ignore);
3425 }
3426 WindowMessage::DragWindow => {
3427 let _ = window.drag_window();
3428 }
3429 WindowMessage::ResizeDragWindow(direction) => {
3430 let _ = window.drag_resize_window(match direction {
3431 tauri_runtime::ResizeDirection::East => tao::window::ResizeDirection::East,
3432 tauri_runtime::ResizeDirection::North => tao::window::ResizeDirection::North,
3433 tauri_runtime::ResizeDirection::NorthEast => tao::window::ResizeDirection::NorthEast,
3434 tauri_runtime::ResizeDirection::NorthWest => tao::window::ResizeDirection::NorthWest,
3435 tauri_runtime::ResizeDirection::South => tao::window::ResizeDirection::South,
3436 tauri_runtime::ResizeDirection::SouthEast => tao::window::ResizeDirection::SouthEast,
3437 tauri_runtime::ResizeDirection::SouthWest => tao::window::ResizeDirection::SouthWest,
3438 tauri_runtime::ResizeDirection::West => tao::window::ResizeDirection::West,
3439 });
3440 }
3441 WindowMessage::RequestRedraw => {
3442 window.request_redraw();
3443 }
3444 WindowMessage::SetBadgeCount(_count, _desktop_filename) => {
3445 #[cfg(target_os = "ios")]
3446 window.set_badge_count(
3447 _count.map_or(0, |x| x.clamp(i32::MIN as i64, i32::MAX as i64) as i32),
3448 );
3449
3450 #[cfg(target_os = "macos")]
3451 window.set_badge_label(_count.map(|x| x.to_string()));
3452
3453 #[cfg(any(
3454 target_os = "linux",
3455 target_os = "dragonfly",
3456 target_os = "freebsd",
3457 target_os = "netbsd",
3458 target_os = "openbsd"
3459 ))]
3460 window.set_badge_count(_count, _desktop_filename);
3461 }
3462 WindowMessage::SetBadgeLabel(_label) => {
3463 #[cfg(target_os = "macos")]
3464 window.set_badge_label(_label);
3465 }
3466 WindowMessage::SetOverlayIcon(_icon) => {
3467 #[cfg(windows)]
3468 window.set_overlay_icon(_icon.map(|x| x.0).as_ref());
3469 }
3470 WindowMessage::SetProgressBar(progress_state) => {
3471 window.set_progress_bar(ProgressBarStateWrapper::from(progress_state).0);
3472 }
3473 WindowMessage::SetTitleBarStyle(_style) => {
3474 #[cfg(target_os = "macos")]
3475 match _style {
3476 TitleBarStyle::Visible => {
3477 window.set_titlebar_transparent(false);
3478 window.set_fullsize_content_view(true);
3479 }
3480 TitleBarStyle::Transparent => {
3481 window.set_titlebar_transparent(true);
3482 window.set_fullsize_content_view(false);
3483 }
3484 TitleBarStyle::Overlay => {
3485 window.set_titlebar_transparent(true);
3486 window.set_fullsize_content_view(true);
3487 }
3488 unknown => {
3489 #[cfg(feature = "tracing")]
3490 tracing::warn!("unknown title bar style applied: {unknown}");
3491
3492 #[cfg(not(feature = "tracing"))]
3493 eprintln!("unknown title bar style applied: {unknown}");
3494 }
3495 };
3496 }
3497 WindowMessage::SetTrafficLightPosition(_position) => {
3498 #[cfg(target_os = "macos")]
3499 window.set_traffic_light_inset(_position);
3500 }
3501 WindowMessage::SetTheme(theme) => {
3502 window.set_theme(to_tao_theme(theme));
3503 }
3504 WindowMessage::SetBackgroundColor(color) => {
3505 window.set_background_color(color.map(Into::into))
3506 }
3507 }
3508 }
3509 }
3510 Message::Webview(window_id, webview_id, webview_message) => {
3511 #[cfg(any(
3512 target_os = "macos",
3513 windows,
3514 target_os = "linux",
3515 target_os = "dragonfly",
3516 target_os = "freebsd",
3517 target_os = "netbsd",
3518 target_os = "openbsd"
3519 ))]
3520 if let WebviewMessage::Reparent(new_parent_window_id, tx) = webview_message {
3521 let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| {
3522 w.webviews
3523 .iter()
3524 .position(|w| w.id == webview_id)
3525 .map(|webview_index| w.webviews.remove(webview_index))
3526 });
3527
3528 if let Some(webview) = webview_handle {
3529 if let Some((Some(new_parent_window), new_parent_window_webviews)) = windows
3530 .0
3531 .borrow_mut()
3532 .get_mut(&new_parent_window_id)
3533 .map(|w| (w.inner.clone(), &mut w.webviews))
3534 {
3535 #[cfg(target_os = "macos")]
3536 let reparent_result = {
3537 use wry::WebViewExtMacOS;
3538 webview.inner.reparent(new_parent_window.ns_window() as _)
3539 };
3540 #[cfg(windows)]
3541 let reparent_result = { webview.inner.reparent(new_parent_window.hwnd()) };
3542
3543 #[cfg(any(
3544 target_os = "linux",
3545 target_os = "dragonfly",
3546 target_os = "freebsd",
3547 target_os = "netbsd",
3548 target_os = "openbsd"
3549 ))]
3550 let reparent_result = {
3551 if let Some(container) = new_parent_window.default_vbox() {
3552 webview.inner.reparent(container)
3553 } else {
3554 Err(wry::Error::MessageSender)
3555 }
3556 };
3557
3558 match reparent_result {
3559 Ok(_) => {
3560 new_parent_window_webviews.push(webview);
3561 tx.send(Ok(())).unwrap();
3562 }
3563 Err(e) => {
3564 log::error!("failed to reparent webview: {e}");
3565 tx.send(Err(Error::FailedToSendMessage)).unwrap();
3566 }
3567 }
3568 }
3569 } else {
3570 tx.send(Err(Error::FailedToSendMessage)).unwrap();
3571 }
3572
3573 return;
3574 }
3575
3576 let webview_handle = windows.0.borrow().get(&window_id).map(|w| {
3577 (
3578 w.inner.clone(),
3579 w.webviews.iter().find(|w| w.id == webview_id).cloned(),
3580 )
3581 });
3582 if let Some((Some(window), Some(webview))) = webview_handle {
3583 match webview_message {
3584 WebviewMessage::WebviewEvent(_) => { }
3585 WebviewMessage::SynthesizedWindowEvent(_) => { }
3586 WebviewMessage::Reparent(_window_id, _tx) => { }
3587 WebviewMessage::AddEventListener(id, listener) => {
3588 webview
3589 .webview_event_listeners
3590 .lock()
3591 .unwrap()
3592 .insert(id, listener);
3593 }
3594
3595 #[cfg(all(feature = "tracing", not(target_os = "android")))]
3596 WebviewMessage::EvaluateScript(script, tx, span) => {
3597 let _span = span.entered();
3598 if let Err(e) = webview.evaluate_script(&script) {
3599 log::error!("{e}");
3600 }
3601 tx.send(()).unwrap();
3602 }
3603 #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
3604 WebviewMessage::EvaluateScript(script) => {
3605 if let Err(e) = webview.evaluate_script(&script) {
3606 log::error!("{e}");
3607 }
3608 }
3609 WebviewMessage::Navigate(url) => {
3610 if let Err(e) = webview.load_url(url.as_str()) {
3611 log::error!("failed to navigate to url {}: {}", url, e);
3612 }
3613 }
3614 WebviewMessage::Reload => {
3615 if let Err(e) = webview.reload() {
3616 log::error!("failed to reload: {e}");
3617 }
3618 }
3619 WebviewMessage::Show => {
3620 if let Err(e) = webview.set_visible(true) {
3621 log::error!("failed to change webview visibility: {e}");
3622 }
3623 }
3624 WebviewMessage::Hide => {
3625 if let Err(e) = webview.set_visible(false) {
3626 log::error!("failed to change webview visibility: {e}");
3627 }
3628 }
3629 WebviewMessage::Print => {
3630 let _ = webview.print();
3631 }
3632 WebviewMessage::Close => {
3633 #[allow(unknown_lints, clippy::manual_inspect)]
3634 windows.0.borrow_mut().get_mut(&window_id).map(|window| {
3635 if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) {
3636 window.webviews.remove(i);
3637 }
3638 window
3639 });
3640 }
3641 WebviewMessage::SetBounds(bounds) => {
3642 let bounds: RectWrapper = bounds.into();
3643 let bounds = bounds.0;
3644
3645 if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3646 let scale_factor = window.scale_factor();
3647 let size = bounds.size.to_logical::<f32>(scale_factor);
3648 let position = bounds.position.to_logical::<f32>(scale_factor);
3649 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3650 b.width_rate = size.width / window_size.width;
3651 b.height_rate = size.height / window_size.height;
3652 b.x_rate = position.x / window_size.width;
3653 b.y_rate = position.y / window_size.height;
3654 }
3655
3656 if let Err(e) = webview.set_bounds(bounds) {
3657 log::error!("failed to set webview size: {e}");
3658 }
3659 }
3660 WebviewMessage::SetSize(size) => match webview.bounds() {
3661 Ok(mut bounds) => {
3662 bounds.size = size;
3663
3664 let scale_factor = window.scale_factor();
3665 let size = size.to_logical::<f32>(scale_factor);
3666
3667 if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3668 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3669 b.width_rate = size.width / window_size.width;
3670 b.height_rate = size.height / window_size.height;
3671 }
3672
3673 if let Err(e) = webview.set_bounds(bounds) {
3674 log::error!("failed to set webview size: {e}");
3675 }
3676 }
3677 Err(e) => {
3678 log::error!("failed to get webview bounds: {e}");
3679 }
3680 },
3681 WebviewMessage::SetPosition(position) => match webview.bounds() {
3682 Ok(mut bounds) => {
3683 bounds.position = position;
3684
3685 let scale_factor = window.scale_factor();
3686 let position = position.to_logical::<f32>(scale_factor);
3687
3688 if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3689 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3690 b.x_rate = position.x / window_size.width;
3691 b.y_rate = position.y / window_size.height;
3692 }
3693
3694 if let Err(e) = webview.set_bounds(bounds) {
3695 log::error!("failed to set webview position: {e}");
3696 }
3697 }
3698 Err(e) => {
3699 log::error!("failed to get webview bounds: {e}");
3700 }
3701 },
3702 WebviewMessage::SetZoom(scale_factor) => {
3703 if let Err(e) = webview.zoom(scale_factor) {
3704 log::error!("failed to set webview zoom: {e}");
3705 }
3706 }
3707 WebviewMessage::SetBackgroundColor(color) => {
3708 if let Err(e) =
3709 webview.set_background_color(color.map(Into::into).unwrap_or((255, 255, 255, 255)))
3710 {
3711 log::error!("failed to set webview background color: {e}");
3712 }
3713 }
3714 WebviewMessage::ClearAllBrowsingData => {
3715 if let Err(e) = webview.clear_all_browsing_data() {
3716 log::error!("failed to clear webview browsing data: {e}");
3717 }
3718 }
3719 WebviewMessage::Url(tx) => {
3721 tx.send(
3722 webview
3723 .url()
3724 .map(|u| u.parse().expect("invalid webview URL"))
3725 .map_err(|_| Error::FailedToSendMessage),
3726 )
3727 .unwrap();
3728 }
3729
3730 WebviewMessage::Cookies(tx) => {
3731 tx.send(webview.cookies().map_err(|_| Error::FailedToSendMessage))
3732 .unwrap();
3733 }
3734
3735 WebviewMessage::SetCookie(cookie) => {
3736 if let Err(e) = webview.set_cookie(&cookie) {
3737 log::error!("failed to set webview cookie: {e}");
3738 }
3739 }
3740
3741 WebviewMessage::DeleteCookie(cookie) => {
3742 if let Err(e) = webview.delete_cookie(&cookie) {
3743 log::error!("failed to delete webview cookie: {e}");
3744 }
3745 }
3746
3747 WebviewMessage::CookiesForUrl(url, tx) => {
3748 let webview_cookies = webview
3749 .cookies_for_url(url.as_str())
3750 .map_err(|_| Error::FailedToSendMessage);
3751 tx.send(webview_cookies).unwrap();
3752 }
3753
3754 WebviewMessage::Bounds(tx) => {
3755 tx.send(
3756 webview
3757 .bounds()
3758 .map(|bounds| tauri_runtime::dpi::Rect {
3759 size: bounds.size,
3760 position: bounds.position,
3761 })
3762 .map_err(|_| Error::FailedToSendMessage),
3763 )
3764 .unwrap();
3765 }
3766 WebviewMessage::Position(tx) => {
3767 tx.send(
3768 webview
3769 .bounds()
3770 .map(|bounds| bounds.position.to_physical(window.scale_factor()))
3771 .map_err(|_| Error::FailedToSendMessage),
3772 )
3773 .unwrap();
3774 }
3775 WebviewMessage::Size(tx) => {
3776 tx.send(
3777 webview
3778 .bounds()
3779 .map(|bounds| bounds.size.to_physical(window.scale_factor()))
3780 .map_err(|_| Error::FailedToSendMessage),
3781 )
3782 .unwrap();
3783 }
3784 WebviewMessage::SetFocus => {
3785 if let Err(e) = webview.focus() {
3786 log::error!("failed to focus webview: {e}");
3787 }
3788 }
3789 WebviewMessage::SetAutoResize(auto_resize) => match webview.bounds() {
3790 Ok(bounds) => {
3791 let scale_factor = window.scale_factor();
3792 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3793 *webview.bounds.lock().unwrap() = if auto_resize {
3794 let size = bounds.size.to_logical::<f32>(scale_factor);
3795 let position = bounds.position.to_logical::<f32>(scale_factor);
3796 Some(WebviewBounds {
3797 x_rate: position.x / window_size.width,
3798 y_rate: position.y / window_size.height,
3799 width_rate: size.width / window_size.width,
3800 height_rate: size.height / window_size.height,
3801 })
3802 } else {
3803 None
3804 };
3805 }
3806 Err(e) => {
3807 log::error!("failed to get webview bounds: {e}");
3808 }
3809 },
3810 WebviewMessage::WithWebview(f) => {
3811 #[cfg(any(
3812 target_os = "linux",
3813 target_os = "dragonfly",
3814 target_os = "freebsd",
3815 target_os = "netbsd",
3816 target_os = "openbsd"
3817 ))]
3818 {
3819 f(webview.webview());
3820 }
3821 #[cfg(target_os = "macos")]
3822 {
3823 use wry::WebViewExtMacOS;
3824 f(Webview {
3825 webview: Retained::into_raw(webview.webview()) as *mut objc2::runtime::AnyObject
3826 as *mut std::ffi::c_void,
3827 manager: Retained::into_raw(webview.manager()) as *mut objc2::runtime::AnyObject
3828 as *mut std::ffi::c_void,
3829 ns_window: Retained::into_raw(webview.ns_window()) as *mut objc2::runtime::AnyObject
3830 as *mut std::ffi::c_void,
3831 });
3832 }
3833 #[cfg(target_os = "ios")]
3834 {
3835 use wry::WebViewExtIOS;
3836
3837 f(Webview {
3838 webview: Retained::into_raw(webview.inner.webview())
3839 as *mut objc2::runtime::AnyObject
3840 as *mut std::ffi::c_void,
3841 manager: Retained::into_raw(webview.inner.manager())
3842 as *mut objc2::runtime::AnyObject
3843 as *mut std::ffi::c_void,
3844 view_controller: window.ui_view_controller(),
3845 });
3846 }
3847 #[cfg(windows)]
3848 {
3849 f(Webview {
3850 controller: webview.controller(),
3851 environment: webview.environment(),
3852 });
3853 }
3854 #[cfg(target_os = "android")]
3855 {
3856 f(webview.handle())
3857 }
3858 }
3859 #[cfg(any(debug_assertions, feature = "devtools"))]
3860 WebviewMessage::OpenDevTools => {
3861 webview.open_devtools();
3862 }
3863 #[cfg(any(debug_assertions, feature = "devtools"))]
3864 WebviewMessage::CloseDevTools => {
3865 webview.close_devtools();
3866 }
3867 #[cfg(any(debug_assertions, feature = "devtools"))]
3868 WebviewMessage::IsDevToolsOpen(tx) => {
3869 tx.send(webview.is_devtools_open()).unwrap();
3870 }
3871 }
3872 }
3873 }
3874 Message::CreateWebview(window_id, handler) => {
3875 let window = windows
3876 .0
3877 .borrow()
3878 .get(&window_id)
3879 .map(|w| (w.inner.clone(), w.focused_webview.clone()));
3880 if let Some((Some(window), focused_webview)) = window {
3881 match handler(&window, CreateWebviewOptions { focused_webview }) {
3882 Ok(webview) => {
3883 #[allow(unknown_lints, clippy::manual_inspect)]
3884 windows.0.borrow_mut().get_mut(&window_id).map(|w| {
3885 w.webviews.push(webview);
3886 w.has_children.store(true, Ordering::Relaxed);
3887 w
3888 });
3889 }
3890 Err(e) => {
3891 log::error!("{e}");
3892 }
3893 }
3894 }
3895 }
3896 Message::CreateWindow(window_id, handler) => match handler(event_loop) {
3897 Ok(webview) => loop {
3899 if let Ok(mut windows) = windows.0.try_borrow_mut() {
3900 windows.insert(window_id, webview);
3901 break;
3902 }
3903 },
3904 Err(e) => {
3905 log::error!("{e}");
3906 }
3907 },
3908 Message::CreateRawWindow(window_id, handler, sender) => {
3909 let (label, builder) = handler();
3910
3911 #[cfg(windows)]
3912 let background_color = builder.window.background_color;
3913 #[cfg(windows)]
3914 let is_window_transparent = builder.window.transparent;
3915
3916 if let Ok(window) = builder.build(event_loop) {
3917 window_id_map.insert(window.id(), window_id);
3918
3919 let window = Arc::new(window);
3920
3921 #[cfg(windows)]
3922 let surface = if is_window_transparent {
3923 if let Ok(context) = softbuffer::Context::new(window.clone()) {
3924 if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
3925 window.draw_surface(&mut surface, background_color);
3926 Some(surface)
3927 } else {
3928 None
3929 }
3930 } else {
3931 None
3932 }
3933 } else {
3934 None
3935 };
3936
3937 windows.0.borrow_mut().insert(
3938 window_id,
3939 WindowWrapper {
3940 label,
3941 has_children: AtomicBool::new(false),
3942 inner: Some(window.clone()),
3943 window_event_listeners: Default::default(),
3944 webviews: Vec::new(),
3945 #[cfg(windows)]
3946 background_color,
3947 #[cfg(windows)]
3948 is_window_transparent,
3949 #[cfg(windows)]
3950 surface,
3951 focused_webview: Default::default(),
3952 },
3953 );
3954 sender.send(Ok(Arc::downgrade(&window))).unwrap();
3955 } else {
3956 sender.send(Err(Error::CreateWindow)).unwrap();
3957 }
3958 }
3959
3960 Message::UserEvent(_) => (),
3961 Message::EventLoopWindowTarget(message) => match message {
3962 EventLoopWindowTargetMessage::CursorPosition(sender) => {
3963 let pos = event_loop
3964 .cursor_position()
3965 .map_err(|_| Error::FailedToSendMessage);
3966 sender.send(pos).unwrap();
3967 }
3968 EventLoopWindowTargetMessage::SetTheme(theme) => {
3969 event_loop.set_theme(to_tao_theme(theme));
3970 }
3971 EventLoopWindowTargetMessage::SetDeviceEventFilter(filter) => {
3972 event_loop.set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
3973 }
3974 },
3975 }
3976}
3977
3978fn handle_event_loop<T: UserEvent>(
3979 event: Event<'_, Message<T>>,
3980 event_loop: &EventLoopWindowTarget<Message<T>>,
3981 control_flow: &mut ControlFlow,
3982 context: EventLoopIterationContext<'_, T>,
3983) {
3984 let EventLoopIterationContext {
3985 callback,
3986 window_id_map,
3987 windows,
3988 #[cfg(feature = "tracing")]
3989 active_tracing_spans,
3990 } = context;
3991 if *control_flow != ControlFlow::Exit {
3992 *control_flow = ControlFlow::Wait;
3993 }
3994
3995 match event {
3996 Event::NewEvents(StartCause::Init) => {
3997 callback(RunEvent::Ready);
3998 }
3999
4000 Event::NewEvents(StartCause::Poll) => {
4001 callback(RunEvent::Resumed);
4002 }
4003
4004 Event::MainEventsCleared => {
4005 callback(RunEvent::MainEventsCleared);
4006 }
4007
4008 Event::LoopDestroyed => {
4009 callback(RunEvent::Exit);
4010 }
4011
4012 #[cfg(windows)]
4013 Event::RedrawRequested(id) => {
4014 if let Some(window_id) = window_id_map.get(&id) {
4015 let mut windows_ref = windows.0.borrow_mut();
4016 if let Some(window) = windows_ref.get_mut(&window_id) {
4017 if window.is_window_transparent {
4018 let background_color = window.background_color;
4019 if let Some(surface) = &mut window.surface {
4020 if let Some(window) = &window.inner {
4021 window.draw_surface(surface, background_color);
4022 }
4023 }
4024 }
4025 }
4026 }
4027 }
4028
4029 #[cfg(feature = "tracing")]
4030 Event::RedrawEventsCleared => {
4031 active_tracing_spans.remove_window_draw();
4032 }
4033
4034 Event::UserEvent(Message::Webview(
4035 window_id,
4036 webview_id,
4037 WebviewMessage::WebviewEvent(event),
4038 )) => {
4039 let windows_ref = windows.0.borrow();
4040 if let Some(window) = windows_ref.get(&window_id) {
4041 if let Some(webview) = window.webviews.iter().find(|w| w.id == webview_id) {
4042 let label = webview.label.clone();
4043 let webview_event_listeners = webview.webview_event_listeners.clone();
4044
4045 drop(windows_ref);
4046
4047 callback(RunEvent::WebviewEvent {
4048 label,
4049 event: event.clone(),
4050 });
4051 let listeners = webview_event_listeners.lock().unwrap();
4052 let handlers = listeners.values();
4053 for handler in handlers {
4054 handler(&event);
4055 }
4056 }
4057 }
4058 }
4059
4060 Event::UserEvent(Message::Webview(
4061 window_id,
4062 _webview_id,
4063 WebviewMessage::SynthesizedWindowEvent(event),
4064 )) => {
4065 if let Some(event) = WindowEventWrapper::from(event).0 {
4066 let windows_ref = windows.0.borrow();
4067 let window = windows_ref.get(&window_id);
4068 if let Some(window) = window {
4069 let label = window.label.clone();
4070 let window_event_listeners = window.window_event_listeners.clone();
4071
4072 drop(windows_ref);
4073
4074 callback(RunEvent::WindowEvent {
4075 label,
4076 event: event.clone(),
4077 });
4078
4079 let listeners = window_event_listeners.lock().unwrap();
4080 let handlers = listeners.values();
4081 for handler in handlers {
4082 handler(&event);
4083 }
4084 }
4085 }
4086 }
4087
4088 Event::WindowEvent {
4089 event, window_id, ..
4090 } => {
4091 if let Some(window_id) = window_id_map.get(&window_id) {
4092 {
4093 let windows_ref = windows.0.borrow();
4094 if let Some(window) = windows_ref.get(&window_id) {
4095 if let Some(event) = WindowEventWrapper::parse(window, &event).0 {
4096 let label = window.label.clone();
4097 let window_event_listeners = window.window_event_listeners.clone();
4098
4099 drop(windows_ref);
4100
4101 callback(RunEvent::WindowEvent {
4102 label,
4103 event: event.clone(),
4104 });
4105 let listeners = window_event_listeners.lock().unwrap();
4106 let handlers = listeners.values();
4107 for handler in handlers {
4108 handler(&event);
4109 }
4110 }
4111 }
4112 }
4113
4114 match event {
4115 #[cfg(windows)]
4116 TaoWindowEvent::ThemeChanged(theme) => {
4117 if let Some(window) = windows.0.borrow().get(&window_id) {
4118 for webview in &window.webviews {
4119 let theme = match theme {
4120 TaoTheme::Dark => wry::Theme::Dark,
4121 TaoTheme::Light => wry::Theme::Light,
4122 _ => wry::Theme::Light,
4123 };
4124 if let Err(e) = webview.set_theme(theme) {
4125 log::error!("failed to set theme: {e}");
4126 }
4127 }
4128 }
4129 }
4130 TaoWindowEvent::CloseRequested => {
4131 on_close_requested(callback, window_id, windows);
4132 }
4133 TaoWindowEvent::Destroyed => {
4134 let removed = windows.0.borrow_mut().remove(&window_id).is_some();
4135 if removed {
4136 let is_empty = windows.0.borrow().is_empty();
4137 if is_empty {
4138 let (tx, rx) = channel();
4139 callback(RunEvent::ExitRequested { code: None, tx });
4140
4141 let recv = rx.try_recv();
4142 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
4143
4144 if !should_prevent {
4145 *control_flow = ControlFlow::Exit;
4146 }
4147 }
4148 }
4149 }
4150 TaoWindowEvent::Resized(size) => {
4151 if let Some((Some(window), webviews)) = windows
4152 .0
4153 .borrow()
4154 .get(&window_id)
4155 .map(|w| (w.inner.clone(), w.webviews.clone()))
4156 {
4157 let size = size.to_logical::<f32>(window.scale_factor());
4158 for webview in webviews {
4159 if let Some(b) = &*webview.bounds.lock().unwrap() {
4160 if let Err(e) = webview.set_bounds(wry::Rect {
4161 position: LogicalPosition::new(size.width * b.x_rate, size.height * b.y_rate)
4162 .into(),
4163 size: LogicalSize::new(size.width * b.width_rate, size.height * b.height_rate)
4164 .into(),
4165 }) {
4166 log::error!("failed to autoresize webview: {e}");
4167 }
4168 }
4169 }
4170 }
4171 }
4172 _ => {}
4173 }
4174 }
4175 }
4176 Event::UserEvent(message) => match message {
4177 Message::RequestExit(code) => {
4178 let (tx, rx) = channel();
4179 callback(RunEvent::ExitRequested {
4180 code: Some(code),
4181 tx,
4182 });
4183
4184 let recv = rx.try_recv();
4185 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
4186
4187 if !should_prevent {
4188 *control_flow = ControlFlow::Exit;
4189 }
4190 }
4191 Message::Window(id, WindowMessage::Close) => {
4192 on_close_requested(callback, id, windows);
4193 }
4194 Message::Window(id, WindowMessage::Destroy) => {
4195 on_window_close(id, windows);
4196 }
4197 Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
4198 message => {
4199 handle_user_message(
4200 event_loop,
4201 message,
4202 UserMessageContext {
4203 window_id_map,
4204 windows,
4205 },
4206 );
4207 }
4208 },
4209 #[cfg(any(target_os = "macos", target_os = "ios"))]
4210 Event::Opened { urls } => {
4211 callback(RunEvent::Opened { urls });
4212 }
4213 #[cfg(target_os = "macos")]
4214 Event::Reopen {
4215 has_visible_windows,
4216 ..
4217 } => callback(RunEvent::Reopen {
4218 has_visible_windows,
4219 }),
4220 _ => (),
4221 }
4222}
4223
4224fn on_close_requested<'a, T: UserEvent>(
4225 callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
4226 window_id: WindowId,
4227 windows: Arc<WindowsStore>,
4228) {
4229 let (tx, rx) = channel();
4230 let windows_ref = windows.0.borrow();
4231 if let Some(w) = windows_ref.get(&window_id) {
4232 let label = w.label.clone();
4233 let window_event_listeners = w.window_event_listeners.clone();
4234
4235 drop(windows_ref);
4236
4237 let listeners = window_event_listeners.lock().unwrap();
4238 let handlers = listeners.values();
4239 for handler in handlers {
4240 handler(&WindowEvent::CloseRequested {
4241 signal_tx: tx.clone(),
4242 });
4243 }
4244 callback(RunEvent::WindowEvent {
4245 label,
4246 event: WindowEvent::CloseRequested { signal_tx: tx },
4247 });
4248 if let Ok(true) = rx.try_recv() {
4249 } else {
4250 on_window_close(window_id, windows);
4251 }
4252 }
4253}
4254
4255fn on_window_close(window_id: WindowId, windows: Arc<WindowsStore>) {
4256 if let Some(window_wrapper) = windows.0.borrow_mut().get_mut(&window_id) {
4257 window_wrapper.inner = None;
4258 #[cfg(windows)]
4259 window_wrapper.surface.take();
4260 }
4261}
4262
4263fn parse_proxy_url(url: &Url) -> Result<ProxyConfig> {
4264 let host = url.host().map(|h| h.to_string()).unwrap_or_default();
4265 let port = url.port().map(|p| p.to_string()).unwrap_or_default();
4266
4267 if url.scheme() == "http" {
4268 let config = ProxyConfig::Http(ProxyEndpoint { host, port });
4269
4270 Ok(config)
4271 } else if url.scheme() == "socks5" {
4272 let config = ProxyConfig::Socks5(ProxyEndpoint { host, port });
4273
4274 Ok(config)
4275 } else {
4276 Err(Error::InvalidProxyUrl)
4277 }
4278}
4279
4280fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
4281 window_id: WindowId,
4282 webview_id: u32,
4283 event_loop: &EventLoopWindowTarget<Message<T>>,
4284 context: &Context<T>,
4285 pending: PendingWindow<T, Wry<T>>,
4286 after_window_creation: Option<F>,
4287) -> Result<WindowWrapper> {
4288 #[allow(unused_mut)]
4289 let PendingWindow {
4290 mut window_builder,
4291 label,
4292 webview,
4293 } = pending;
4294
4295 #[cfg(feature = "tracing")]
4296 let _webview_create_span = tracing::debug_span!("wry::webview::create").entered();
4297 #[cfg(feature = "tracing")]
4298 let window_draw_span = tracing::debug_span!("wry::window::draw").entered();
4299 #[cfg(feature = "tracing")]
4300 let window_create_span =
4301 tracing::debug_span!(parent: &window_draw_span, "wry::window::create").entered();
4302
4303 let window_event_listeners = WindowEventListeners::default();
4304
4305 #[cfg(windows)]
4306 let background_color = window_builder.inner.window.background_color;
4307 #[cfg(windows)]
4308 let is_window_transparent = window_builder.inner.window.transparent;
4309
4310 #[cfg(target_os = "macos")]
4311 {
4312 if window_builder.tabbing_identifier.is_none()
4313 || window_builder.inner.window.transparent
4314 || !window_builder.inner.window.decorations
4315 {
4316 window_builder.inner = window_builder.inner.with_automatic_window_tabbing(false);
4317 }
4318 }
4319
4320 #[cfg(desktop)]
4321 if window_builder.prevent_overflow.is_some() || window_builder.center {
4322 let monitor = if let Some(window_position) = &window_builder.inner.window.position {
4323 event_loop.available_monitors().find(|m| {
4324 let monitor_pos = m.position();
4325 let monitor_size = m.size();
4326
4327 let window_position = window_position.to_physical::<i32>(m.scale_factor());
4329
4330 monitor_pos.x <= window_position.x
4331 && window_position.x < monitor_pos.x + monitor_size.width as i32
4332 && monitor_pos.y <= window_position.y
4333 && window_position.y < monitor_pos.y + monitor_size.height as i32
4334 })
4335 } else {
4336 event_loop.primary_monitor()
4337 };
4338 if let Some(monitor) = monitor {
4339 let scale_factor = monitor.scale_factor();
4340 let desired_size = window_builder
4341 .inner
4342 .window
4343 .inner_size
4344 .unwrap_or_else(|| TaoPhysicalSize::new(800, 600).into());
4345 let mut inner_size = window_builder
4346 .inner
4347 .window
4348 .inner_size_constraints
4349 .clamp(desired_size, scale_factor)
4350 .to_physical::<u32>(scale_factor);
4351 let mut window_size = inner_size;
4352 #[allow(unused_mut)]
4353 let mut shadow_width = 0;
4356 #[cfg(windows)]
4357 if window_builder.inner.window.decorations {
4358 use windows::Win32::UI::WindowsAndMessaging::{AdjustWindowRect, WS_OVERLAPPEDWINDOW};
4359 let mut rect = windows::Win32::Foundation::RECT::default();
4360 let result = unsafe { AdjustWindowRect(&mut rect, WS_OVERLAPPEDWINDOW, false) };
4361 if result.is_ok() {
4362 shadow_width = (rect.right - rect.left) as u32;
4363 window_size.height += -rect.top as u32;
4365 }
4366 }
4367
4368 if let Some(margin) = window_builder.prevent_overflow {
4369 let work_area = monitor.work_area();
4370 let margin = margin.to_physical::<u32>(scale_factor);
4371 let constraint = PhysicalSize::new(
4372 work_area.size.width - margin.width,
4373 work_area.size.height - margin.height,
4374 );
4375 if window_size.width > constraint.width || window_size.height > constraint.height {
4376 if window_size.width > constraint.width {
4377 inner_size.width = inner_size
4378 .width
4379 .saturating_sub(window_size.width - constraint.width);
4380 window_size.width = constraint.width;
4381 }
4382 if window_size.height > constraint.height {
4383 inner_size.height = inner_size
4384 .height
4385 .saturating_sub(window_size.height - constraint.height);
4386 window_size.height = constraint.height;
4387 }
4388 window_builder.inner.window.inner_size = Some(inner_size.into());
4389 }
4390 }
4391
4392 if window_builder.center {
4393 window_size.width += shadow_width;
4394 let position = window::calculate_window_center_position(window_size, monitor);
4395 let logical_position = position.to_logical::<f64>(scale_factor);
4396 window_builder = window_builder.position(logical_position.x, logical_position.y);
4397 }
4398 }
4399 };
4400
4401 let window = window_builder
4402 .inner
4403 .build(event_loop)
4404 .map_err(|_| Error::CreateWindow)?;
4405
4406 #[cfg(feature = "tracing")]
4407 {
4408 drop(window_create_span);
4409
4410 context
4411 .main_thread
4412 .active_tracing_spans
4413 .0
4414 .borrow_mut()
4415 .push(ActiveTracingSpan::WindowDraw {
4416 id: window.id(),
4417 span: window_draw_span,
4418 });
4419 }
4420
4421 context.window_id_map.insert(window.id(), window_id);
4422
4423 if let Some(handler) = after_window_creation {
4424 let raw = RawWindow {
4425 #[cfg(windows)]
4426 hwnd: window.hwnd(),
4427 #[cfg(any(
4428 target_os = "linux",
4429 target_os = "dragonfly",
4430 target_os = "freebsd",
4431 target_os = "netbsd",
4432 target_os = "openbsd"
4433 ))]
4434 gtk_window: window.gtk_window(),
4435 #[cfg(any(
4436 target_os = "linux",
4437 target_os = "dragonfly",
4438 target_os = "freebsd",
4439 target_os = "netbsd",
4440 target_os = "openbsd"
4441 ))]
4442 default_vbox: window.default_vbox(),
4443 _marker: &std::marker::PhantomData,
4444 };
4445 handler(raw);
4446 }
4447
4448 let mut webviews = Vec::new();
4449
4450 let focused_webview = Arc::new(Mutex::new(None));
4451
4452 if let Some(webview) = webview {
4453 webviews.push(create_webview(
4454 #[cfg(feature = "unstable")]
4455 WebviewKind::WindowChild,
4456 #[cfg(not(feature = "unstable"))]
4457 WebviewKind::WindowContent,
4458 &window,
4459 Arc::new(Mutex::new(window_id)),
4460 webview_id,
4461 context,
4462 webview,
4463 focused_webview.clone(),
4464 )?);
4465 }
4466
4467 let window = Arc::new(window);
4468
4469 #[cfg(windows)]
4470 let surface = if is_window_transparent {
4471 if let Ok(context) = softbuffer::Context::new(window.clone()) {
4472 if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
4473 window.draw_surface(&mut surface, background_color);
4474 Some(surface)
4475 } else {
4476 None
4477 }
4478 } else {
4479 None
4480 }
4481 } else {
4482 None
4483 };
4484
4485 Ok(WindowWrapper {
4486 label,
4487 has_children: AtomicBool::new(false),
4488 inner: Some(window),
4489 webviews,
4490 window_event_listeners,
4491 #[cfg(windows)]
4492 background_color,
4493 #[cfg(windows)]
4494 is_window_transparent,
4495 #[cfg(windows)]
4496 surface,
4497 focused_webview,
4498 })
4499}
4500
4501#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
4503enum WebviewKind {
4504 WindowContent,
4506 WindowChild,
4508}
4509
4510#[derive(Debug, Clone)]
4511struct WebviewBounds {
4512 x_rate: f32,
4513 y_rate: f32,
4514 width_rate: f32,
4515 height_rate: f32,
4516}
4517
4518fn create_webview<T: UserEvent>(
4519 kind: WebviewKind,
4520 window: &Window,
4521 window_id: Arc<Mutex<WindowId>>,
4522 id: WebviewId,
4523 context: &Context<T>,
4524 pending: PendingWebview<T, Wry<T>>,
4525 #[allow(unused_variables)] focused_webview: Arc<Mutex<Option<String>>>,
4526) -> Result<WebviewWrapper> {
4527 if !context.webview_runtime_installed {
4528 #[cfg(all(not(debug_assertions), windows))]
4529 dialog::error(
4530 r#"Could not find the WebView2 Runtime.
4531
4532Make sure it is installed or download it from <A href="https://developer.microsoft.com/en-us/microsoft-edge/webview2">https://developer.microsoft.com/en-us/microsoft-edge/webview2</A>
4533
4534You may have it installed on another user account, but it is not available for this one.
4535"#,
4536 );
4537
4538 if cfg!(target_os = "macos") {
4539 log::warn!("WebKit webview runtime not found, attempting to create webview anyway.");
4540 } else {
4541 return Err(Error::WebviewRuntimeNotInstalled);
4542 }
4543 }
4544
4545 #[allow(unused_mut)]
4546 let PendingWebview {
4547 webview_attributes,
4548 uri_scheme_protocols,
4549 label,
4550 ipc_handler,
4551 url,
4552 ..
4553 } = pending;
4554
4555 let mut web_context = context
4556 .main_thread
4557 .web_context
4558 .lock()
4559 .expect("poisoned WebContext store");
4560 let is_first_context = web_context.is_empty();
4561 let automation_enabled = std::env::var("TAURI_WEBVIEW_AUTOMATION").as_deref() == Ok("true");
4563 let web_context_key = webview_attributes.data_directory;
4564 let entry = web_context.entry(web_context_key.clone());
4565 let web_context = match entry {
4566 Occupied(occupied) => {
4567 let occupied = occupied.into_mut();
4568 occupied.referenced_by_webviews.insert(label.clone());
4569 occupied
4570 }
4571 Vacant(vacant) => {
4572 let mut web_context = WryWebContext::new(web_context_key.clone());
4573 web_context.set_allows_automation(if automation_enabled {
4574 is_first_context
4575 } else {
4576 false
4577 });
4578 vacant.insert(WebContext {
4579 inner: web_context,
4580 referenced_by_webviews: [label.clone()].into(),
4581 registered_custom_protocols: HashSet::new(),
4582 })
4583 }
4584 };
4585
4586 let mut webview_builder = WebViewBuilder::new_with_web_context(&mut web_context.inner)
4587 .with_id(&label)
4588 .with_focused(webview_attributes.focus)
4589 .with_transparent(webview_attributes.transparent)
4590 .with_accept_first_mouse(webview_attributes.accept_first_mouse)
4591 .with_incognito(webview_attributes.incognito)
4592 .with_clipboard(webview_attributes.clipboard)
4593 .with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled);
4594
4595 if url != "about:blank" {
4596 webview_builder = webview_builder.with_url(&url);
4597 }
4598
4599 #[cfg(target_os = "macos")]
4600 if let Some(webview_configuration) = webview_attributes.webview_configuration {
4601 webview_builder = webview_builder.with_webview_configuration(webview_configuration);
4602 }
4603
4604 #[cfg(any(target_os = "windows", target_os = "android"))]
4605 {
4606 webview_builder = webview_builder.with_https_scheme(webview_attributes.use_https_scheme);
4607 }
4608
4609 if let Some(background_throttling) = webview_attributes.background_throttling {
4610 webview_builder = webview_builder.with_background_throttling(match background_throttling {
4611 tauri_utils::config::BackgroundThrottlingPolicy::Disabled => {
4612 wry::BackgroundThrottlingPolicy::Disabled
4613 }
4614 tauri_utils::config::BackgroundThrottlingPolicy::Suspend => {
4615 wry::BackgroundThrottlingPolicy::Suspend
4616 }
4617 tauri_utils::config::BackgroundThrottlingPolicy::Throttle => {
4618 wry::BackgroundThrottlingPolicy::Throttle
4619 }
4620 });
4621 }
4622
4623 if webview_attributes.javascript_disabled {
4624 webview_builder = webview_builder.with_javascript_disabled();
4625 }
4626
4627 if let Some(color) = webview_attributes.background_color {
4628 webview_builder = webview_builder.with_background_color(color.into());
4629 }
4630
4631 if webview_attributes.drag_drop_handler_enabled {
4632 let proxy = context.proxy.clone();
4633 let window_id_ = window_id.clone();
4634 webview_builder = webview_builder.with_drag_drop_handler(move |event| {
4635 let event = match event {
4636 WryDragDropEvent::Enter {
4637 paths,
4638 position: (x, y),
4639 } => DragDropEvent::Enter {
4640 paths,
4641 position: PhysicalPosition::new(x as _, y as _),
4642 },
4643 WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::Over {
4644 position: PhysicalPosition::new(x as _, y as _),
4645 },
4646 WryDragDropEvent::Drop {
4647 paths,
4648 position: (x, y),
4649 } => DragDropEvent::Drop {
4650 paths,
4651 position: PhysicalPosition::new(x as _, y as _),
4652 },
4653 WryDragDropEvent::Leave => DragDropEvent::Leave,
4654 _ => unimplemented!(),
4655 };
4656
4657 let message = if kind == WebviewKind::WindowContent {
4658 WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::DragDrop(event))
4659 } else {
4660 WebviewMessage::WebviewEvent(WebviewEvent::DragDrop(event))
4661 };
4662
4663 let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message));
4664 true
4665 });
4666 }
4667
4668 if let Some(navigation_handler) = pending.navigation_handler {
4669 webview_builder = webview_builder.with_navigation_handler(move |url| {
4670 url
4671 .parse()
4672 .map(|url| navigation_handler(&url))
4673 .unwrap_or(true)
4674 });
4675 }
4676
4677 if let Some(new_window_handler) = pending.new_window_handler {
4678 #[cfg(desktop)]
4679 let context = context.clone();
4680 webview_builder = webview_builder.with_new_window_req_handler(move |url, features| {
4681 url
4682 .parse()
4683 .map(|url| {
4684 let response = new_window_handler(
4685 url,
4686 tauri_runtime::webview::NewWindowFeatures::new(
4687 features.size,
4688 features.position,
4689 tauri_runtime::webview::NewWindowOpener {
4690 #[cfg(desktop)]
4691 webview: features.opener.webview,
4692 #[cfg(windows)]
4693 environment: features.opener.environment,
4694 #[cfg(target_os = "macos")]
4695 target_configuration: features.opener.target_configuration,
4696 },
4697 ),
4698 );
4699 match response {
4700 tauri_runtime::webview::NewWindowResponse::Allow => wry::NewWindowResponse::Allow,
4701 #[cfg(desktop)]
4702 tauri_runtime::webview::NewWindowResponse::Create { window_id } => {
4703 let windows = &context.main_thread.windows.0;
4704 let webview = loop {
4705 if let Some(webview) = windows.try_borrow().ok().and_then(|windows| {
4706 windows
4707 .get(&window_id)
4708 .map(|window| window.webviews.first().unwrap().clone())
4709 }) {
4710 break webview;
4711 } else {
4712 std::thread::sleep(std::time::Duration::from_millis(50));
4714 continue;
4715 };
4716 };
4717
4718 #[cfg(desktop)]
4719 wry::NewWindowResponse::Create {
4720 #[cfg(target_os = "macos")]
4721 webview: wry::WebViewExtMacOS::webview(&*webview).as_super().into(),
4722 #[cfg(any(
4723 target_os = "linux",
4724 target_os = "dragonfly",
4725 target_os = "freebsd",
4726 target_os = "netbsd",
4727 target_os = "openbsd",
4728 ))]
4729 webview: webview.webview(),
4730 #[cfg(windows)]
4731 webview: webview.webview(),
4732 }
4733 }
4734 tauri_runtime::webview::NewWindowResponse::Deny => wry::NewWindowResponse::Deny,
4735 }
4736 })
4737 .unwrap_or(wry::NewWindowResponse::Deny)
4738 });
4739 }
4740
4741 if let Some(document_title_changed_handler) = pending.document_title_changed_handler {
4742 webview_builder =
4743 webview_builder.with_document_title_changed_handler(document_title_changed_handler)
4744 }
4745
4746 let webview_bounds = if let Some(bounds) = webview_attributes.bounds {
4747 let bounds: RectWrapper = bounds.into();
4748 let bounds = bounds.0;
4749
4750 let scale_factor = window.scale_factor();
4751 let position = bounds.position.to_logical::<f32>(scale_factor);
4752 let size = bounds.size.to_logical::<f32>(scale_factor);
4753
4754 webview_builder = webview_builder.with_bounds(bounds);
4755
4756 let window_size = window.inner_size().to_logical::<f32>(scale_factor);
4757
4758 if webview_attributes.auto_resize {
4759 Some(WebviewBounds {
4760 x_rate: position.x / window_size.width,
4761 y_rate: position.y / window_size.height,
4762 width_rate: size.width / window_size.width,
4763 height_rate: size.height / window_size.height,
4764 })
4765 } else {
4766 None
4767 }
4768 } else {
4769 #[cfg(feature = "unstable")]
4770 {
4771 webview_builder = webview_builder.with_bounds(wry::Rect {
4772 position: LogicalPosition::new(0, 0).into(),
4773 size: window.inner_size().into(),
4774 });
4775 Some(WebviewBounds {
4776 x_rate: 0.,
4777 y_rate: 0.,
4778 width_rate: 1.,
4779 height_rate: 1.,
4780 })
4781 }
4782 #[cfg(not(feature = "unstable"))]
4783 None
4784 };
4785
4786 if let Some(download_handler) = pending.download_handler {
4787 let download_handler_ = download_handler.clone();
4788 webview_builder = webview_builder.with_download_started_handler(move |url, path| {
4789 if let Ok(url) = url.parse() {
4790 download_handler_(DownloadEvent::Requested {
4791 url,
4792 destination: path,
4793 })
4794 } else {
4795 false
4796 }
4797 });
4798 webview_builder = webview_builder.with_download_completed_handler(move |url, path, success| {
4799 if let Ok(url) = url.parse() {
4800 download_handler(DownloadEvent::Finished { url, path, success });
4801 }
4802 });
4803 }
4804
4805 if let Some(page_load_handler) = pending.on_page_load_handler {
4806 webview_builder = webview_builder.with_on_page_load_handler(move |event, url| {
4807 let _ = url.parse().map(|url| {
4808 page_load_handler(
4809 url,
4810 match event {
4811 wry::PageLoadEvent::Started => tauri_runtime::webview::PageLoadEvent::Started,
4812 wry::PageLoadEvent::Finished => tauri_runtime::webview::PageLoadEvent::Finished,
4813 },
4814 )
4815 });
4816 });
4817 }
4818
4819 if let Some(user_agent) = webview_attributes.user_agent {
4820 webview_builder = webview_builder.with_user_agent(&user_agent);
4821 }
4822
4823 if let Some(proxy_url) = webview_attributes.proxy_url {
4824 let config = parse_proxy_url(&proxy_url)?;
4825
4826 webview_builder = webview_builder.with_proxy_config(config);
4827 }
4828
4829 #[cfg(windows)]
4830 {
4831 if let Some(additional_browser_args) = webview_attributes.additional_browser_args {
4832 webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args);
4833 }
4834
4835 if let Some(environment) = webview_attributes.environment {
4836 webview_builder = webview_builder.with_environment(environment);
4837 }
4838
4839 webview_builder = webview_builder.with_theme(match window.theme() {
4840 TaoTheme::Dark => wry::Theme::Dark,
4841 TaoTheme::Light => wry::Theme::Light,
4842 _ => wry::Theme::Light,
4843 });
4844
4845 webview_builder =
4846 webview_builder.with_scroll_bar_style(match webview_attributes.scroll_bar_style {
4847 ScrollBarStyle::Default => WryScrollBarStyle::Default,
4848 ScrollBarStyle::FluentOverlay => WryScrollBarStyle::FluentOverlay,
4849 _ => unreachable!(),
4850 });
4851 }
4852
4853 #[cfg(windows)]
4854 {
4855 webview_builder = webview_builder
4856 .with_browser_extensions_enabled(webview_attributes.browser_extensions_enabled);
4857 }
4858
4859 #[cfg(any(
4860 windows,
4861 target_os = "linux",
4862 target_os = "dragonfly",
4863 target_os = "freebsd",
4864 target_os = "netbsd",
4865 target_os = "openbsd"
4866 ))]
4867 {
4868 if let Some(path) = &webview_attributes.extensions_path {
4869 webview_builder = webview_builder.with_extensions_path(path);
4870 }
4871 }
4872
4873 #[cfg(any(
4874 target_os = "linux",
4875 target_os = "dragonfly",
4876 target_os = "freebsd",
4877 target_os = "netbsd",
4878 target_os = "openbsd"
4879 ))]
4880 {
4881 if let Some(related_view) = webview_attributes.related_view {
4882 webview_builder = webview_builder.with_related_view(related_view);
4883 }
4884 }
4885
4886 #[cfg(any(target_os = "macos", target_os = "ios"))]
4887 {
4888 if let Some(data_store_identifier) = &webview_attributes.data_store_identifier {
4889 webview_builder = webview_builder.with_data_store_identifier(*data_store_identifier);
4890 }
4891
4892 webview_builder =
4893 webview_builder.with_allow_link_preview(webview_attributes.allow_link_preview);
4894 }
4895
4896 #[cfg(target_os = "ios")]
4897 {
4898 if let Some(input_accessory_view_builder) = webview_attributes.input_accessory_view_builder {
4899 webview_builder = webview_builder
4900 .with_input_accessory_view_builder(move |webview| input_accessory_view_builder.0(webview));
4901 }
4902 }
4903
4904 #[cfg(target_os = "macos")]
4905 {
4906 if let Some(position) = &webview_attributes.traffic_light_position {
4907 webview_builder = webview_builder.with_traffic_light_inset(*position);
4908 }
4909 }
4910
4911 webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
4912 kind,
4913 window_id.clone(),
4914 id,
4915 context.clone(),
4916 label.clone(),
4917 ipc_handler,
4918 ));
4919
4920 for script in webview_attributes.initialization_scripts {
4921 webview_builder = webview_builder
4922 .with_initialization_script_for_main_only(script.script, script.for_main_frame_only);
4923 }
4924
4925 for (scheme, protocol) in uri_scheme_protocols {
4926 #[cfg(any(
4929 target_os = "linux",
4930 target_os = "dragonfly",
4931 target_os = "freebsd",
4932 target_os = "netbsd",
4933 target_os = "openbsd"
4934 ))]
4935 {
4936 if web_context.registered_custom_protocols.contains(&scheme) {
4937 continue;
4938 }
4939
4940 web_context
4941 .registered_custom_protocols
4942 .insert(scheme.clone());
4943 }
4944
4945 webview_builder = webview_builder.with_asynchronous_custom_protocol(
4946 scheme,
4947 move |webview_id, request, responder| {
4948 protocol(
4949 webview_id,
4950 request,
4951 Box::new(move |response| responder.respond(response)),
4952 )
4953 },
4954 );
4955 }
4956
4957 #[cfg(any(debug_assertions, feature = "devtools"))]
4958 {
4959 webview_builder = webview_builder.with_devtools(webview_attributes.devtools.unwrap_or(true));
4960 }
4961
4962 #[cfg(target_os = "android")]
4963 {
4964 if let Some(on_webview_created) = pending.on_webview_created {
4965 webview_builder = webview_builder.on_webview_created(move |ctx| {
4966 on_webview_created(tauri_runtime::webview::CreationContext {
4967 env: ctx.env,
4968 activity: ctx.activity,
4969 webview: ctx.webview,
4970 })
4971 });
4972 }
4973 }
4974
4975 let webview = match kind {
4976 #[cfg(not(any(
4977 target_os = "windows",
4978 target_os = "macos",
4979 target_os = "ios",
4980 target_os = "android"
4981 )))]
4982 WebviewKind::WindowChild => {
4983 let vbox = window.default_vbox().unwrap();
4985 webview_builder.build_gtk(vbox)
4986 }
4987 #[cfg(any(
4988 target_os = "windows",
4989 target_os = "macos",
4990 target_os = "ios",
4991 target_os = "android"
4992 ))]
4993 WebviewKind::WindowChild => webview_builder.build_as_child(&window),
4994 WebviewKind::WindowContent => {
4995 #[cfg(any(
4996 target_os = "windows",
4997 target_os = "macos",
4998 target_os = "ios",
4999 target_os = "android"
5000 ))]
5001 let builder = webview_builder.build(&window);
5002 #[cfg(not(any(
5003 target_os = "windows",
5004 target_os = "macos",
5005 target_os = "ios",
5006 target_os = "android"
5007 )))]
5008 let builder = {
5009 let vbox = window.default_vbox().unwrap();
5010 webview_builder.build_gtk(vbox)
5011 };
5012 builder
5013 }
5014 }
5015 .map_err(|e| Error::CreateWebview(Box::new(e)))?;
5016
5017 if kind == WebviewKind::WindowContent {
5018 #[cfg(any(
5019 target_os = "linux",
5020 target_os = "dragonfly",
5021 target_os = "freebsd",
5022 target_os = "netbsd",
5023 target_os = "openbsd"
5024 ))]
5025 undecorated_resizing::attach_resize_handler(&webview);
5026 #[cfg(windows)]
5027 if window.is_resizable() && !window.is_decorated() {
5028 undecorated_resizing::attach_resize_handler(window.hwnd(), window.has_undecorated_shadow());
5029 }
5030 }
5031
5032 #[cfg(windows)]
5033 {
5034 let controller = webview.controller();
5035 let proxy_clone = context.proxy.clone();
5036 let window_id_ = window_id.clone();
5037 let mut token = 0;
5038 unsafe {
5039 let label_ = label.clone();
5040 let focused_webview_ = focused_webview.clone();
5041 controller.add_GotFocus(
5042 &FocusChangedEventHandler::create(Box::new(move |_, _| {
5043 let mut focused_webview = focused_webview_.lock().unwrap();
5044 let already_focused = focused_webview.is_some();
5047 focused_webview.replace(label_.clone());
5048
5049 if !already_focused {
5050 let _ = proxy_clone.send_event(Message::Webview(
5051 *window_id_.lock().unwrap(),
5052 id,
5053 WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)),
5054 ));
5055 }
5056 Ok(())
5057 })),
5058 &mut token,
5059 )
5060 }
5061 .unwrap();
5062 unsafe {
5063 let label_ = label.clone();
5064 let window_id_ = window_id.clone();
5065 let proxy_clone = context.proxy.clone();
5066 controller.add_LostFocus(
5067 &FocusChangedEventHandler::create(Box::new(move |_, _| {
5068 let mut focused_webview = focused_webview.lock().unwrap();
5069 let lost_window_focus = focused_webview.as_ref().map_or(true, |w| w == &label_);
5077
5078 if lost_window_focus {
5079 *focused_webview = None;
5081 let _ = proxy_clone.send_event(Message::Webview(
5082 *window_id_.lock().unwrap(),
5083 id,
5084 WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)),
5085 ));
5086 }
5087 Ok(())
5088 })),
5089 &mut token,
5090 )
5091 }
5092 .unwrap();
5093
5094 if let Ok(webview) = unsafe { controller.CoreWebView2() } {
5095 let proxy_clone = context.proxy.clone();
5096 unsafe {
5097 let _ = webview.add_ContainsFullScreenElementChanged(
5098 &ContainsFullScreenElementChangedEventHandler::create(Box::new(move |sender, _| {
5099 let mut contains_fullscreen_element = windows::core::BOOL::default();
5100 sender
5101 .ok_or_else(windows::core::Error::empty)?
5102 .ContainsFullScreenElement(&mut contains_fullscreen_element)?;
5103 let _ = proxy_clone.send_event(Message::Window(
5104 *window_id.lock().unwrap(),
5105 WindowMessage::SetFullscreen(contains_fullscreen_element.as_bool()),
5106 ));
5107 Ok(())
5108 })),
5109 &mut token,
5110 );
5111 }
5112 }
5113 }
5114
5115 Ok(WebviewWrapper {
5116 label,
5117 id,
5118 inner: Rc::new(webview),
5119 context_store: context.main_thread.web_context.clone(),
5120 webview_event_listeners: Default::default(),
5121 context_key: if automation_enabled {
5122 None
5123 } else {
5124 web_context_key
5125 },
5126 bounds: Arc::new(Mutex::new(webview_bounds)),
5127 })
5128}
5129
5130fn create_ipc_handler<T: UserEvent>(
5132 _kind: WebviewKind,
5133 window_id: Arc<Mutex<WindowId>>,
5134 webview_id: WebviewId,
5135 context: Context<T>,
5136 label: String,
5137 ipc_handler: Option<WebviewIpcHandler<T, Wry<T>>>,
5138) -> Box<IpcHandler> {
5139 Box::new(move |request| {
5140 if let Some(handler) = &ipc_handler {
5141 handler(
5142 DetachedWebview {
5143 label: label.clone(),
5144 dispatcher: WryWebviewDispatcher {
5145 window_id: window_id.clone(),
5146 webview_id,
5147 context: context.clone(),
5148 },
5149 },
5150 request,
5151 );
5152 }
5153 })
5154}
5155
5156#[cfg(target_os = "macos")]
5157fn inner_size(
5158 window: &Window,
5159 webviews: &[WebviewWrapper],
5160 has_children: bool,
5161) -> TaoPhysicalSize<u32> {
5162 if !has_children && !webviews.is_empty() {
5163 use wry::WebViewExtMacOS;
5164 let webview = webviews.first().unwrap();
5165 let view = unsafe { Retained::cast_unchecked::<objc2_app_kit::NSView>(webview.webview()) };
5166 let view_frame = view.frame();
5167 let logical: TaoLogicalSize<f64> = (view_frame.size.width, view_frame.size.height).into();
5168 return logical.to_physical(window.scale_factor());
5169 }
5170
5171 window.inner_size()
5172}
5173
5174#[cfg(not(target_os = "macos"))]
5175#[allow(unused_variables)]
5176fn inner_size(
5177 window: &Window,
5178 webviews: &[WebviewWrapper],
5179 has_children: bool,
5180) -> TaoPhysicalSize<u32> {
5181 window.inner_size()
5182}
5183
5184fn to_tao_theme(theme: Option<Theme>) -> Option<TaoTheme> {
5185 match theme {
5186 Some(Theme::Light) => Some(TaoTheme::Light),
5187 Some(Theme::Dark) => Some(TaoTheme::Dark),
5188 _ => None,
5189 }
5190}