1
0
Jonathan Kelley 2 жил өмнө
parent
commit
1b14b309e9

+ 10 - 22
examples/calculator.rs

@@ -25,6 +25,7 @@ fn app(cx: Scope) -> Element {
         if val.get() == "0" {
             val.set(String::new());
         }
+
         val.make_mut().push_str(num.to_string().as_str());
     };
 
@@ -99,12 +100,8 @@ fn app(cx: Scope) -> Element {
                                 }
                             }
                             div { class: "digit-keys",
-                                button { class: "calculator-key key-0", onclick: move |_| input_digit(0),
-                                    "0"
-                                }
-                                button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'),
-                                    "●"
-                                }
+                                button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" }
+                                button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" }
                                 (1..10).map(|k| rsx!{
                                     button {
                                         class: "calculator-key {k}",
@@ -116,22 +113,13 @@ fn app(cx: Scope) -> Element {
                             }
                         }
                         div { class: "operator-keys",
-                            button { class: "calculator-key key-divide", onclick: move |_| input_operator("/"),
-                                "÷"
-                            }
-                            button { class: "calculator-key key-multiply", onclick: move |_| input_operator("*"),
-                                "×"
-                            }
-                            button { class: "calculator-key key-subtract", onclick: move |_| input_operator("-"),
-                                "−"
-                            }
-                            button { class: "calculator-key key-add", onclick: move |_| input_operator("+"),
-                                "+"
-                            }
-                            button { class: "calculator-key key-equals",
-                                onclick: move |_| {
-                                    val.set(format!("{}", calc_val(val.as_str())));
-                                },
+                            button { class: "calculator-key key-divide", onclick: move |_| input_operator("/"), "÷" }
+                            button { class: "calculator-key key-multiply", onclick: move |_| input_operator("*"), "×" }
+                            button { class: "calculator-key key-subtract", onclick: move |_| input_operator("-"), "−" }
+                            button { class: "calculator-key key-add", onclick: move |_| input_operator("+"), "+" }
+                            button {
+                                class: "calculator-key key-equals",
+                                onclick: move |_| val.set(format!("{}", calc_val(val.as_str()))),
                                 "="
                             }
                         }

+ 2 - 2
examples/callback.rs

@@ -5,7 +5,7 @@ fn main() {
 }
 
 fn app(cx: Scope) -> Element {
-    let login = use_callback!(cx, || move |evt: MouseEvent| async move {
+    let login = use_callback!(cx, move |_| async move {
         let res = reqwest::get("https://dog.ceo/api/breeds/list/all")
             .await
             .unwrap()
@@ -13,7 +13,7 @@ fn app(cx: Scope) -> Element {
             .await
             .unwrap();
 
-        println!("{}, ", res);
+        println!("{:#?}, ", res);
     });
 
     cx.render(rsx! {

+ 1 - 1
examples/nested_listeners.rs

@@ -22,7 +22,7 @@ fn app(cx: Scope) -> Element {
             button {
                 onclick: move |evt| {
                     println!("clicked! bottom no bubbling");
-                    evt.cancel_bubble();
+                    evt.stop_propogation();
                 },
                 "Dont propogate"
             }

+ 1 - 1
examples/rsx_compile_fail.rs

@@ -6,7 +6,7 @@ use dioxus_ssr::config::SsrConfig;
 
 fn main() {
     let mut vdom = VirtualDom::new(example);
-    vdom.rebuild();
+    _ = vdom.rebuild();
 
     let out = dioxus_ssr::render_vdom_cfg(&vdom, SsrConfig::default().newline(true).indent(true));
     println!("{}", out);

+ 0 - 32
examples/simple_todo.rs

@@ -1,32 +0,0 @@
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let mut idx = use_state(cx, || 0);
-    let onhover = |h| println!("go!");
-
-    cx.render(rsx! {
-        div {
-            button { onclick: move |_| idx += 1, "+" }
-            button { onclick: move |_| idx -= 1, "-" }
-            ul {
-                (0..**idx).map(|i| rsx! {
-                    Child { i: i, onhover: onhover }
-                })
-            }
-        }
-    })
-}
-
-#[inline_props]
-fn Child<'a>(cx: Scope<'a>, i: i32, onhover: EventHandler<'a, MouseEvent>) -> Element {
-    cx.render(rsx! {
-        li {
-            onmouseover: move |e| onhover.call(e),
-            "{i}"
-        }
-    })
-}

+ 0 - 16
examples/suspense2.rs

@@ -1,16 +0,0 @@
-use dioxus::prelude::*;
-use dioxus_desktop::{Config, LogicalSize, WindowBuilder};
-
-fn main() {
-    dioxus_desktop::launch(|cx| cx.render(rsx! { async_app {} }));
-}
-
-async fn async_app(cx: Scope<'_>) -> Element {
-    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
-
-    cx.render(rsx! {
-        div {
-            "hi!"
-        }
-    })
-}

+ 6 - 6
examples/window_event.rs

@@ -35,13 +35,13 @@ fn app(cx: Scope) -> Element {
                 nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center" }
                 button {
                     class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
-                    onmousedown: |evt| evt.cancel_bubble(),
+                    onmousedown: |evt| evt.stop_propogation(),
                     onclick: move |_| window.set_minimized(true),
                     "Minimize"
                 }
                 button {
                     class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
-                    onmousedown: |evt| evt.cancel_bubble(),
+                    onmousedown: |evt| evt.stop_propogation(),
                     onclick: move |_| {
 
                         window.set_fullscreen(!**fullscreen);
@@ -52,7 +52,7 @@ fn app(cx: Scope) -> Element {
                 }
                 button {
                     class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
-                    onmousedown: |evt| evt.cancel_bubble(),
+                    onmousedown: |evt| evt.stop_propogation(),
                     onclick: move |_| window.close(),
                     "Close"
                 }
@@ -66,7 +66,7 @@ fn app(cx: Scope) -> Element {
                 div {
                     button {
                         class: "inline-flex items-center text-white bg-green-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
-                        onmousedown: |evt| evt.cancel_bubble(),
+                        onmousedown: |evt| evt.stop_propogation(),
                         onclick: move |_| {
                             window.set_always_on_top(!always_on_top);
                             always_on_top.set(!always_on_top);
@@ -77,7 +77,7 @@ fn app(cx: Scope) -> Element {
                 div {
                     button {
                         class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
-                        onmousedown: |evt| evt.cancel_bubble(),
+                        onmousedown: |evt| evt.stop_propogation(),
                         onclick: move |_| {
                             window.set_decorations(!decorations);
                             decorations.set(!decorations);
@@ -88,7 +88,7 @@ fn app(cx: Scope) -> Element {
                 div {
                     button {
                         class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
-                        onmousedown: |evt| evt.cancel_bubble(),
+                        onmousedown: |evt| evt.stop_propogation(),
                         onclick: move |_| window.set_title("Dioxus Application"),
                         "Change Title"
                     }

+ 12 - 3
packages/core/src/nodes.rs

@@ -159,15 +159,24 @@ pub enum TemplateNode<'a> {
     },
 
     /// This template node is just a piece of static text
-    Text { text: &'a str },
+    Text {
+        /// The actual text
+        text: &'a str,
+    },
 
     /// This template node is unknown, and needs to be created at runtime.
-    Dynamic { id: usize },
+    Dynamic {
+        /// The index of the dynamic node in the VNode's dynamic_nodes list
+        id: usize,
+    },
 
     /// This template node is known to be some text, but needs to be created at runtime
     ///
     /// This is separate from the pure Dynamic variant for various optimizations
-    DynamicText { id: usize },
+    DynamicText {
+        /// The index of the dynamic node in the VNode's dynamic_nodes list
+        id: usize,
+    },
 }
 
 /// A node created at runtime

+ 3 - 2
packages/desktop/Cargo.toml

@@ -33,7 +33,7 @@ webbrowser = "0.8.0"
 infer = "0.11.0"
 dunce = "1.0.2"
 
-interprocess = { version = "1.1.1" }
+interprocess = { version = "1.1.1", optional = true}
 futures-util = "0.3.25"
 
 [target.'cfg(target_os = "ios")'.dependencies]
@@ -44,11 +44,12 @@ objc_id = "0.1.1"
 core-foundation = "0.9.3"
 
 [features]
-default = ["tokio_runtime"]
+default = ["tokio_runtime", "hot-reload"]
 tokio_runtime = ["tokio"]
 fullscreen = ["wry/fullscreen"]
 transparent = ["wry/transparent"]
 tray = ["wry/tray"]
+hot-reload = ["interprocess"]
 
 [dev-dependencies]
 dioxus-core-macro = { path = "../core-macro" }

+ 12 - 2
packages/desktop/src/controller.rs

@@ -1,7 +1,7 @@
 use crate::desktop_context::{DesktopContext, UserWindowEvent};
 use crate::events::{decode_event, EventMessage};
 use dioxus_core::*;
-use futures_channel::mpsc::UnboundedReceiver;
+use futures_channel::mpsc::{unbounded, UnboundedSender};
 use futures_util::StreamExt;
 #[cfg(target_os = "ios")]
 use objc::runtime::Object;
@@ -22,6 +22,9 @@ pub(super) struct DesktopController {
     pub(super) pending_edits: Arc<Mutex<Vec<String>>>,
     pub(super) quit_app_on_close: bool,
     pub(super) is_ready: Arc<AtomicBool>,
+    pub(super) proxy: EventLoopProxy<UserWindowEvent>,
+    pub(super) event_tx: UnboundedSender<serde_json::Value>,
+
     #[cfg(target_os = "ios")]
     pub(super) views: Vec<*mut Object>,
 }
@@ -33,9 +36,10 @@ impl DesktopController {
         root: Component<P>,
         props: P,
         proxy: EventLoopProxy<UserWindowEvent>,
-        mut event_rx: UnboundedReceiver<serde_json::Value>,
     ) -> Self {
         let edit_queue = Arc::new(Mutex::new(Vec::new()));
+        let (event_tx, mut event_rx) = unbounded();
+        let proxy2 = proxy.clone();
 
         let pending_edits = edit_queue.clone();
         let desktop_context_proxy = proxy.clone();
@@ -87,6 +91,8 @@ impl DesktopController {
             webviews: HashMap::new(),
             is_ready: Arc::new(AtomicBool::new(false)),
             quit_app_on_close: true,
+            proxy: proxy2,
+            event_tx,
             #[cfg(target_os = "ios")]
             views: vec![],
         }
@@ -117,4 +123,8 @@ impl DesktopController {
             }
         }
     }
+
+    pub(crate) fn set_template(&self, serialized_template: String) {
+        todo!()
+    }
 }

+ 89 - 80
packages/desktop/src/desktop_context.rs

@@ -168,6 +168,11 @@ pub enum UserWindowEvent {
     DragWindow,
     FocusWindow,
 
+    /// Set a new Dioxus template for hot-reloading
+    ///
+    /// Is a no-op in release builds. Must fit the right format for templates
+    SetTemplate(String),
+
     Visible(bool),
     Minimize(bool),
     Maximize(bool),
@@ -195,97 +200,101 @@ pub enum UserWindowEvent {
     PopView,
 }
 
-pub(super) fn handler(
-    user_event: UserWindowEvent,
-    desktop: &mut DesktopController,
-    control_flow: &mut ControlFlow,
-) {
-    // currently dioxus-desktop supports a single window only,
-    // so we can grab the only webview from the map;
-    // on wayland it is possible that a user event is emitted
-    // before the webview is initialized. ignore the event.
-    let webview = if let Some(webview) = desktop.webviews.values().next() {
-        webview
-    } else {
-        return;
-    };
-    let window = webview.window();
-
-    match user_event {
-        Initialize | EditsReady => desktop.try_load_ready_webviews(),
-        CloseWindow => *control_flow = ControlFlow::Exit,
-        DragWindow => {
-            // if the drag_window has any errors, we don't do anything
-            window.fullscreen().is_none().then(|| window.drag_window());
-        }
-        Visible(state) => window.set_visible(state),
-        Minimize(state) => window.set_minimized(state),
-        Maximize(state) => window.set_maximized(state),
-        MaximizeToggle => window.set_maximized(!window.is_maximized()),
-        Fullscreen(state) => {
-            if let Some(handle) = window.current_monitor() {
-                window.set_fullscreen(state.then_some(WryFullscreen::Borderless(Some(handle))));
+impl DesktopController {
+    pub(super) fn handle_event(
+        &mut self,
+        user_event: UserWindowEvent,
+        control_flow: &mut ControlFlow,
+    ) {
+        // currently dioxus-desktop supports a single window only,
+        // so we can grab the only webview from the map;
+        // on wayland it is possible that a user event is emitted
+        // before the webview is initialized. ignore the event.
+        let webview = if let Some(webview) = self.webviews.values().next() {
+            webview
+        } else {
+            return;
+        };
+
+        let window = webview.window();
+
+        match user_event {
+            Initialize | EditsReady => self.try_load_ready_webviews(),
+            SetTemplate(template) => self.set_template(template),
+            CloseWindow => *control_flow = ControlFlow::Exit,
+            DragWindow => {
+                // if the drag_window has any errors, we don't do anything
+                window.fullscreen().is_none().then(|| window.drag_window());
             }
-        }
-        FocusWindow => window.set_focus(),
-        Resizable(state) => window.set_resizable(state),
-        AlwaysOnTop(state) => window.set_always_on_top(state),
+            Visible(state) => window.set_visible(state),
+            Minimize(state) => window.set_minimized(state),
+            Maximize(state) => window.set_maximized(state),
+            MaximizeToggle => window.set_maximized(!window.is_maximized()),
+            Fullscreen(state) => {
+                if let Some(handle) = window.current_monitor() {
+                    window.set_fullscreen(state.then_some(WryFullscreen::Borderless(Some(handle))));
+                }
+            }
+            FocusWindow => window.set_focus(),
+            Resizable(state) => window.set_resizable(state),
+            AlwaysOnTop(state) => window.set_always_on_top(state),
 
-        CursorVisible(state) => window.set_cursor_visible(state),
-        CursorGrab(state) => {
-            let _ = window.set_cursor_grab(state);
-        }
+            CursorVisible(state) => window.set_cursor_visible(state),
+            CursorGrab(state) => {
+                let _ = window.set_cursor_grab(state);
+            }
 
-        SetTitle(content) => window.set_title(&content),
-        SetDecorations(state) => window.set_decorations(state),
+            SetTitle(content) => window.set_title(&content),
+            SetDecorations(state) => window.set_decorations(state),
 
-        SetZoomLevel(scale_factor) => webview.zoom(scale_factor),
+            SetZoomLevel(scale_factor) => webview.zoom(scale_factor),
 
-        Print => {
-            if let Err(e) = webview.print() {
-                // we can't panic this error.
-                log::warn!("Open print modal failed: {e}");
+            Print => {
+                if let Err(e) = webview.print() {
+                    // we can't panic this error.
+                    log::warn!("Open print modal failed: {e}");
+                }
+            }
+            DevTool => {
+                #[cfg(debug_assertions)]
+                webview.open_devtools();
+                #[cfg(not(debug_assertions))]
+                log::warn!("Devtools are disabled in release builds");
             }
-        }
-        DevTool => {
-            #[cfg(debug_assertions)]
-            webview.open_devtools();
-            #[cfg(not(debug_assertions))]
-            log::warn!("Devtools are disabled in release builds");
-        }
 
-        Eval(code) => {
-            if let Err(e) = webview.evaluate_script(code.as_str()) {
-                // we can't panic this error.
-                log::warn!("Eval script error: {e}");
+            Eval(code) => {
+                if let Err(e) = webview.evaluate_script(code.as_str()) {
+                    // we can't panic this error.
+                    log::warn!("Eval script error: {e}");
+                }
             }
-        }
 
-        #[cfg(target_os = "ios")]
-        PushView(view) => unsafe {
-            use objc::runtime::Object;
-            use objc::*;
-            assert!(is_main_thread());
-            let ui_view = window.ui_view() as *mut Object;
-            let ui_view_frame: *mut Object = msg_send![ui_view, frame];
-            let _: () = msg_send![view, setFrame: ui_view_frame];
-            let _: () = msg_send![view, setAutoresizingMask: 31];
-
-            let ui_view_controller = window.ui_view_controller() as *mut Object;
-            let _: () = msg_send![ui_view_controller, setView: view];
-            desktop.views.push(ui_view);
-        },
-
-        #[cfg(target_os = "ios")]
-        PopView => unsafe {
-            use objc::runtime::Object;
-            use objc::*;
-            assert!(is_main_thread());
-            if let Some(view) = desktop.views.pop() {
+            #[cfg(target_os = "ios")]
+            PushView(view) => unsafe {
+                use objc::runtime::Object;
+                use objc::*;
+                assert!(is_main_thread());
+                let ui_view = window.ui_view() as *mut Object;
+                let ui_view_frame: *mut Object = msg_send![ui_view, frame];
+                let _: () = msg_send![view, setFrame: ui_view_frame];
+                let _: () = msg_send![view, setAutoresizingMask: 31];
+
                 let ui_view_controller = window.ui_view_controller() as *mut Object;
                 let _: () = msg_send![ui_view_controller, setView: view];
-            }
-        },
+                desktop.views.push(ui_view);
+            },
+
+            #[cfg(target_os = "ios")]
+            PopView => unsafe {
+                use objc::runtime::Object;
+                use objc::*;
+                assert!(is_main_thread());
+                if let Some(view) = desktop.views.pop() {
+                    let ui_view_controller = window.ui_view_controller() as *mut Object;
+                    let _: () = msg_send![ui_view_controller, setView: view];
+                }
+            },
+        }
     }
 }
 

+ 13 - 10
packages/desktop/src/hot_reload.rs

@@ -1,28 +1,31 @@
 use dioxus_core::VirtualDom;
-use interprocess::local_socket::LocalSocketStream;
-// use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
+
+use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
 use std::io::{BufRead, BufReader};
 use std::time::Duration;
 use std::{sync::Arc, sync::Mutex};
 
-fn _handle_error(connection: std::io::Result<LocalSocketStream>) -> Option<LocalSocketStream> {
+fn handle_error(connection: std::io::Result<LocalSocketStream>) -> Option<LocalSocketStream> {
     connection
         .map_err(|error| eprintln!("Incoming connection failed: {}", error))
         .ok()
 }
 
-pub(crate) fn _init(_dom: &VirtualDom) {
+pub(crate) fn init(_dom: &VirtualDom) {
     let latest_in_connection: Arc<Mutex<Option<BufReader<LocalSocketStream>>>> =
         Arc::new(Mutex::new(None));
-    let _latest_in_connection_handle = latest_in_connection.clone();
+
+    let latest_in_connection_handle = latest_in_connection.clone();
 
     // connect to processes for incoming data
     std::thread::spawn(move || {
-        // if let Ok(listener) = LocalSocketListener::bind("@dioxusin") {
-        //     for conn in listener.incoming().filter_map(handle_error) {
-        //         *latest_in_connection_handle.lock().unwrap() = Some(BufReader::new(conn));
-        //     }
-        // }
+        let temp_file = std::env::temp_dir().join("@dioxusin");
+
+        if let Ok(listener) = LocalSocketListener::bind(temp_file) {
+            for conn in listener.incoming().filter_map(handle_error) {
+                *latest_in_connection_handle.lock().unwrap() = Some(BufReader::new(conn));
+            }
+        }
     });
 
     std::thread::spawn(move || {

+ 127 - 113
packages/desktop/src/lib.rs

@@ -8,13 +8,17 @@ mod controller;
 mod desktop_context;
 mod escape;
 mod events;
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-mod hot_reload;
 mod protocol;
 
+#[cfg(all(feature = "hot-reload", debug_assertions))]
+mod hot_reload;
+
+use std::sync::atomic::AtomicBool;
+use std::sync::Arc;
+
 use desktop_context::UserWindowEvent;
 pub use desktop_context::{use_eval, use_window, DesktopContext};
-use futures_channel::mpsc::unbounded;
+use futures_channel::mpsc::UnboundedSender;
 pub use wry;
 pub use wry::application as tao;
 
@@ -100,15 +104,68 @@ pub fn launch_cfg(root: Component, config_builder: Config) {
 /// ```
 pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cfg: Config) {
     let event_loop = EventLoop::with_user_event();
+    let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
 
-    let (event_tx, event_rx) = unbounded();
-    let mut desktop =
-        DesktopController::new_on_tokio(root, props, event_loop.create_proxy(), event_rx);
-    let proxy = event_loop.create_proxy();
+    event_loop.run(move |window_event, event_loop, control_flow| {
+        *control_flow = ControlFlow::Wait;
+
+        match window_event {
+            Event::NewEvents(StartCause::Init) => desktop.start(&mut cfg, event_loop),
+
+            Event::WindowEvent {
+                event, window_id, ..
+            } => match event {
+                WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
+                WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
+                _ => {}
+            },
+
+            Event::UserEvent(user_event) => desktop.handle_event(user_event, control_flow),
+            Event::MainEventsCleared => {}
+            Event::Resumed => {}
+            Event::Suspended => {}
+            Event::LoopDestroyed => {}
+            Event::RedrawRequested(_id) => {}
+            _ => {}
+        }
+    })
+}
+
+impl DesktopController {
+    fn start(
+        &mut self,
+        cfg: &mut Config,
+        event_loop: &tao::event_loop::EventLoopWindowTarget<UserWindowEvent>,
+    ) {
+        let webview = build_webview(
+            cfg,
+            event_loop,
+            self.is_ready.clone(),
+            self.proxy.clone(),
+            self.event_tx.clone(),
+        );
+
+        self.webviews.insert(webview.window().id(), webview);
+    }
+}
 
-    // We assume that if the icon is None, then the user just didnt set it
+fn build_webview(
+    cfg: &mut Config,
+    event_loop: &tao::event_loop::EventLoopWindowTarget<UserWindowEvent>,
+    is_ready: Arc<AtomicBool>,
+    proxy: tao::event_loop::EventLoopProxy<UserWindowEvent>,
+    event_tx: UnboundedSender<serde_json::Value>,
+) -> wry::webview::WebView {
+    let builder = cfg.window.clone();
+    let window = builder.build(event_loop).unwrap();
+    let file_handler = cfg.file_drop_handler.take();
+    let custom_head = cfg.custom_head.clone();
+    let resource_dir = cfg.resource_dir.clone();
+    let index_file = cfg.custom_index.clone();
+
+    // We assume that if the icon is None in cfg, then the user just didnt set it
     if cfg.window.window.window_icon.is_none() {
-        cfg.window = cfg.window.with_window_icon(Some(
+        window.set_window_icon(Some(
             tao::window::Icon::from_rgba(
                 include_bytes!("./assets/default_icon.bin").to_vec(),
                 460,
@@ -118,84 +175,62 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
         ));
     }
 
-    event_loop.run(move |window_event, event_loop, control_flow| {
-        *control_flow = ControlFlow::Wait;
-
-        // println!("window event: {:?}", window_event);
-        match window_event {
-            Event::NewEvents(StartCause::Init) => {
-                let builder = cfg.window.clone();
-
-                let window = builder.build(event_loop).unwrap();
-                let window_id = window.id();
-
-                let (is_ready, _) = (desktop.is_ready.clone(), ());
-                // let (is_ready, sender) = (desktop.is_ready.clone(), desktop.sender.clone());
-
-                let proxy = proxy.clone();
-
-                let file_handler = cfg.file_drop_handler.take();
-                let custom_head = cfg.custom_head.clone();
-                let resource_dir = cfg.resource_dir.clone();
-                let index_file = cfg.custom_index.clone();
-                let event_tx = event_tx.clone();
-
-                let mut webview = WebViewBuilder::new(window)
-                    .unwrap()
-                    .with_transparent(cfg.window.window.transparent)
-                    .with_url("dioxus://index.html/")
-                    .unwrap()
-                    .with_ipc_handler(move |_window: &Window, payload: String| {
-                        parse_ipc_message(&payload)
-                            .map(|message| match message.method() {
-                                "user_event" => {
-                                    _ = event_tx.unbounded_send(message.params());
+    let mut webview = WebViewBuilder::new(window)
+        .unwrap()
+        .with_transparent(cfg.window.window.transparent)
+        .with_url("dioxus://index.html/")
+        .unwrap()
+        .with_ipc_handler(move |_window: &Window, payload: String| {
+            parse_ipc_message(&payload)
+                .map(|message| match message.method() {
+                    "user_event" => {
+                        _ = event_tx.unbounded_send(message.params());
+                    }
+                    "initialize" => {
+                        is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
+                        let _ = proxy.send_event(UserWindowEvent::EditsReady);
+                    }
+                    "browser_open" => {
+                        let data = message.params();
+                        log::trace!("Open browser: {:?}", data);
+                        if let Some(temp) = data.as_object() {
+                            if temp.contains_key("href") {
+                                let url = temp.get("href").unwrap().as_str().unwrap();
+                                if let Err(e) = webbrowser::open(url) {
+                                    log::error!("Open Browser error: {:?}", e);
                                 }
-                                "initialize" => {
-                                    is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
-                                    let _ = proxy.send_event(UserWindowEvent::EditsReady);
-                                }
-                                "browser_open" => {
-                                    let data = message.params();
-                                    log::trace!("Open browser: {:?}", data);
-                                    if let Some(temp) = data.as_object() {
-                                        if temp.contains_key("href") {
-                                            let url = temp.get("href").unwrap().as_str().unwrap();
-                                            if let Err(e) = webbrowser::open(url) {
-                                                log::error!("Open Browser error: {:?}", e);
-                                            }
-                                        }
-                                    }
-                                }
-                                _ => (),
-                            })
-                            .unwrap_or_else(|| {
-                                log::warn!("invalid IPC message received");
-                            });
-                    })
-                    .with_custom_protocol(String::from("dioxus"), move |r| {
-                        protocol::desktop_handler(
-                            r,
-                            resource_dir.clone(),
-                            custom_head.clone(),
-                            index_file.clone(),
-                        )
-                    })
-                    .with_file_drop_handler(move |window, evet| {
-                        file_handler
-                            .as_ref()
-                            .map(|handler| handler(window, evet))
-                            .unwrap_or_default()
-                    });
-
-                for (name, handler) in cfg.protocols.drain(..) {
-                    webview = webview.with_custom_protocol(name, handler)
-                }
-
-                if cfg.disable_context_menu {
-                    // in release mode, we don't want to show the dev tool or reload menus
-                    webview = webview.with_initialization_script(
-                        r#"
+                            }
+                        }
+                    }
+                    _ => (),
+                })
+                .unwrap_or_else(|| {
+                    log::warn!("invalid IPC message received");
+                });
+        })
+        .with_custom_protocol(String::from("dioxus"), move |r| {
+            protocol::desktop_handler(
+                r,
+                resource_dir.clone(),
+                custom_head.clone(),
+                index_file.clone(),
+            )
+        })
+        .with_file_drop_handler(move |window, evet| {
+            file_handler
+                .as_ref()
+                .map(|handler| handler(window, evet))
+                .unwrap_or_default()
+        });
+
+    for (name, handler) in cfg.protocols.drain(..) {
+        webview = webview.with_custom_protocol(name, handler)
+    }
+
+    if cfg.disable_context_menu {
+        // in release mode, we don't want to show the dev tool or reload menus
+        webview = webview.with_initialization_script(
+            r#"
                         if (document.addEventListener) {
                         document.addEventListener('contextmenu', function(e) {
                             e.preventDefault();
@@ -206,32 +241,11 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
                         });
                         }
                     "#,
-                    )
-                } else {
-                    // in debug, we are okay with the reload menu showing and dev tool
-                    webview = webview.with_devtools(true);
-                }
-
-                desktop.webviews.insert(window_id, webview.build().unwrap());
-            }
-
-            Event::WindowEvent {
-                event, window_id, ..
-            } => match event {
-                WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
-                WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
-                _ => {}
-            },
+        )
+    } else {
+        // in debug, we are okay with the reload menu showing and dev tool
+        webview = webview.with_devtools(true);
+    }
 
-            Event::UserEvent(user_event) => {
-                desktop_context::handler(user_event, &mut desktop, control_flow)
-            }
-            Event::MainEventsCleared => {}
-            Event::Resumed => {}
-            Event::Suspended => {}
-            Event::LoopDestroyed => {}
-            Event::RedrawRequested(_id) => {}
-            _ => {}
-        }
-    })
+    webview.build().unwrap()
 }

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

@@ -17,6 +17,12 @@ macro_rules! use_callback {
             move || $($rest)*
         )
     };
+    ($cx:ident, $($rest:tt)*) => {
+        use_callback(
+            $cx,
+            move || $($rest)*
+        )
+    };
 }
 pub fn use_callback<'a, T, R, F>(cx: &'a ScopeState, make: impl FnOnce() -> R) -> impl FnMut(T) + 'a
 where

+ 3 - 0
packages/interpreter/src/bindings.rs

@@ -17,6 +17,9 @@ extern "C" {
     #[wasm_bindgen(method)]
     pub fn MountToRoot(this: &Interpreter);
 
+    #[wasm_bindgen(method)]
+    pub fn AppendChildren(this: &Interpreter, m: u32);
+
     #[wasm_bindgen(method)]
     pub fn AssignId(this: &Interpreter, path: &[u8], id: u32);
 

+ 10 - 2
packages/interpreter/src/interpreter.js

@@ -210,7 +210,6 @@ export class Interpreter {
     }
   }
   handleEdits(edits) {
-
     for (let template of edits.templates) {
       this.SaveTemplate(template);
     }
@@ -276,7 +275,16 @@ export class Interpreter {
   }
   HydrateText(path, value, id) {
     let node = this.LoadChild(path);
-    node.textContent = value;
+
+    if (node.nodeType == Node.TEXT_NODE) {
+      node.textContent = value;
+    } else {
+      // replace with a textnode
+      let text = document.createTextNode(value);
+      node.replaceWith(text);
+      node = text;
+    }
+
     this.nodes[id] = node;
   }
   ReplacePlaceholder(path, m) {

+ 5 - 0
packages/native-core/src/real_dom.rs

@@ -220,7 +220,12 @@ impl<S: State> RealDom<S> {
                     let node = self.tree.get_mut(node_id).unwrap();
                     if let NodeType::Text { text } = &mut node.node_data.node_type {
                         *text = value.to_string();
+                    } else {
+                        node.node_data.node_type = NodeType::Text {
+                            text: value.to_string(),
+                        };
                     }
+
                     mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated);
                 }
                 LoadTemplate { name, index, id } => {

+ 5 - 7
packages/web/src/dom.rs

@@ -14,15 +14,14 @@ use futures_channel::mpsc;
 use js_sys::Function;
 use std::{any::Any, rc::Rc};
 use wasm_bindgen::{closure::Closure, JsCast, JsValue};
-use web_sys::{Document, Element, Event, HtmlElement};
+use web_sys::{Document, Element, Event};
 
 use crate::Config;
 
 pub struct WebsysDom {
-    document: Document,
     interpreter: Interpreter,
     handler: Closure<dyn FnMut(&Event)>,
-    root: Element,
+    _root: Element,
 }
 
 impl WebsysDom {
@@ -36,9 +35,8 @@ impl WebsysDom {
         };
 
         Self {
-            document,
             interpreter: Interpreter::new(root.clone()),
-            root,
+            _root: root,
             handler: Closure::wrap(Box::new(move |event: &web_sys::Event| {
                 let _ = event_channel.unbounded_send(event.clone());
             })),
@@ -63,7 +61,7 @@ impl WebsysDom {
         let i = &self.interpreter;
         for edit in edits.drain(..) {
             match edit {
-                AppendChildren { id, m } => i.AppendChildren(id, m),
+                AppendChildren { id, m } => i.AppendChildren(m as u32),
                 AssignId { path, id } => i.AssignId(path, id.0 as u32),
                 CreatePlaceholder { id } => i.CreatePlaceholder(id.0 as u32),
                 CreateTextNode { value, id } => i.CreateTextNode(value.into(), id.0 as u32),
@@ -87,8 +85,8 @@ impl WebsysDom {
                     self.interpreter.NewEventListener(
                         name,
                         id.0 as u32,
-                        self.handler.as_ref().unchecked_ref(),
                         event_bubbles(&name[2..]),
+                        self.handler.as_ref().unchecked_ref(),
                     );
                 }
                 RemoveEventListener { name, id } => i.RemoveEventListener(name, id.0 as u32),

+ 1 - 0
packages/web/src/lib.rs

@@ -211,6 +211,7 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
         let mut res = {
             let work = dom.wait_for_work().fuse();
             pin_mut!(work);
+
             futures_util::select! {
                 _ = work => None,
                 new_template = hotreload_rx.next() => {