소스 검색

Fix non tokio builds for desktop

Jonathan Kelley 1 년 전
부모
커밋
94b17cc8ca

+ 3 - 96
Cargo.lock

@@ -2033,23 +2033,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "cssparser"
-version = "0.29.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa"
-dependencies = [
- "cssparser-macros",
- "dtoa-short",
- "itoa 1.0.10",
- "matches",
- "phf 0.10.1",
- "proc-macro2",
- "quote",
- "smallvec",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "cssparser"
 version = "0.33.0"
@@ -2533,6 +2516,7 @@ dependencies = [
  "dioxus-hot-reload",
  "dioxus-html",
  "dioxus-interpreter-js",
+ "dioxus-signals",
  "dunce",
  "exitcode",
  "futures-channel",
@@ -2545,7 +2529,6 @@ dependencies = [
  "objc_id",
  "rfd",
  "rustc-hash",
- "scraper",
  "serde",
  "serde_json",
  "slab",
@@ -3115,12 +3098,6 @@ dependencies = [
  "zeroize",
 ]
 
-[[package]]
-name = "ego-tree"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
-
 [[package]]
 name = "either"
 version = "1.9.0"
@@ -3910,15 +3887,6 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "getopts"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
-dependencies = [
- "unicode-width",
-]
-
 [[package]]
 name = "getrandom"
 version = "0.1.16"
@@ -5615,7 +5583,7 @@ dependencies = [
  "html5ever",
  "indexmap 1.9.3",
  "matches",
- "selectors 0.22.0",
+ "selectors",
 ]
 
 [[package]]
@@ -7249,9 +7217,7 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
 dependencies = [
- "phf_macros 0.10.0",
  "phf_shared 0.10.0",
- "proc-macro-hack",
 ]
 
 [[package]]
@@ -7328,20 +7294,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "phf_macros"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
- "proc-macro-hack",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "phf_macros"
 version = "0.11.2"
@@ -8843,23 +8795,6 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
-[[package]]
-name = "scraper"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59e25654b5e9fd557a67dbaab5a5d36b8c448d0561beb4c041b6dbb902eddfa6"
-dependencies = [
- "ahash 0.8.7",
- "cssparser 0.29.6",
- "ego-tree",
- "getopts",
- "html5ever",
- "once_cell",
- "selectors 0.24.0",
- "smallvec",
- "tendril",
-]
-
 [[package]]
 name = "sct"
 version = "0.7.1"
@@ -8928,29 +8863,11 @@ dependencies = [
  "phf 0.8.0",
  "phf_codegen 0.8.0",
  "precomputed-hash",
- "servo_arc 0.1.1",
+ "servo_arc",
  "smallvec",
  "thin-slice",
 ]
 
-[[package]]
-name = "selectors"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416"
-dependencies = [
- "bitflags 1.3.2",
- "cssparser 0.29.6",
- "derive_more",
- "fxhash",
- "log",
- "phf 0.8.0",
- "phf_codegen 0.8.0",
- "precomputed-hash",
- "servo_arc 0.2.0",
- "smallvec",
-]
-
 [[package]]
 name = "semver"
 version = "1.0.21"
@@ -9201,16 +9118,6 @@ dependencies = [
  "stable_deref_trait",
 ]
 
-[[package]]
-name = "servo_arc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741"
-dependencies = [
- "nodrop",
- "stable_deref_trait",
-]
-
 [[package]]
 name = "sha-1"
 version = "0.10.1"

+ 2 - 2
packages/desktop/Cargo.toml

@@ -30,7 +30,7 @@ wry = { version = "0.35.0", default-features = false, features = [
     "protocol",
     "file-drop",
 ] }
-futures-channel = { workspace = true }
+futures-channel.workspace = true
 tokio = { workspace = true, features = [
     "sync",
     "rt-multi-thread",
@@ -81,8 +81,8 @@ features = ["tokio_runtime", "hot-reload"]
 [dev-dependencies]
 dioxus-core-macro = { workspace = true }
 dioxus-hooks = { workspace = true }
+dioxus-signals = { workspace = true }
 exitcode = "1.1.2"
-scraper = "0.16.0"
 
 [build-dependencies]
 dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }

+ 23 - 23
packages/desktop/headless_tests/rendering.rs

@@ -1,6 +1,11 @@
 use dioxus::prelude::*;
+use dioxus_core::Element;
 use dioxus_desktop::DesktopContext;
 
+fn main() {
+    check_app_exits(check_html_renders);
+}
+
 pub(crate) fn check_app_exits(app: Component) {
     use dioxus_desktop::Config;
     use tao::window::WindowBuilder;
@@ -22,28 +27,20 @@ pub(crate) fn check_app_exits(app: Component) {
     should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
 }
 
-fn main() {
-    check_app_exits(check_html_renders);
-}
-
 fn use_inner_html(d: &'static str) -> Option<String> {
-    let eval_provider = use_eval(cx);
-
     let value: Signal<Option<String>> = use_signal(|| None);
-    use_effect((), |_| {
-        to_owned![value, eval_provider];
-        async move {
-            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
-            let html = eval_provider(&format!(
-                r#"let element = document.getElementById('{}');
+    use_effect(|| async move {
+        tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        window().eval();
+        let html = eval_provider(&format!(
+            r#"let element = document.getElementById('{}');
                     return element.innerHTML"#,
-                id
-            ))
-            .unwrap();
-            if let Ok(serde_json::Value::String(html)) = html.await {
-                println!("html: {}", html);
-                value.set(Some(html));
-            }
+            id
+        ))
+        .unwrap();
+        if let Ok(serde_json::Value::String(html)) = html.await {
+            println!("html: {}", html);
+            value.set(Some(html));
         }
     });
     value.read().clone()
@@ -58,10 +55,13 @@ fn check_html_renders() -> Element {
 
     if let Some(raw_html) = inner_html {
         println!("{}", raw_html);
-        let fragment = scraper::Html::parse_fragment(&raw_html);
-        println!("fragment: {}", fragment.html());
-        let expected = scraper::Html::parse_fragment(EXPECTED_HTML);
-        println!("expected: {}", expected.html());
+        let fragment = &raw_html;
+        let expected = EXPECTED_HTML;
+        // let fragment = scraper::Html::parse_fragment(&raw_html);
+        // println!("fragment: {}", fragment.html());
+        // let expected = scraper::Html::parse_fragment(EXPECTED_HTML);
+        // println!("expected: {}", expected.html());
+        assert_eq!(raw_html, EXPECTED_HTML);
         if fragment == expected {
             println!("html matches");
             desktop_context.close();

+ 1 - 1
packages/desktop/src/app.rs

@@ -1,7 +1,7 @@
 use crate::{
     config::{Config, WindowCloseBehaviour},
-    desktop_context::WindowEventHandlers,
     element::DesktopElement,
+    event_handlers::WindowEventHandlers,
     file_upload::FileDialogRequest,
     ipc::IpcMessage,
     ipc::{EventData, UserWindowEvent},

+ 10 - 114
packages/desktop/src/desktop_context.rs

@@ -4,17 +4,16 @@ use crate::{
     edits::EditQueue,
     ipc::{EventData, UserWindowEvent},
     query::QueryEngine,
-    shortcut::{HotKey, ShortcutId, ShortcutRegistryError},
+    shortcut::{HotKey, ShortcutHandle, ShortcutRegistryError},
     webview::WebviewInstance,
-    AssetRequest, Config,
+    AssetRequest, Config, WryEventHandler,
 };
 use dioxus_core::{
     prelude::{current_scope_id, ScopeId},
-    use_hook, VirtualDom,
+    VirtualDom,
 };
 use dioxus_interpreter_js::MutationState;
-use slab::Slab;
-use std::{cell::RefCell, fmt::Debug, rc::Rc, rc::Weak};
+use std::{cell::RefCell, rc::Rc, rc::Weak};
 use tao::{
     event::Event,
     event_loop::EventLoopWindowTarget,
@@ -208,12 +207,12 @@ impl DesktopService {
     pub fn create_wry_event_handler(
         &self,
         handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
-    ) -> WryEventHandlerId {
+    ) -> WryEventHandler {
         self.shared.event_handlers.add(self.window.id(), handler)
     }
 
     /// Remove a wry event handler created with [`DesktopContext::create_wry_event_handler`]
-    pub fn remove_wry_event_handler(&self, id: WryEventHandlerId) {
+    pub fn remove_wry_event_handler(&self, id: WryEventHandler) {
         self.shared.event_handlers.remove(id)
     }
 
@@ -224,14 +223,14 @@ impl DesktopService {
         &self,
         hotkey: HotKey,
         callback: impl FnMut() + 'static,
-    ) -> Result<ShortcutId, ShortcutRegistryError> {
+    ) -> Result<ShortcutHandle, ShortcutRegistryError> {
         self.shared
             .shortcut_manager
             .add_shortcut(hotkey, Box::new(callback))
     }
 
     /// Remove a global shortcut
-    pub fn remove_shortcut(&self, id: ShortcutId) {
+    pub fn remove_shortcut(&self, id: ShortcutHandle) {
         self.shared.shortcut_manager.remove_shortcut(id)
     }
 
@@ -250,12 +249,12 @@ impl DesktopService {
     pub fn register_asset_handler(
         &self,
         name: String,
-        f: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
+        handler: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
         scope: Option<ScopeId>,
     ) {
         self.asset_handlers.register_handler(
             name,
-            f,
+            handler,
             scope.unwrap_or(current_scope_id().unwrap_or(ScopeId(0))),
         )
     }
@@ -313,106 +312,3 @@ fn is_main_thread() -> bool {
     let result: BOOL = unsafe { msg_send![cls, isMainThread] };
     result != NO
 }
-
-/// The unique identifier of a window event handler. This can be used to later remove the handler.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct WryEventHandlerId(usize);
-
-#[derive(Clone, Default)]
-pub(crate) struct WindowEventHandlers {
-    handlers: Rc<RefCell<Slab<WryWindowEventHandlerInner>>>,
-}
-
-impl WindowEventHandlers {
-    pub(crate) fn add(
-        &self,
-        window_id: WindowId,
-        handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
-    ) -> WryEventHandlerId {
-        WryEventHandlerId(
-            self.handlers
-                .borrow_mut()
-                .insert(WryWindowEventHandlerInner {
-                    window_id,
-                    handler: Box::new(handler),
-                }),
-        )
-    }
-
-    pub(crate) fn remove(&self, id: WryEventHandlerId) {
-        self.handlers.borrow_mut().try_remove(id.0);
-    }
-
-    pub(crate) fn apply_event(
-        &self,
-        event: &Event<UserWindowEvent>,
-        target: &EventLoopWindowTarget<UserWindowEvent>,
-    ) {
-        for (_, handler) in self.handlers.borrow_mut().iter_mut() {
-            handler.apply_event(event, target);
-        }
-    }
-}
-
-struct WryWindowEventHandlerInner {
-    window_id: WindowId,
-    handler: WryEventHandlerCallback,
-}
-
-type WryEventHandlerCallback =
-    Box<dyn FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static>;
-
-impl WryWindowEventHandlerInner {
-    fn apply_event(
-        &mut self,
-        event: &Event<UserWindowEvent>,
-        target: &EventLoopWindowTarget<UserWindowEvent>,
-    ) {
-        // if this event does not apply to the window this listener cares about, return
-        if let Event::WindowEvent { window_id, .. } = event {
-            if *window_id != self.window_id {
-                return;
-            }
-        }
-        (self.handler)(event, target)
-    }
-}
-
-/// Get a closure that executes any JavaScript in the WebView context.
-pub fn use_wry_event_handler(
-    handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
-) -> WryEventHandler {
-    use_hook(move || {
-        let desktop = window();
-
-        let id = desktop.create_wry_event_handler(handler);
-
-        WryEventHandler {
-            handlers: desktop.shared.event_handlers.clone(),
-            id,
-        }
-    })
-}
-
-/// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events.
-///
-/// This will automatically be removed when the component is unmounted.
-#[derive(Clone)]
-pub struct WryEventHandler {
-    pub(crate) handlers: WindowEventHandlers,
-    /// The unique identifier of the event handler.
-    pub id: WryEventHandlerId,
-}
-
-impl WryEventHandler {
-    /// Remove the event handler.
-    pub fn remove(&self) {
-        self.handlers.remove(self.id);
-    }
-}
-
-impl Drop for WryEventHandler {
-    fn drop(&mut self) {
-        self.handlers.remove(self.id);
-    }
-}

+ 65 - 0
packages/desktop/src/event_handlers.rs

@@ -0,0 +1,65 @@
+use crate::{ipc::UserWindowEvent, window};
+use slab::Slab;
+use std::cell::RefCell;
+use tao::{event::Event, event_loop::EventLoopWindowTarget, window::WindowId};
+
+/// The unique identifier of a window event handler. This can be used to later remove the handler.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct WryEventHandler(pub(crate) usize);
+
+impl WryEventHandler {
+    /// Unregister this event handler from the window
+    pub fn remove(&self) {
+        window().shared.event_handlers.remove(*self)
+    }
+}
+
+#[derive(Default)]
+pub struct WindowEventHandlers {
+    handlers: RefCell<Slab<WryWindowEventHandlerInner>>,
+}
+
+struct WryWindowEventHandlerInner {
+    window_id: WindowId,
+
+    #[allow(clippy::type_complexity)]
+    handler:
+        Box<dyn FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static>,
+}
+
+impl WindowEventHandlers {
+    pub(crate) fn add(
+        &self,
+        window_id: WindowId,
+        handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
+    ) -> WryEventHandler {
+        WryEventHandler(
+            self.handlers
+                .borrow_mut()
+                .insert(WryWindowEventHandlerInner {
+                    window_id,
+                    handler: Box::new(handler),
+                }),
+        )
+    }
+
+    pub(crate) fn remove(&self, id: WryEventHandler) {
+        self.handlers.borrow_mut().try_remove(id.0);
+    }
+
+    pub fn apply_event(
+        &self,
+        event: &Event<UserWindowEvent>,
+        target: &EventLoopWindowTarget<UserWindowEvent>,
+    ) {
+        for (_, handler) in self.handlers.borrow_mut().iter_mut() {
+            // if this event does not apply to the window this listener cares about, return
+            if let Event::WindowEvent { window_id, .. } = event {
+                if *window_id != handler.window_id {
+                    return;
+                }
+            }
+            (handler.handler)(event, target)
+        }
+    }
+}

+ 27 - 34
packages/desktop/src/hooks.rs

@@ -8,29 +8,23 @@ use dioxus_core::{
     prelude::{consume_context, current_scope_id},
     use_hook,
 };
-use dioxus_hooks::use_on_drop;
+use dioxus_hooks::use_hook_with_cleanup;
 use tao::{event::Event, event_loop::EventLoopWindowTarget};
 use wry::RequestAsyncResponder;
 
 /// Get an imperative handle to the current window
 pub fn use_window() -> DesktopContext {
-    use_hook(|| consume_context::<DesktopContext>())
+    use_hook(consume_context::<DesktopContext>)
 }
 
 /// Get a closure that executes any JavaScript in the WebView context.
 pub fn use_wry_event_handler(
     handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
 ) -> WryEventHandler {
-    use_hook(move || {
-        let desktop = window();
-
-        let id = desktop.create_wry_event_handler(handler);
-
-        WryEventHandler {
-            handlers: desktop.shared.event_handlers.clone(),
-            id,
-        }
-    })
+    use_hook_with_cleanup(
+        move || window().create_wry_event_handler(handler),
+        move |handler| handler.remove(),
+    )
 }
 
 /// Provide a callback to handle asset loading yourself.
@@ -41,19 +35,20 @@ pub fn use_asset_handler(
     name: &str,
     handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static,
 ) {
-    let name = use_hook(|| {
-        crate::window().asset_handlers.register_handler(
-            name.to_string(),
-            Box::new(handler),
-            current_scope_id().unwrap(),
-        );
-
-        Rc::new(name.to_string())
-    });
+    use_hook_with_cleanup(
+        || {
+            crate::window().asset_handlers.register_handler(
+                name.to_string(),
+                Box::new(handler),
+                current_scope_id().unwrap(),
+            );
 
-    use_on_drop(move || {
-        _ = crate::window().asset_handlers.remove_handler(name.as_ref());
-    });
+            Rc::new(name.to_string())
+        },
+        move |name| {
+            _ = crate::window().asset_handlers.remove_handler(name.as_ref());
+        },
+    );
 }
 
 /// Get a closure that executes any JavaScript in the WebView context.
@@ -61,14 +56,12 @@ pub fn use_global_shortcut(
     accelerator: impl IntoAccelerator,
     handler: impl FnMut() + 'static,
 ) -> Result<ShortcutHandle, ShortcutRegistryError> {
-    use_hook(move || {
-        let desktop = window();
-
-        let id = desktop.create_shortcut(accelerator.accelerator(), handler);
-
-        Ok(ShortcutHandle {
-            desktop,
-            shortcut_id: id?,
-        })
-    })
+    use_hook_with_cleanup(
+        move || window().create_shortcut(accelerator.accelerator(), handler),
+        |handle| {
+            if let Ok(handle) = handle {
+                handle.remove();
+            }
+        },
+    )
 }

+ 1 - 1
packages/desktop/src/launch.rs

@@ -70,5 +70,5 @@ pub fn launch(
         }));
 
     #[cfg(not(feature = "tokio"))]
-    launch_with_props_blocking(config, platform_config)
+    launch_with_props_blocking(virtual_dom, platform_config)
 }

+ 4 - 6
packages/desktop/src/lib.rs

@@ -10,6 +10,7 @@ mod desktop_context;
 mod edits;
 mod element;
 mod eval;
+mod event_handlers;
 mod events;
 mod file_upload;
 mod hooks;
@@ -38,11 +39,8 @@ pub use wry;
 // Public exports
 pub use assets::AssetRequest;
 pub use config::{Config, WindowCloseBehaviour};
-pub use desktop_context::{
-    window, DesktopContext, DesktopService, WryEventHandler, WryEventHandlerId,
-};
+pub use desktop_context::{window, DesktopContext, DesktopService};
+pub use event_handlers::WryEventHandler;
 pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler};
-pub use shortcut::{ShortcutHandle, ShortcutId, ShortcutRegistryError};
+pub use shortcut::{ShortcutHandle, ShortcutRegistryError};
 pub use wry::RequestAsyncResponder;
-
-pub use hooks::*;

+ 15 - 20
packages/desktop/src/query.rs

@@ -1,11 +1,11 @@
 use std::{cell::RefCell, rc::Rc};
 
 use crate::DesktopContext;
+use futures_util::StreamExt;
 use serde::{de::DeserializeOwned, Deserialize};
 use serde_json::Value;
 use slab::Slab;
 use thiserror::Error;
-use tokio::sync::broadcast::error::RecvError;
 
 const DIOXUS_CODE: &str = r#"
 let dioxus = {
@@ -64,8 +64,8 @@ impl<T> Default for SharedSlab<T> {
 }
 
 struct QueryEntry {
-    channel_sender: tokio::sync::mpsc::UnboundedSender<Value>,
-    return_sender: Option<tokio::sync::oneshot::Sender<Value>>,
+    channel_sender: futures_channel::mpsc::UnboundedSender<Value>,
+    return_sender: Option<futures_channel::oneshot::Sender<Value>>,
 }
 
 const QUEUE_NAME: &str = "__msg_queues";
@@ -83,8 +83,8 @@ impl QueryEngine {
         script: &str,
         context: DesktopContext,
     ) -> Query<V> {
-        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
-        let (return_tx, return_rx) = tokio::sync::oneshot::channel();
+        let (tx, rx) = futures_channel::mpsc::unbounded();
+        let (return_tx, return_rx) = futures_channel::oneshot::channel();
         let request_id = self.active_requests.slab.borrow_mut().insert(QueryEntry {
             channel_sender: tx,
             return_sender: Some(return_tx),
@@ -99,14 +99,14 @@ impl QueryEngine {
                     if (!window.{QUEUE_NAME}) {{
                         window.{QUEUE_NAME} = [];
                     }}
-    
+
                     let _request_id = {request_id};
-    
+
                     if (!window.{QUEUE_NAME}[{request_id}]) {{
                         window.{QUEUE_NAME}[{request_id}] = [];
                     }}
                     let _message_queue = window.{QUEUE_NAME}[{request_id}];
-    
+
                     {script}
                 }})().then((result)=>{{
                     let returned_value = {{
@@ -150,7 +150,7 @@ impl QueryEngine {
                     let _ = sender.send(data);
                 }
             } else {
-                let _ = entry.channel_sender.send(data);
+                let _ = entry.channel_sender.unbounded_send(data);
             }
         }
     }
@@ -159,8 +159,8 @@ impl QueryEngine {
 pub(crate) struct Query<V: DeserializeOwned> {
     desktop: DesktopContext,
     slab: SharedSlab<QueryEntry>,
-    receiver: tokio::sync::mpsc::UnboundedReceiver<Value>,
-    return_receiver: Option<tokio::sync::oneshot::Receiver<Value>>,
+    receiver: futures_channel::mpsc::UnboundedReceiver<Value>,
+    return_receiver: Option<futures_channel::oneshot::Receiver<Value>>,
     id: usize,
     phantom: std::marker::PhantomData<V>,
 }
@@ -200,18 +200,13 @@ impl<V: DeserializeOwned> Query<V> {
 
     /// Receive a message from the query
     pub async fn recv(&mut self) -> Result<Value, QueryError> {
-        self.receiver
-            .recv()
-            .await
-            .ok_or(QueryError::Recv(RecvError::Closed))
+        self.receiver.next().await.ok_or(QueryError::Recv)
     }
 
     /// Receive the result of the query
     pub async fn result(&mut self) -> Result<Value, QueryError> {
         match self.return_receiver.take() {
-            Some(receiver) => receiver
-                .await
-                .map_err(|_| QueryError::Recv(RecvError::Closed)),
+            Some(receiver) => receiver.await.map_err(|_| QueryError::Recv),
             None => Err(QueryError::Finished),
         }
     }
@@ -238,8 +233,8 @@ impl<V: DeserializeOwned> Drop for Query<V> {
 
 #[derive(Error, Debug)]
 pub enum QueryError {
-    #[error("Error receiving query result: {0}")]
-    Recv(RecvError),
+    #[error("Error receiving query result.")]
+    Recv,
     #[error("Error sending message to query: {0}")]
     Send(String),
     #[error("Error deserializing query result: {0}")]

+ 41 - 71
packages/desktop/src/shortcut.rs

@@ -1,11 +1,3 @@
-use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr};
-
-use dioxus_html::input_data::keyboard_types::Modifiers;
-use slab::Slab;
-use tao::keyboard::ModifiersState;
-
-use crate::desktop_context::DesktopContext;
-
 #[cfg(any(
     target_os = "windows",
     target_os = "macos",
@@ -23,31 +15,47 @@ pub use global_hotkey::{
 #[cfg(any(target_os = "ios", target_os = "android"))]
 pub use crate::mobile_shortcut::*;
 
+use crate::window;
+use dioxus_html::input_data::keyboard_types::Modifiers;
+use slab::Slab;
+use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr};
+use tao::keyboard::ModifiersState;
+
+/// An global id for a shortcut.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ShortcutHandle {
+    id: u32,
+    number: usize,
+}
+
+impl ShortcutHandle {
+    /// Remove the shortcut.
+    pub fn remove(&self) {
+        window().remove_shortcut(*self);
+    }
+}
+
+/// An error that can occur when registering a shortcut.
+#[non_exhaustive]
+#[derive(Debug, Clone)]
+pub enum ShortcutRegistryError {
+    /// The shortcut is invalid.
+    InvalidShortcut(String),
+    /// An unknown error occurred.
+    Other(Rc<dyn std::error::Error>),
+}
+
 pub(crate) struct ShortcutRegistry {
     manager: GlobalHotKeyManager,
-    shortcuts: RefCell<HashMap<u32, Shortcut>>,
+    shortcuts: RefCell<HashMap<u32, ShortcutInner>>,
 }
 
-struct Shortcut {
+struct ShortcutInner {
     #[allow(unused)]
     shortcut: HotKey,
     callbacks: Slab<Box<dyn FnMut()>>,
 }
 
-impl Shortcut {
-    fn insert(&mut self, callback: Box<dyn FnMut()>) -> usize {
-        self.callbacks.insert(callback)
-    }
-
-    fn remove(&mut self, id: usize) {
-        let _ = self.callbacks.remove(id);
-    }
-
-    fn is_empty(&self) -> bool {
-        self.callbacks.is_empty()
-    }
-}
-
 impl ShortcutRegistry {
     pub fn new() -> Self {
         Self {
@@ -57,7 +65,7 @@ impl ShortcutRegistry {
     }
 
     pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) {
-        if let Some(Shortcut { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) {
+        if let Some(ShortcutInner { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) {
             for (_, callback) in callbacks.iter_mut() {
                 (callback)();
             }
@@ -68,15 +76,15 @@ impl ShortcutRegistry {
         &self,
         hotkey: HotKey,
         callback: Box<dyn FnMut()>,
-    ) -> Result<ShortcutId, ShortcutRegistryError> {
+    ) -> Result<ShortcutHandle, ShortcutRegistryError> {
         let accelerator_id = hotkey.clone().id();
 
         let mut shortcuts = self.shortcuts.borrow_mut();
 
         if let Some(callbacks) = shortcuts.get_mut(&accelerator_id) {
-            return Ok(ShortcutId {
+            return Ok(ShortcutHandle {
                 id: accelerator_id,
-                number: callbacks.insert(callback),
+                number: callbacks.callbacks.insert(callback),
             });
         };
 
@@ -87,7 +95,7 @@ impl ShortcutRegistry {
             err => ShortcutRegistryError::Other(Rc::new(err)),
         })?;
 
-        let mut shortcut = Shortcut {
+        let mut shortcut = ShortcutInner {
             shortcut: hotkey,
             callbacks: Slab::new(),
         };
@@ -96,17 +104,17 @@ impl ShortcutRegistry {
 
         shortcuts.insert(accelerator_id, shortcut);
 
-        Ok(ShortcutId {
+        Ok(ShortcutHandle {
             id: accelerator_id,
             number: id,
         })
     }
 
-    pub(crate) fn remove_shortcut(&self, id: ShortcutId) {
+    pub(crate) fn remove_shortcut(&self, id: ShortcutHandle) {
         let mut shortcuts = self.shortcuts.borrow_mut();
         if let Some(callbacks) = shortcuts.get_mut(&id.id) {
-            callbacks.remove(id.number);
-            if callbacks.is_empty() {
+            let _ = callbacks.callbacks.remove(id.number);
+            if callbacks.callbacks.is_empty() {
                 if let Some(_shortcut) = shortcuts.remove(&id.id) {
                     let _ = self.manager.unregister(_shortcut.shortcut);
                 }
@@ -121,31 +129,6 @@ impl ShortcutRegistry {
     }
 }
 
-/// An error that can occur when registering a shortcut.
-#[non_exhaustive]
-#[derive(Debug, Clone)]
-pub enum ShortcutRegistryError {
-    /// The shortcut is invalid.
-    InvalidShortcut(String),
-    /// An unknown error occurred.
-    Other(Rc<dyn std::error::Error>),
-}
-
-/// An global id for a shortcut.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct ShortcutId {
-    id: u32,
-    number: usize,
-}
-
-/// A global shortcut. This will be automatically removed when it is dropped.
-#[derive(Clone)]
-pub struct ShortcutHandle {
-    pub(crate) desktop: DesktopContext,
-    /// The id of the shortcut
-    pub shortcut_id: ShortcutId,
-}
-
 pub trait IntoAccelerator {
     fn accelerator(&self) -> HotKey;
 }
@@ -174,19 +157,6 @@ impl IntoAccelerator for &str {
     }
 }
 
-impl ShortcutHandle {
-    /// Remove the shortcut.
-    pub fn remove(&self) {
-        self.desktop.remove_shortcut(self.shortcut_id);
-    }
-}
-
-impl Drop for ShortcutHandle {
-    fn drop(&mut self) {
-        self.remove()
-    }
-}
-
 pub trait IntoModifersState {
     fn into_modifiers_state(self) -> Modifiers;
 }

+ 0 - 6
packages/hooks/src/lib.rs

@@ -67,12 +67,6 @@ pub use use_coroutine::*;
 mod use_future;
 pub use use_future::*;
 
-// mod use_effect;
-// pub use use_effect::*;
-
-// mod use_memo;
-// pub use use_memo::*;
-
 // mod use_on_create;
 // pub use use_on_create::*;
 

+ 10 - 0
packages/hooks/src/use_on_destroy.rs

@@ -100,3 +100,13 @@ pub fn use_on_destroy<D: FnOnce() + 'static>(destroy: D) {
 pub fn use_on_drop<D: FnOnce() + 'static>(ondrop: D) {
     use_on_destroy(ondrop);
 }
+
+pub fn use_hook_with_cleanup<T: Clone + 'static>(
+    hook: impl FnOnce() -> T,
+    cleanup: impl FnOnce(T) + 'static,
+) -> T {
+    let value = use_hook(|| hook());
+    let _value = value.clone();
+    use_on_destroy(move || cleanup(_value));
+    value
+}

+ 14 - 5
packages/interpreter/Cargo.toml

@@ -13,18 +13,27 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 [dependencies]
 wasm-bindgen = { workspace = true, optional = true }
 js-sys = { version = "0.3.56", optional = true }
-web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] }
+web-sys = { version = "0.3.56", optional = true, features = [
+    "Element",
+    "Node",
+] }
 sledgehammer_bindgen = { version = "0.3.1", default-features = false, optional = true }
 sledgehammer_utils = { version = "0.2", optional = true }
 serde = { version = "1.0", features = ["derive"], optional = true }
 
-dioxus-core = { workspace = true , optional = true }
-dioxus-html = { workspace = true , optional = true }
+dioxus-core = { workspace = true, optional = true }
+dioxus-html = { workspace = true, optional = true }
 
 [features]
 default = []
 serialize = ["serde"]
 sledgehammer = ["sledgehammer_bindgen", "sledgehammer_utils"]
-web = ["sledgehammer", "wasm-bindgen", "js-sys", "web-sys", "sledgehammer_bindgen/web"]
-binary-protocol = ["sledgehammer", "wasm-bindgen", "dioxus-core", "dioxus-html"]
+web = [
+    "sledgehammer",
+    "wasm-bindgen",
+    "js-sys",
+    "web-sys",
+    "sledgehammer_bindgen/web",
+]
+binary-protocol = ["sledgehammer", "dioxus-core", "dioxus-html"]
 minimal_bindings = []

+ 4 - 4
packages/signals/Cargo.toml

@@ -16,19 +16,19 @@ rust-version = "1.60.0"
 dioxus-core = { workspace = true }
 generational-box = { workspace = true }
 tracing = { workspace = true }
-simple_logger = "4.2.0"
 serde = { version = "1", features = ["derive"], optional = true }
 parking_lot = "0.12.1"
 once_cell = "1.18.0"
-rustc-hash.workspace = true
-futures-channel.workspace = true
-futures-util.workspace = true
+rustc-hash = { workspace = true }
+futures-channel = { workspace = true }
+futures-util = { workspace = true }
 
 [dev-dependencies]
 dioxus = { workspace = true }
 dioxus-desktop = { workspace = true }
 tokio = { version = "1", features = ["full"] }
 tracing-subscriber = "0.3.17"
+simple_logger = "4.2.0"
 
 [features]
 default = []