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