浏览代码

Add back ondestroy hook

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

+ 2 - 5
examples/shortcut.rs

@@ -8,10 +8,7 @@ fn main() {
 fn app() -> Element {
     let toggled = use_signal(|| false);
 
-    use_global_shortcut("ctrl+s", {
-        to_owned![toggled];
-        move || toggled.modify(|t| !*t)
-    });
+    use_global_shortcut("ctrl+s", move || toggled.toggle());
 
-    rsx!("toggle: {toggled.get()}")
+    rsx!("toggle: {toggled}")
 }

+ 29 - 80
examples/svg.rs

@@ -1,103 +1,52 @@
 // Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
 
 use dioxus::prelude::*;
+use rand::{thread_rng, Rng};
 
 fn main() {
     dioxus_desktop::launch(app);
 }
 
 fn app() -> Element {
-    let val = use_signal(|| 5);
-
     rsx! {
-        div {
-            user_select: "none",
-            webkit_user_select: "none",
-            margin_left: "10%",
-            margin_right: "10%",
+        div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%",
             h1 { "Click die to generate a new value" }
-            div {
-                cursor: "pointer",
-                height: "80%",
-                width: "80%",
-                Die {
-                    value: **val,
-                    keep: true,
-                    onclick: move |_| {
-                        use rand::Rng;
-                        let mut rng = rand::thread_rng();
-                        val.set(rng.gen_range(1..=6));
-                    }
-                }
-            }
+            div { cursor: "pointer", height: "100%", width: "100%", Dice {} }
         }
     }
 }
 
-#[derive(Props)]
-pub struct DieProps<'a> {
-    pub value: u64,
-    pub keep: bool,
-    pub onclick: EventHandler<'a, MouseEvent>,
-}
-
-const DOTS: [(i64, i64); 7] = [(-1, -1), (-1, -0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, 0)];
-const DOTS_FOR_VALUE: [[bool; 7]; 6] = [
-    [false, false, false, false, false, false, true],
-    [false, false, true, true, false, false, false],
-    [false, false, true, true, false, false, true],
-    [true, false, true, true, false, true, false],
-    [true, false, true, true, false, true, true],
-    [true, true, true, true, true, true, false],
-];
-
-const OFFSET: i64 = 600;
-const DOT_RADIUS: &str = "200";
-const HELD_COLOR: &str = "#aaa";
-const UNHELD_COLOR: &str = "#ddd";
-
-// A six-sided die (D6) with dots.
-#[allow(non_snake_case)]
-pub fn Die(props: DieProps) -> Element {
-    let &DieProps { value, keep, .. } = cx.props;
+#[component]
+fn Dice() -> Element {
+    const Y: bool = true;
+    const N: bool = false;
+    const DOTS: [(i64, i64); 7] = [(-1, -1), (-1, -0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, 0)];
+    const DOTS_FOR_VALUE: [[bool; 7]; 6] = [
+        [N, N, N, N, N, N, Y],
+        [N, N, Y, Y, N, N, N],
+        [N, N, Y, Y, N, N, Y],
+        [Y, N, Y, Y, N, Y, N],
+        [Y, N, Y, Y, N, Y, Y],
+        [Y, Y, Y, Y, Y, Y, N],
+    ];
+
+    let value = use_signal(|| 5);
+    let active_dots = use_selector(move || &DOTS_FOR_VALUE[(*value() - 1) as usize]);
 
-    let active_dots = &DOTS_FOR_VALUE[(value - 1) as usize];
-    let fill = if keep { HELD_COLOR } else { UNHELD_COLOR };
-    let dots = DOTS
-        .iter()
-        .zip(active_dots.iter())
-        .filter(|(_, &active)| active)
-        .map(|((x, y), _)| {
-            let dcx = x * OFFSET;
-            let dcy = y * OFFSET;
-
-            rsx! {
+    rsx! {
+        svg {
+            view_box: "-1000 -1000 2000 2000",
+            prevent_default: "onclick",
+            onclick: move |e| value.set(thread_rng().gen_range(1..=6)),
+            rect { x: -1000, y: -1000, width: 2000, height: 2000, rx: 200, fill: "#aaa" }
+            for ((x, y), _) in DOTS.iter().zip(active_dots.read().iter()).filter(|(_, &active)| active) {
                 circle {
-                    cx: "{dcx}",
-                    cy: "{dcy}",
-                    r: "{DOT_RADIUS}",
+                    cx: *x * 600,
+                    cy: *y * 600,
+                    r: 200,
                     fill: "#333"
                 }
             }
-        });
-
-    rsx! {
-      svg {
-        onclick: move |e| cx.props.onclick.call(e),
-        prevent_default: "onclick",
-        class: "die",
-        view_box: "-1000 -1000 2000 2000",
-
-        rect {
-          x: "-1000",
-          y: "-1000",
-          width: "2000",
-          height: "2000",
-          rx: "{DOT_RADIUS}",
-          fill: "{fill}",
         }
-
-        {dots}
-      }
     }
 }

+ 13 - 0
packages/core/src/nodes.rs

@@ -867,6 +867,11 @@ impl IntoVNode for VNode {
         self
     }
 }
+impl IntoVNode for &VNode {
+    fn into_vnode(self) -> VNode {
+        self.clone()
+    }
+}
 impl IntoVNode for Element {
     fn into_vnode(self) -> VNode {
         match self {
@@ -875,6 +880,14 @@ impl IntoVNode for Element {
         }
     }
 }
+impl IntoVNode for &Element {
+    fn into_vnode(self) -> VNode {
+        match self {
+            Some(val) => val.into_vnode(),
+            _ => VNode::empty().unwrap(),
+        }
+    }
+}
 
 // Note that we're using the E as a generic but this is never crafted anyways.
 pub struct FromNodeIterator;

+ 1 - 0
packages/desktop/Cargo.toml

@@ -44,6 +44,7 @@ infer = "0.11.0"
 dunce = "1.0.2"
 slab = { workspace = true }
 rustc-hash = { workspace = true }
+dioxus-hooks = { workspace = true }
 
 futures-util = { workspace = true }
 urlencoding = "2.1.2"

+ 45 - 47
packages/desktop/src/hooks.rs

@@ -1,17 +1,21 @@
+use std::rc::Rc;
+
 use crate::{
     assets::*, ipc::UserWindowEvent, shortcut::IntoAccelerator, window, DesktopContext,
     ShortcutHandle, ShortcutRegistryError, WryEventHandler,
 };
-use dioxus_core::use_hook;
+use dioxus_core::{
+    prelude::{consume_context, current_scope_id},
+    use_hook,
+};
+use dioxus_hooks::use_on_drop;
 use tao::{event::Event, event_loop::EventLoopWindowTarget};
 use wry::RequestAsyncResponder;
 
-// /// Get an imperative handle to the current window
-// pub fn use_window() -> &DesktopContext {
-//     cx.use_hook(|| cx.consume_context::<DesktopContext>())
-//         .as_ref()
-//         .unwrap()
-// }
+/// Get an imperative handle to the current window
+pub fn use_window() -> DesktopContext {
+    use_hook(|| consume_context::<DesktopContext>()).unwrap()
+}
 
 /// Get a closure that executes any JavaScript in the WebView context.
 pub fn use_wry_event_handler(
@@ -29,48 +33,42 @@ pub fn use_wry_event_handler(
     })
 }
 
-// /// Provide a callback to handle asset loading yourself.
-// ///
-// /// The callback takes a path as requested by the web view, and it should return `Some(response)`
-// /// if you want to load the asset, and `None` if you want to fallback on the default behavior.
-// pub fn use_asset_handler(
-//     ,
-//     name: &str,
-//     handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static,
-// ) {
-//     cx.use_hook(|| {
-//         crate::window().asset_handlers.register_handler(
-//             name.to_string(),
-//             Box::new(handler),
-//             cx.scope_id(),
-//         );
+/// Provide a callback to handle asset loading yourself.
+///
+/// The callback takes a path as requested by the web view, and it should return `Some(response)`
+/// if you want to load the asset, and `None` if you want to fallback on the default behavior.
+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(),
+        );
 
-//         Handler(name.to_string())
-//     });
+        Rc::new(name.to_string())
+    });
 
