Make new_window asynchronous (#4297)

* Make new_window asynchronous

* fix clippy
This commit is contained in:
Evan Almloff authored and GitHub committed 2025-06-19 14:32:00 -07:00
1 parent 99a6b23df8
commit e6ca0baa46
4 files changed
+118 -24

No files matched your search

+77 -18
View File
@@ -5,14 +5,15 @@ use crate::{
ipc::UserWindowEvent,
query::QueryEngine,
shortcut::{HotKey, HotKeyState, ShortcutHandle, ShortcutRegistryError},
webview::WebviewInstance,
webview::PendingWebview,
AssetRequest, Config, WryEventHandler,
};
use dioxus_core::{
prelude::{Callback, ScopeId},
VirtualDom,
use dioxus_core::{prelude::Callback, VirtualDom};
use std::{
future::{Future, IntoFuture},
pin::Pin,
rc::{Rc, Weak},
};
use std::rc::{Rc, Weak};
use tao::{
event::Event,
event_loop::EventLoopWindowTarget,
@@ -99,21 +100,39 @@ impl DesktopService {
}
}
/// Create a new window using the props and window builder
/// Start the creation of a new window using the props and window builder
///
/// Returns the webview handle for the new window.
///
/// You can use this to control other windows from the current window.
/// Returns a future that resolves to the webview handle for the new window. You can use this
/// to control other windows from the current window once the new window is created.
///
/// Be careful to not create a cycle of windows, or you might leak memory.
pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> WeakDesktopContext {
let window = WebviewInstance::new(cfg, dom, self.shared.clone());
let cx = window.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<Rc<DesktopService>>()
.unwrap()
});
///
/// # Example
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// fn popup() -> Element {
/// rsx! {
/// div { "This is a popup window!" }
/// }
/// }
///
/// // Create a new window with a component that will be rendered in the new window.
/// let dom = VirtualDom::new(popup);
/// // Create and wait for the window
/// let window = dioxus::desktop::window().new_window(dom, Default::default()).await;
/// // Fullscreen the new window
/// window.set_fullscreen(true);
/// ```
// Note: This method is asynchronous because webview2 does not support creating a new window from
// inside of an existing webview callback. Dioxus runs event handlers synchronously inside of a webview
// callback. See [this page](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#reentrancy) for more information.
//
// Related issues:
// - https://github.com/tauri-apps/wry/issues/583
// - https://github.com/DioxusLabs/dioxus/issues/3080
pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> PendingDesktopContext {
let (window, context) = PendingWebview::new(dom, cfg);
self.shared
.proxy
@@ -122,7 +141,7 @@ impl DesktopService {
self.shared.pending_webviews.borrow_mut().push(window);
Rc::downgrade(&cx)
context
}
/// trigger the drag-window event
@@ -300,3 +319,43 @@ fn is_main_thread() -> bool {
let result: BOOL = unsafe { msg_send![cls, isMainThread] };
result != NO
}
/// A [`DesktopContext`] that is pending creation.
///
/// # Example
/// ```rust
/// // Create a new window asynchronously
/// let pending_context = desktop_service.new_window(dom, config);
/// // Wait for the context to be created
/// let window = pending_context.await;
/// window.set_fullscreen(true);
/// ```
pub struct PendingDesktopContext {
pub(crate) receiver: tokio::sync::oneshot::Receiver<DesktopContext>,
}
impl PendingDesktopContext {
/// Resolve the pending context into a [`DesktopContext`].
pub async fn resolve(self) -> DesktopContext {
self.try_resolve()
.await
.expect("Failed to resolve pending desktop context")
}
/// Try to resolve the pending context into a [`DesktopContext`].
pub async fn try_resolve(
self,
) -> Result<DesktopContext, tokio::sync::oneshot::error::RecvError> {
self.receiver.await
}
}
impl IntoFuture for PendingDesktopContext {
type Output = DesktopContext;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(self.resolve())
}
}