diff --git a/examples/clock.rs b/examples/clock.rs index f9c1892d9..4b7be3d3b 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -1,7 +1,8 @@ use dioxus::prelude::*; use dioxus_signals::use_signal; -fn main() { +#[tokio::main] +async fn main() { dioxus_desktop::launch(app); } @@ -10,7 +11,7 @@ fn app(cx: Scope) -> Element { use_future!(cx, || async move { loop { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + tokio::time::sleep(std::time::Duration::from_millis(10)).await; count += 1; println!("current: {count}"); } diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 9a45e6c19..c8ca8a92a 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -3,30 +3,28 @@ use crate::{ desktop_context::{EventData, UserWindowEvent, WindowEventHandlers}, edits::WebviewQueue, element::DesktopElement, - events::IpcMessage, file_upload::FileDialogRequest, + ipc::IpcMessage, query::QueryResult, shortcut::{GlobalHotKeyEvent, ShortcutRegistry}, webview::WebviewHandler, }; use dioxus_core::{Component, ElementId, VirtualDom}; -use dioxus_html::MountedData; -use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent}; +use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent, MountedData}; use futures_util::{pin_mut, FutureExt}; -use std::cell::Cell; -use std::rc::Rc; -use std::{collections::HashMap, sync::Arc}; -use tao::event_loop::EventLoopBuilder; -use tao::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; -use tao::window::WindowId; -use tao::{event::Event, event_loop::ControlFlow}; -use wry::WebView; +use std::{cell::Cell, collections::HashMap, rc::Rc, sync::Arc}; +use tao::{ + event::Event, + event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget}, + window::WindowId, +}; -pub struct App

{ +pub(crate) struct App

{ // move the props into a cell so we can pop it out later to create the first window // iOS panics if we create a window before the event loop is started pub(crate) props: Rc>>, pub(crate) cfg: Rc>>, + pub(crate) root: Component

, pub(crate) webviews: HashMap, pub(crate) event_handlers: WindowEventHandlers, @@ -310,7 +308,7 @@ impl App

{ /// Different hide implementations per platform #[allow(unused)] -pub fn hide_app_window(webview: &WebView) { +pub fn hide_app_window(webview: &wry::WebView) { #[cfg(target_os = "windows")] { use wry::application::platform::windows::WindowExtWindows; diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index 4fb7f6ecb..a5d17b541 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::path::PathBuf; +use dioxus_core::prelude::Component; use tao::window::{Icon, WindowBuilder, WindowId}; use wry::{ http::{Request as HttpRequest, Response as HttpResponse}, @@ -68,6 +69,20 @@ impl Config { } } + /// Launch a Dioxus app using the given component and config + /// + /// See the [`crate::launch::launch`] function for more details. + pub fn launch(self, root: Component<()>) { + crate::launch::launch_cfg(root, self) + } + + /// Launch a Dioxus app using the given component, config, and props + /// + /// See the [`crate::launch::launch_with_props`] function for more details. + pub fn launch_with_props(self, root: Component

, props: P) { + crate::launch::launch_with_props(root, props, self) + } + /// Set whether the default menu bar should be enabled. /// /// > Note: `enable` is `true` by default. To disable the default menu bar pass `false`. diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index ef2f671b2..e1ed1f07a 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -1,4 +1,4 @@ -use crate::events::IpcMessage; +use crate::ipc::IpcMessage; use crate::query::QueryEngine; use crate::shortcut::{HotKey, ShortcutId, ShortcutRegistry, ShortcutRegistryError}; use crate::AssetHandler; diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/ipc.rs similarity index 100% rename from packages/desktop/src/events.rs rename to packages/desktop/src/ipc.rs diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 8af51e137..8ee939494 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -1,12 +1,11 @@ use crate::{ app::App, desktop_context::{EventData, UserWindowEvent}, - events::IpcMethod, + ipc::IpcMethod, Config, }; use dioxus_core::*; use tao::event::{Event, StartCause, WindowEvent}; -use tokio::runtime::Builder; /// Launch the WebView and run the event loop. /// @@ -55,6 +54,7 @@ pub fn launch_cfg(root: Component, config_builder: Config) { /// Launch the WebView and run the event loop, with configuration and root props. /// +/// If the [`tokio`] feature is enabled, this will also startup and block a tokio runtime using the unconstrained task. /// This function will start a multithreaded Tokio runtime as well the WebView event loop. This will block the current thread. /// /// You can configure the WebView window with a configuration closure @@ -78,12 +78,24 @@ pub fn launch_cfg(root: Component, config_builder: Config) { /// } /// ``` pub fn launch_with_props(root: Component

, props: P, cfg: Config) { - // We start the tokio runtime *on this thread* - // Any future we poll later will use this runtime to spawn tasks and for IO - // I would love to just allow dioxus to work with any runtime... but tokio is weird - let rt = Builder::new_multi_thread().enable_all().build().unwrap(); - let _guard = rt.enter(); + #[cfg(feature = "tokio")] + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(tokio::task::unconstrained(async move { + launch_with_props_blocking(root, props, cfg); + })); + #[cfg(not(feature = "tokio"))] + launch_with_props_blocking(root, props, cfg); +} + +/// Launch the WebView and run the event loop, with configuration and root props. +/// +/// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime +/// and is equivalent to calling launch_with_props with the tokio feature disabled. +pub fn launch_with_props_blocking(root: Component

, props: P, cfg: Config) { let (event_loop, mut app) = App::new(cfg, props, root); event_loop.run(move |window_event, event_loop, control_flow| { diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 818605dc5..3f8d9d3ce 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -11,18 +11,19 @@ mod edits; mod element; mod escape; mod eval; -mod events; mod file_upload; mod hooks; +mod ipc; mod menubar; -#[cfg(any(target_os = "ios", target_os = "android"))] -mod mobile_shortcut; mod protocol; mod query; mod shortcut; mod waker; mod webview; +#[cfg(any(target_os = "ios", target_os = "android"))] +mod mobile_shortcut; + // The main entrypoint for this crate pub use launch::*; mod launch; diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 77acebad6..bdcefa473 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -57,7 +57,8 @@ pub(super) async fn desktop_handler( .body(Cow::from(body)) { Ok(response) => { - return responder.respond(response); + responder.respond(response); + return; } Err(err) => tracing::error!("error building response: {}", err), } @@ -103,7 +104,10 @@ pub(super) async fn desktop_handler( .header("Content-Type", content_type) .body(Cow::from(asset)) { - Ok(response) => return responder.respond(response), + Ok(response) => { + responder.respond(response); + return; + } Err(err) => tracing::error!("error building response: {}", err), } } @@ -114,6 +118,7 @@ pub(super) async fn desktop_handler( { Ok(response) => { responder.respond(response); + return; } Err(err) => tracing::error!("error building response: {}", err), }