From 94b17cc8ca99d680e176c87402a6aa3fcb7d06b0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 18 Jan 2024 04:07:28 -0800 Subject: [PATCH] Fix non tokio builds for desktop --- Cargo.lock | 99 +-------- packages/desktop/Cargo.toml | 4 +- packages/desktop/headless_tests/rendering.rs | 46 ++-- packages/desktop/src/app.rs | 2 +- packages/desktop/src/desktop_context.rs | 124 +---------- packages/desktop/src/event_handlers.rs | 65 ++++++ packages/desktop/src/hooks.rs | 61 +++-- packages/desktop/src/launch.rs | 2 +- packages/desktop/src/lib.rs | 10 +- packages/desktop/src/query.rs | 35 ++- packages/desktop/src/shortcut.rs | 220 ++++++++----------- packages/hooks/src/lib.rs | 6 - packages/hooks/src/use_on_destroy.rs | 10 + packages/interpreter/Cargo.toml | 19 +- packages/signals/Cargo.toml | 8 +- 15 files changed, 274 insertions(+), 437 deletions(-) create mode 100644 packages/desktop/src/event_handlers.rs diff --git a/Cargo.lock b/Cargo.lock index 43962c635..ea347b119 100644 --- a/Cargo.lock +++ b/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" diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 3cf7d3690..2acf5fc15 100644 --- a/packages/desktop/Cargo.toml +++ b/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"] } diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index 041b0ed05..d8e5cfd8f 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/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 { - let eval_provider = use_eval(cx); - let value: Signal> = 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(); diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index cb43a97e3..15f989e3b 100644 --- a/packages/desktop/src/app.rs +++ b/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}, diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 1c2cd84a1..867516a64 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/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, &EventLoopWindowTarget) + '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 { + ) -> Result { 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, + handler: Box, scope: Option, ) { 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>>, -} - -impl WindowEventHandlers { - pub(crate) fn add( - &self, - window_id: WindowId, - handler: impl FnMut(&Event, &EventLoopWindowTarget) + '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, - target: &EventLoopWindowTarget, - ) { - for (_, handler) in self.handlers.borrow_mut().iter_mut() { - handler.apply_event(event, target); - } - } -} - -struct WryWindowEventHandlerInner { - window_id: WindowId, - handler: WryEventHandlerCallback, -} - -type WryEventHandlerCallback = - Box, &EventLoopWindowTarget) + 'static>; - -impl WryWindowEventHandlerInner { - fn apply_event( - &mut self, - event: &Event, - target: &EventLoopWindowTarget, - ) { - // 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, &EventLoopWindowTarget) + '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); - } -} diff --git a/packages/desktop/src/event_handlers.rs b/packages/desktop/src/event_handlers.rs new file mode 100644 index 000000000..cd47f01d8 --- /dev/null +++ b/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>, +} + +struct WryWindowEventHandlerInner { + window_id: WindowId, + + #[allow(clippy::type_complexity)] + handler: + Box, &EventLoopWindowTarget) + 'static>, +} + +impl WindowEventHandlers { + pub(crate) fn add( + &self, + window_id: WindowId, + handler: impl FnMut(&Event, &EventLoopWindowTarget) + '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, + target: &EventLoopWindowTarget, + ) { + 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) + } + } +} diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index 60bf4c736..a6de4a203 100644 --- a/packages/desktop/src/hooks.rs +++ b/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::()) + use_hook(consume_context::) } /// Get a closure that executes any JavaScript in the WebView context. pub fn use_wry_event_handler( handler: impl FnMut(&Event, &EventLoopWindowTarget) + '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(), - ); + use_hook_with_cleanup( + || { + crate::window().asset_handlers.register_handler( + name.to_string(), + Box::new(handler), + current_scope_id().unwrap(), + ); - Rc::new(name.to_string()) - }); - - 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 { - 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(); + } + }, + ) } diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index d32070140..11cdd4c15 100644 --- a/packages/desktop/src/launch.rs +++ b/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) } diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index d2968dc35..6305b2729 100644 --- a/packages/desktop/src/lib.rs +++ b/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::*; diff --git a/packages/desktop/src/query.rs b/packages/desktop/src/query.rs index d38478c61..9470c972a 100644 --- a/packages/desktop/src/query.rs +++ b/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 Default for SharedSlab { } struct QueryEntry { - channel_sender: tokio::sync::mpsc::UnboundedSender, - return_sender: Option>, + channel_sender: futures_channel::mpsc::UnboundedSender, + return_sender: Option>, } const QUEUE_NAME: &str = "__msg_queues"; @@ -83,8 +83,8 @@ impl QueryEngine { script: &str, context: DesktopContext, ) -> Query { - 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 { desktop: DesktopContext, slab: SharedSlab, - receiver: tokio::sync::mpsc::UnboundedReceiver, - return_receiver: Option>, + receiver: futures_channel::mpsc::UnboundedReceiver, + return_receiver: Option>, id: usize, phantom: std::marker::PhantomData, } @@ -200,18 +200,13 @@ impl Query { /// Receive a message from the query pub async fn recv(&mut self) -> Result { - 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 { 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 Drop for Query { #[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}")] diff --git a/packages/desktop/src/shortcut.rs b/packages/desktop/src/shortcut.rs index 1fee28e34..b647d1337 100644 --- a/packages/desktop/src/shortcut.rs +++ b/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,101 +15,23 @@ pub use global_hotkey::{ #[cfg(any(target_os = "ios", target_os = "android"))] pub use crate::mobile_shortcut::*; -pub(crate) struct ShortcutRegistry { - manager: GlobalHotKeyManager, - shortcuts: RefCell>, +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, } -struct Shortcut { - #[allow(unused)] - shortcut: HotKey, - callbacks: Slab>, -} - -impl Shortcut { - fn insert(&mut self, callback: Box) -> 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 { - manager: GlobalHotKeyManager::new().unwrap(), - shortcuts: RefCell::new(HashMap::new()), - } - } - - pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) { - if let Some(Shortcut { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) { - for (_, callback) in callbacks.iter_mut() { - (callback)(); - } - } - } - - pub(crate) fn add_shortcut( - &self, - hotkey: HotKey, - callback: Box, - ) -> Result { - 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 { - id: accelerator_id, - number: callbacks.insert(callback), - }); - }; - - self.manager.register(hotkey).map_err(|e| match e { - HotkeyError::HotKeyParseError(shortcut) => { - ShortcutRegistryError::InvalidShortcut(shortcut) - } - err => ShortcutRegistryError::Other(Rc::new(err)), - })?; - - let mut shortcut = Shortcut { - shortcut: hotkey, - callbacks: Slab::new(), - }; - - let id = shortcut.callbacks.insert(callback); - - shortcuts.insert(accelerator_id, shortcut); - - Ok(ShortcutId { - id: accelerator_id, - number: id, - }) - } - - pub(crate) fn remove_shortcut(&self, id: ShortcutId) { - let mut shortcuts = self.shortcuts.borrow_mut(); - if let Some(callbacks) = shortcuts.get_mut(&id.id) { - callbacks.remove(id.number); - if callbacks.is_empty() { - if let Some(_shortcut) = shortcuts.remove(&id.id) { - let _ = self.manager.unregister(_shortcut.shortcut); - } - } - } - } - - pub(crate) fn remove_all(&self) { - let mut shortcuts = self.shortcuts.borrow_mut(); - let hotkeys: Vec<_> = shortcuts.drain().map(|(_, v)| v.shortcut).collect(); - let _ = self.manager.unregister_all(&hotkeys); +impl ShortcutHandle { + /// Remove the shortcut. + pub fn remove(&self) { + window().remove_shortcut(*self); } } @@ -131,19 +45,88 @@ pub enum ShortcutRegistryError { Other(Rc), } -/// An global id for a shortcut. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ShortcutId { - id: u32, - number: usize, +pub(crate) struct ShortcutRegistry { + manager: GlobalHotKeyManager, + shortcuts: RefCell>, } -/// 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, +struct ShortcutInner { + #[allow(unused)] + shortcut: HotKey, + callbacks: Slab>, +} + +impl ShortcutRegistry { + pub fn new() -> Self { + Self { + manager: GlobalHotKeyManager::new().unwrap(), + shortcuts: RefCell::new(HashMap::new()), + } + } + + pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) { + if let Some(ShortcutInner { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) { + for (_, callback) in callbacks.iter_mut() { + (callback)(); + } + } + } + + pub(crate) fn add_shortcut( + &self, + hotkey: HotKey, + callback: Box, + ) -> Result { + let accelerator_id = hotkey.clone().id(); + + let mut shortcuts = self.shortcuts.borrow_mut(); + + if let Some(callbacks) = shortcuts.get_mut(&accelerator_id) { + return Ok(ShortcutHandle { + id: accelerator_id, + number: callbacks.callbacks.insert(callback), + }); + }; + + self.manager.register(hotkey).map_err(|e| match e { + HotkeyError::HotKeyParseError(shortcut) => { + ShortcutRegistryError::InvalidShortcut(shortcut) + } + err => ShortcutRegistryError::Other(Rc::new(err)), + })?; + + let mut shortcut = ShortcutInner { + shortcut: hotkey, + callbacks: Slab::new(), + }; + + let id = shortcut.callbacks.insert(callback); + + shortcuts.insert(accelerator_id, shortcut); + + Ok(ShortcutHandle { + id: accelerator_id, + number: id, + }) + } + + pub(crate) fn remove_shortcut(&self, id: ShortcutHandle) { + let mut shortcuts = self.shortcuts.borrow_mut(); + if let Some(callbacks) = shortcuts.get_mut(&id.id) { + 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); + } + } + } + } + + pub(crate) fn remove_all(&self) { + let mut shortcuts = self.shortcuts.borrow_mut(); + let hotkeys: Vec<_> = shortcuts.drain().map(|(_, v)| v.shortcut).collect(); + let _ = self.manager.unregister_all(&hotkeys); + } } pub trait IntoAccelerator { @@ -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; } diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index f8f36ce24..4f5a8c35b 100644 --- a/packages/hooks/src/lib.rs +++ b/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::*; diff --git a/packages/hooks/src/use_on_destroy.rs b/packages/hooks/src/use_on_destroy.rs index 4ce5d5a0e..26c85b823 100644 --- a/packages/hooks/src/use_on_destroy.rs +++ b/packages/hooks/src/use_on_destroy.rs @@ -100,3 +100,13 @@ pub fn use_on_destroy(destroy: D) { pub fn use_on_drop(ondrop: D) { use_on_destroy(ondrop); } + +pub fn use_hook_with_cleanup( + 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 +} diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index aa285d49c..b2520a96a 100644 --- a/packages/interpreter/Cargo.toml +++ b/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 = [] diff --git a/packages/signals/Cargo.toml b/packages/signals/Cargo.toml index f48120ca2..089e89bec 100644 --- a/packages/signals/Cargo.toml +++ b/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 = []