-//     // todo: can we just put ondrop in core?
-//     struct Handler(String);
-//     impl Drop for Handler {
-//         fn drop(&mut self) {
-//             _ = crate::window().asset_handlers.remove_handler(&self.0);
-//         }
-//     }
-// }
+    use_on_drop(move || {
+        _ = crate::window().asset_handlers.remove_handler(name.as_ref());
+    });
+}
 
-// /// Get a closure that executes any JavaScript in the WebView context.
-// pub fn use_global_shortcut(
-//     ,
-//     accelerator: impl IntoAccelerator,
-//     handler: impl FnMut() + 'static,
-// ) -> &Result<ShortcutHandle, ShortcutRegistryError> {
-//     cx.use_hook(move || {
-//         let desktop = window();
+/// Get a closure that executes any JavaScript in the WebView context.
+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);
+        let id = desktop.create_shortcut(accelerator.accelerator(), handler);
 
-//         Ok(ShortcutHandle {
-//             desktop,
-//             shortcut_id: id?,
-//         })
-//     })
-// }
+        Ok(ShortcutHandle {
+            desktop,
+            shortcut_id: id?,
+        })
+    })
+}

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

@@ -43,7 +43,7 @@ pub use config::{Config, WindowCloseBehaviour};
 pub use desktop_context::{
     window, DesktopContext, DesktopService, WryEventHandler, WryEventHandlerId,
 };
-// pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler};
+pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler};
 pub use shortcut::{ShortcutHandle, ShortcutId, ShortcutRegistryError};
 pub use wry::RequestAsyncResponder;
 

+ 2 - 2
packages/hooks/src/lib.rs

@@ -57,8 +57,8 @@ macro_rules! to_owned {
 
 // pub mod computed;
 
-// mod use_on_destroy;
-// pub use use_on_destroy::*;
+mod use_on_destroy;
+pub use use_on_destroy::*;
 
 // mod use_const;
 // pub use use_const::*;

+ 29 - 12
packages/hooks/src/use_on_destroy.rs

@@ -1,9 +1,11 @@
+use dioxus_core::use_hook;
+
 #[deprecated(
     note = "Use `use_on_destroy` instead, which has the same functionality. \
 This is deprecated because of the introduction of `use_on_create` which is better mirrored by `use_on_destroy`. \
 The reason why `use_on_create` is not `use_on_mount` is because of potential confusion with `dioxus::events::onmounted`."
 )]
-pub fn use_on_unmount<D: FnOnce() + 'static>(cx: &dioxus_core::ScopeState, destroy: D) {
+pub fn use_on_unmount<D: FnOnce() + 'static>(destroy: D) {
     use_on_destroy(destroy);
 }
 
@@ -67,19 +69,34 @@ pub fn use_on_unmount<D: FnOnce() + 'static>(cx: &dioxus_core::ScopeState, destr
 ///     }
 /// }
 /// ```
-pub fn use_on_destroy<D: FnOnce() + 'static>(cx: &dioxus_core::ScopeState, destroy: D) {
-    cx.use_hook(|| LifeCycle {
+pub fn use_on_destroy<D: FnOnce() + 'static>(destroy: D) {
+    struct LifeCycle<D: FnOnce()> {
+        /// Wrap the closure in an option so that we can take it out on drop.
+        ondestroy: Option<D>,
+    }
+
+    /// On drop, we want to run the closure.
+    impl<D: FnOnce()> Drop for LifeCycle<D> {
+        fn drop(&mut self) {
+            if let Some(f) = self.ondestroy.take() {
+                f();
+            }
+        }
+    }
+
+    // We need to impl clone for the lifecycle, but we don't want the drop handler for the closure to be called twice.
+    impl<D: FnOnce()> Clone for LifeCycle<D> {
+        fn clone(&self) -> Self {
+            Self { ondestroy: None }
+        }
+    }
+
+    use_hook(|| LifeCycle {
         ondestroy: Some(destroy),
     });
 }
 
-struct LifeCycle<D: FnOnce()> {
-    ondestroy: Option<D>,
-}
-
-impl<D: FnOnce()> Drop for LifeCycle<D> {
-    fn drop(&mut self) {
-        let f = self.ondestroy.take().unwrap();
-        f();
-    }
+/// Creates a callback that will be run before the component is dropped
+pub fn use_on_drop<D: FnOnce() + 'static>(ondrop: D) {
+    use_on_destroy(ondrop);
 }