浏览代码

Make the tokio runtime optional

Jonathan Kelley 1 年之前
父节点
当前提交
38216fa272

+ 3 - 2
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}");
         }

+ 12 - 14
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;
-
-pub struct App<P> {
+use std::{cell::Cell, collections::HashMap, rc::Rc, sync::Arc};
+use tao::{
+    event::Event,
+    event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget},
+    window::WindowId,
+};
+
+pub(crate) struct App<P> {
     // 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<Cell<Option<P>>>,
     pub(crate) cfg: Rc<Cell<Option<Config>>>,
+
     pub(crate) root: Component<P>,
     pub(crate) webviews: HashMap<WindowId, WebviewHandler>,
     pub(crate) event_handlers: WindowEventHandlers,
@@ -310,7 +308,7 @@ impl<P: 'static> App<P> {
 
 /// 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;

+ 15 - 0
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<P: 'static>(self, root: Component<P>, 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`.

+ 1 - 1
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;

+ 0 - 0
packages/desktop/src/events.rs → packages/desktop/src/ipc.rs


+ 19 - 7
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<P: 'static>(root: Component<P>, 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<P: 'static>(root: Component<P>, props: P, cfg: Config) {
     let (event_loop, mut app) = App::new(cfg, props, root);
 
     event_loop.run(move |window_event, event_loop, control_flow| {

+ 4 - 3
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;

+ 7 - 2
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),
     }