diff --git a/Cargo.lock b/Cargo.lock index d51984c7c..398c74e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5839,6 +5839,7 @@ dependencies = [ "getrandom 0.3.4", "http-range", "ouroboros", + "pollster", "rand 0.9.2", "reqwest 0.12.24", "separator", diff --git a/Cargo.toml b/Cargo.toml index 10462b098..b74148399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -485,6 +485,7 @@ futures = { workspace = true } axum-core = { workspace = true } uuid = { workspace = true, features = ["v4", "serde"] } tower-http = { workspace = true, features = ["timeout"] } +pollster = "0.4.0" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] getrandom = { workspace = true, features = ["wasm_js"] } diff --git a/examples/08-apis/wgpu_child_window.rs b/examples/08-apis/wgpu_child_window.rs index 051a04969..9ade4d400 100644 --- a/examples/08-apis/wgpu_child_window.rs +++ b/examples/08-apis/wgpu_child_window.rs @@ -5,16 +5,32 @@ //! //! To use this feature set `with_as_child_window()` on your desktop config which will then let you -use dioxus::desktop::{ - Config, DesktopContext, - tao::{event::Event as WryEvent, window::WindowBuilder}, - use_wry_event_handler, window, -}; use dioxus::prelude::*; +use dioxus::{ + desktop::tao::{event::Event as WryEvent, window::Window}, + desktop::{Config, tao::window::WindowBuilder, use_wry_event_handler, window}, +}; +use std::sync::Arc; fn main() { let config = Config::new() .with_window(WindowBuilder::new().with_transparent(true)) + .with_on_window(|window, dom| { + let resources = Arc::new(pollster::block_on(async { + let resource = GraphicsContextAsyncBuilder { + desktop: window, + resources_builder: |ctx| Box::pin(GraphicsResources::new(ctx.clone())), + } + .build() + .await; + + resource.with_resources(|resources| resources.render()); + + resource + })); + + dom.provide_root_context(resources); + }) .with_as_child_window(); dioxus::LaunchBuilder::desktop() @@ -23,14 +39,7 @@ fn main() { } fn app() -> Element { - let graphics_resources = use_resource(move || async { - GraphicsContextAsyncBuilder { - desktop: window(), - resources_builder: |ctx| Box::pin(GraphicsResources::new(ctx)), - } - .build() - .await - }); + let graphics_resources = consume_context::>(); // on first render request a redraw use_effect(|| { @@ -40,27 +49,17 @@ fn app() -> Element { use_wry_event_handler(move |event, _| { use dioxus::desktop::tao::event::WindowEvent; - if let WryEvent::RedrawRequested(_id) = event { - let resources = graphics_resources.read(); - if let Some(resources) = resources.as_ref() { - resources.with_resources(|resources| resources.render()); - } - } - if let WryEvent::WindowEvent { event: WindowEvent::Resized(new_size), .. } = event { - let ctx = graphics_resources.value(); - if let Some(ctx_ref) = ctx.as_ref() { - ctx_ref.with_resources(|srcs| { - let mut cfg = srcs.config.clone(); - cfg.width = new_size.width; - cfg.height = new_size.height; - srcs.surface.configure(&srcs.device, &cfg); - }); - } + graphics_resources.with_resources(|srcs| { + let mut cfg = srcs.config.clone(); + cfg.width = new_size.width; + cfg.height = new_size.height; + srcs.surface.configure(&srcs.device, &cfg); + }); window().window.request_redraw(); } @@ -84,7 +83,7 @@ fn app() -> Element { /// to be able to borrow the window for the wgpu::Surface #[ouroboros::self_referencing] struct GraphicsContext { - desktop: DesktopContext, + desktop: Arc, #[borrows(desktop)] #[not_covariant] resources: GraphicsResources<'this>, @@ -99,8 +98,7 @@ struct GraphicsResources<'a> { } impl<'a> GraphicsResources<'a> { - async fn new(context: &'a DesktopContext) -> Self { - let window = &context.window; + async fn new(window: Arc) -> Self { let size = window.inner_size(); let instance = wgpu::Instance::default(); diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index bf8c81bda..b09323e3e 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -1,8 +1,11 @@ -use dioxus_core::LaunchConfig; -use std::borrow::Cow; +use dioxus_core::{LaunchConfig, VirtualDom}; use std::path::PathBuf; -use tao::event_loop::{EventLoop, EventLoopWindowTarget}; +use std::{borrow::Cow, sync::Arc}; use tao::window::{Icon, WindowBuilder}; +use tao::{ + event_loop::{EventLoop, EventLoopWindowTarget}, + window::Window, +}; use wry::http::{Request as HttpRequest, Response as HttpResponse}; use wry::{RequestAsyncResponder, WebViewId}; @@ -64,6 +67,9 @@ pub struct Config { pub(crate) window_close_behavior: WindowCloseBehaviour, pub(crate) custom_event_handler: Option, pub(crate) disable_file_drop_handler: bool, + + #[allow(clippy::type_complexity)] + pub(crate) on_window: Option, &mut VirtualDom) + 'static>>, } impl LaunchConfig for Config {} @@ -111,6 +117,7 @@ impl Config { window_close_behavior: WindowCloseBehaviour::WindowCloses, custom_event_handler: None, disable_file_drop_handler: false, + on_window: None, } } @@ -296,6 +303,15 @@ impl Config { } self } + + /// Allows modifying the window and virtual dom right after they are built, but before the webview is created. + /// + /// This is important for z-ordering textures in child windows. Note that this callback runs on + /// every window creation, so it's up to you to + pub fn with_on_window(mut self, f: impl FnMut(Arc, &mut VirtualDom) + 'static) -> Self { + self.on_window = Some(Box::new(f)); + self + } } impl Default for Config { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 57d42ca8e..150447a39 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -14,6 +14,7 @@ use std::{ future::{Future, IntoFuture}, pin::Pin, rc::{Rc, Weak}, + sync::Arc, }; use tao::{ event::Event, @@ -59,7 +60,7 @@ pub struct DesktopService { pub webview: WebView, /// The tao window itself - pub window: Window, + pub window: Arc, pub(crate) shared: Rc, @@ -85,7 +86,7 @@ impl std::ops::Deref for DesktopService { impl DesktopService { pub(crate) fn new( webview: WebView, - window: Window, + window: Arc, shared: Rc, asset_handlers: AssetHandlerRegistry, file_hover: NativeFileHover, diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index df662aaa9..6cbeab183 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -16,7 +16,7 @@ use dioxus_history::{History, MemoryHistory}; use dioxus_hooks::to_owned; use dioxus_html::{FileData, FormValue, HtmlEvent, PlatformEventData}; use futures_util::{pin_mut, FutureExt}; -use std::sync::atomic::AtomicBool; +use std::sync::{atomic::AtomicBool, Arc}; use std::{cell::OnceCell, time::Duration}; use std::{rc::Rc, task::Waker}; use wry::{DragDropEvent, RequestAsyncResponder, WebContext, WebViewBuilder, WebViewId}; @@ -200,7 +200,7 @@ pub(crate) struct WebviewInstance { impl WebviewInstance { pub(crate) fn new( mut cfg: Config, - dom: VirtualDom, + mut dom: VirtualDom, shared: Rc, ) -> WebviewInstance { let mut window = cfg.window.clone(); @@ -228,7 +228,10 @@ impl WebviewInstance { )); } - let window = window.build(&shared.target).unwrap(); + let window = Arc::new(window.build(&shared.target).unwrap()); + if let Some(on_build) = cfg.on_window.as_mut() { + on_build(window.clone(), &mut dom); + } // https://developer.apple.com/documentation/appkit/nswindowcollectionbehavior/nswindowcollectionbehaviormanaged #[cfg(target_os = "macos")] @@ -485,6 +488,9 @@ impl WebviewInstance { provide_context(history_provider); }); + // Request an initial redraw + desktop_context.window.request_redraw(); + WebviewInstance { dom, edits,