@@ -5,14 +5,15 @@ use crate::{
5
5
ipc:: UserWindowEvent ,
6
6
query:: QueryEngine ,
7
7
shortcut:: { HotKey , HotKeyState , ShortcutHandle , ShortcutRegistryError } ,
8
- webview:: WebviewInstance ,
8
+ webview:: PendingWebview ,
9
9
AssetRequest , Config , WryEventHandler ,
10
10
} ;
11
- use dioxus_core:: {
12
- prelude:: { Callback , ScopeId } ,
13
- VirtualDom ,
11
+ use dioxus_core:: { prelude:: Callback , VirtualDom } ;
12
+ use std:: {
13
+ future:: { Future , IntoFuture } ,
14
+ pin:: Pin ,
15
+ rc:: { Rc , Weak } ,
14
16
} ;
15
- use std:: rc:: { Rc , Weak } ;
16
17
use tao:: {
17
18
event:: Event ,
18
19
event_loop:: EventLoopWindowTarget ,
@@ -99,21 +100,39 @@ impl DesktopService {
99
100
}
100
101
}
101
102
102
- /// Create a new window using the props and window builder
103
+ /// Start the creation of a new window using the props and window builder
103
104
///
104
- /// Returns the webview handle for the new window.
105
- ///
106
- /// You can use this to control other windows from the current window.
105
+ /// Returns a future that resolves to the webview handle for the new window. You can use this
106
+ /// to control other windows from the current window once the new window is created.
107
107
///
108
108
/// Be careful to not create a cycle of windows, or you might leak memory.
109
- pub fn new_window ( & self , dom : VirtualDom , cfg : Config ) -> WeakDesktopContext {
110
- let window = WebviewInstance :: new ( cfg, dom, self . shared . clone ( ) ) ;
111
-
112
- let cx = window. dom . in_runtime ( || {
113
- ScopeId :: ROOT
114
- . consume_context :: < Rc < DesktopService > > ( )
115
- . unwrap ( )
116
- } ) ;
109
+ ///
110
+ /// # Example
111
+ ///
112
+ /// ```rust, no_run
113
+ /// use dioxus::prelude::*;
114
+ /// fn popup() -> Element {
115
+ /// rsx! {
116
+ /// div { "This is a popup window!" }
117
+ /// }
118
+ /// }
119
+ ///
120
+ /// // Create a new window with a component that will be rendered in the new window.
121
+ /// let dom = VirtualDom::new(popup);
122
+ /// // Create and wait for the window
123
+ /// let window = dioxus::desktop::window().new_window(dom, Default::default()).await;
124
+ /// // Fullscreen the new window
125
+ /// window.set_fullscreen(true);
126
+ /// ```
127
+ // Note: This method is asynchronous because webview2 does not support creating a new window from
128
+ // inside of an existing webview callback. Dioxus runs event handlers synchronously inside of a webview
129
+ // callback. See [this page](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#reentrancy) for more information.
130
+ //
131
+ // Related issues:
132
+ // - https://github.com/tauri-apps/wry/issues/583
133
+ // - https://github.com/DioxusLabs/dioxus/issues/3080
134
+ pub fn new_window ( & self , dom : VirtualDom , cfg : Config ) -> PendingDesktopContext {
135
+ let ( window, context) = PendingWebview :: new ( dom, cfg) ;
117
136
118
137
self . shared
119
138
. proxy
@@ -122,7 +141,7 @@ impl DesktopService {
122
141
123
142
self . shared . pending_webviews . borrow_mut ( ) . push ( window) ;
124
143
125
- Rc :: downgrade ( & cx )
144
+ context
126
145
}
127
146
128
147
/// trigger the drag-window event
@@ -300,3 +319,43 @@ fn is_main_thread() -> bool {
300
319
let result: BOOL = unsafe { msg_send ! [ cls, isMainThread] } ;
301
320
result != NO
302
321
}
322
+
323
+ /// A [`DesktopContext`] that is pending creation.
324
+ ///
325
+ /// # Example
326
+ /// ```rust
327
+ /// // Create a new window asynchronously
328
+ /// let pending_context = desktop_service.new_window(dom, config);
329
+ /// // Wait for the context to be created
330
+ /// let window = pending_context.await;
331
+ /// window.set_fullscreen(true);
332
+ /// ```
333
+ pub struct PendingDesktopContext {
334
+ pub ( crate ) receiver : tokio:: sync:: oneshot:: Receiver < DesktopContext > ,
335
+ }
336
+
337
+ impl PendingDesktopContext {
338
+ /// Resolve the pending context into a [`DesktopContext`].
339
+ pub async fn resolve ( self ) -> DesktopContext {
340
+ self . try_resolve ( )
341
+ . await
342
+ . expect ( "Failed to resolve pending desktop context" )
343
+ }
344
+
345
+ /// Try to resolve the pending context into a [`DesktopContext`].
346
+ pub async fn try_resolve (
347
+ self ,
348
+ ) -> Result < DesktopContext , tokio:: sync:: oneshot:: error:: RecvError > {
349
+ self . receiver . await
350
+ }
351
+ }
352
+
353
+ impl IntoFuture for PendingDesktopContext {
354
+ type Output = DesktopContext ;
355
+
356
+ type IntoFuture = Pin < Box < dyn Future < Output = Self :: Output > > > ;
357
+
358
+ fn into_future ( self ) -> Self :: IntoFuture {
359
+ Box :: pin ( self . resolve ( ) )
360
+ }
361
+ }
0 commit comments