diff --git a/Cargo.toml b/Cargo.toml index 89e938f3..21857dca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,19 +32,16 @@ edition = "2021" description = "Top level crate for Blitz" license = "MIT OR Apache-2.0" keywords = ["dom", "ui", "gui", "react", "wasm"] -rust-version = "1.60.0" +rust-version = "1.70.0" publish = false [profile.dev.package."*"] opt-level = 2 # Need to force specific versions of these dependencies -[dependencies] +[dev-dependencies] # webrender = "0.61.0" -euclid = { version = "0.22", features = ["serde"] } # mozbuild = "0.1.0" - -[dev-dependencies] blitz = { path = "./packages/blitz" } blitz-dom = { path = "./packages/dom" } comrak = { version = "0.21.0", default-features = false } diff --git a/packages/dioxus-blitz/Cargo.toml b/packages/dioxus-blitz/Cargo.toml index 032374fa..dff82f40 100644 --- a/packages/dioxus-blitz/Cargo.toml +++ b/packages/dioxus-blitz/Cargo.toml @@ -4,14 +4,17 @@ version = "0.0.0" edition = "2021" [features] +default = ["hot-reload", "menu"] +hot-reload = ["dep:dioxus-cli-config", "dep:dioxus-hot-reload"] menu = ["dep:muda"] -default = ["menu"] [dependencies] winit = { version = "0.30.2", features = ["rwh_06"] } muda = { version = "0.11.5", features = ["serde"], optional = true } tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } +dioxus-cli-config = { git = "https://github.com/dioxuslabs/dioxus", rev = "a3aa6ae771a2d0a4d8cb6055c41efc0193b817ef", optional = true } +dioxus-hot-reload = { git = "https://github.com/dioxuslabs/dioxus", rev = "a3aa6ae771a2d0a4d8cb6055c41efc0193b817ef", optional = true } futures-util = "0.3.30" vello = { workspace = true } wgpu = { workspace = true } diff --git a/packages/dioxus-blitz/src/documents/dioxus_document.rs b/packages/dioxus-blitz/src/documents/dioxus_document.rs index 671b6efd..feb2f0f3 100644 --- a/packages/dioxus-blitz/src/documents/dioxus_document.rs +++ b/packages/dioxus-blitz/src/documents/dioxus_document.rs @@ -31,7 +31,7 @@ fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName { } pub struct DioxusDocument { - vdom: VirtualDom, + pub(crate) vdom: VirtualDom, vdom_state: DioxusState, inner: Document, } @@ -230,6 +230,7 @@ impl MutationWriter<'_> { } // If element_id is already mapping to a node, remove that node from the document else if let Some(mapped_node_id) = self.state.node_id_mapping[element_id] { + // todo: we should mark these as needing garbage collection? self.doc.remove_node(mapped_node_id); } diff --git a/packages/dioxus-blitz/src/lib.rs b/packages/dioxus-blitz/src/lib.rs index bcc00433..2ea767c3 100644 --- a/packages/dioxus-blitz/src/lib.rs +++ b/packages/dioxus-blitz/src/lib.rs @@ -4,7 +4,7 @@ mod documents; mod waker; mod window; -use crate::waker::{EventData, UserWindowEvent}; +use crate::waker::{EventData, UserEvent}; use crate::{documents::HtmlDocument, window::View}; use blitz::RenderState; @@ -20,6 +20,10 @@ use winit::{ event_loop::ControlFlow, }; +pub mod exports { + pub use dioxus; +} + #[derive(Default)] pub struct Config { pub stylesheets: Vec, @@ -94,7 +98,7 @@ fn launch_with_window(window: View<'static, Doc>) { let _guard = rt.enter(); // Build an event loop for the application - let mut builder = EventLoop::::with_user_event(); + let mut builder = EventLoop::::with_user_event(); #[cfg(target_os = "android")] { @@ -117,6 +121,24 @@ fn launch_with_window(window: View<'static, Doc>) { #[cfg(not(any(target_os = "android", target_os = "ios")))] let mut initial = true; + // Setup hot-reloading if enabled. + #[cfg(all( + feature = "hot-reload", + debug_assertions, + not(target_os = "android"), + not(target_os = "ios") + ))] + { + if let Ok(cfg) = dioxus_cli_config::CURRENT_CONFIG.as_ref() { + dioxus_hot_reload::connect_at(cfg.target_dir.join("dioxusin"), { + let proxy = proxy.clone(); + move |template| { + let _ = proxy.send_event(UserEvent::HotReloadEvent(template)); + } + }) + } + } + // the move to winit wants us to use a struct with a run method instead of the callback approach // we want to just keep the callback approach for now #[allow(deprecated)] @@ -165,21 +187,58 @@ fn launch_with_window(window: View<'static, Doc>) { }; } - Event::UserEvent(UserWindowEvent(EventData::Poll, id)) => { + Event::UserEvent(UserEvent::Window { + data: EventData::Poll, + window_id: id, + }) => { if let Some(view) = windows.get_mut(&id) { if view.poll() { view.request_redraw(); } }; } + + #[cfg(all( + feature = "hot-reload", + debug_assertions, + not(target_os = "android"), + not(target_os = "ios") + ))] + Event::UserEvent(UserEvent::HotReloadEvent(msg)) => match msg { + dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => { + for window in windows.values_mut() { + if let Some(dx_doc) = window + .renderer + .dom + .as_any_mut() + .downcast_mut::() + { + dx_doc.vdom.replace_template(template); + + if window.poll() { + window.request_redraw(); + } + } + } + } + dioxus_hot_reload::HotReloadMsg::Shutdown => event_loop.exit(), + dioxus_hot_reload::HotReloadMsg::UpdateAsset(asset) => { + // TODO dioxus-desktop seems to handle this by forcing a reload of all stylesheets. + dbg!("Update asset {:?}", asset); + } + }, + // Event::UserEvent(_redraw) => { // for (_, view) in windows.iter() { // view.request_redraw(); // } // } Event::NewEvents(_) => { - for id in windows.keys() { - _ = proxy.send_event(UserWindowEvent(EventData::Poll, *id)); + for window_id in windows.keys().copied() { + _ = proxy.send_event(UserEvent::Window { + data: EventData::Poll, + window_id, + }); } } diff --git a/packages/dioxus-blitz/src/waker.rs b/packages/dioxus-blitz/src/waker.rs index ff78fb26..2c20983e 100644 --- a/packages/dioxus-blitz/src/waker.rs +++ b/packages/dioxus-blitz/src/waker.rs @@ -3,7 +3,20 @@ use std::sync::Arc; use winit::{event_loop::EventLoopProxy, window::WindowId}; #[derive(Debug, Clone)] -pub struct UserWindowEvent(pub EventData, pub WindowId); +pub enum UserEvent { + Window { + window_id: WindowId, + data: EventData, + }, + /// Handle a hotreload event, basically telling us to update our templates + #[cfg(all( + feature = "hot-reload", + debug_assertions, + not(target_os = "android"), + not(target_os = "ios") + ))] + HotReloadEvent(dioxus_hot_reload::HotReloadMsg), +} #[derive(Debug, Clone)] pub enum EventData { @@ -17,9 +30,9 @@ pub enum EventData { /// This lets the VirtualDom "come up for air" and process events while the main thread is blocked by the WebView. /// /// All other IO lives in the Tokio runtime, -pub fn tao_waker(proxy: &EventLoopProxy, id: WindowId) -> std::task::Waker { +pub fn tao_waker(proxy: &EventLoopProxy, id: WindowId) -> std::task::Waker { struct DomHandle { - proxy: EventLoopProxy, + proxy: EventLoopProxy, id: WindowId, } @@ -30,9 +43,10 @@ pub fn tao_waker(proxy: &EventLoopProxy, id: WindowId) -> std:: impl ArcWake for DomHandle { fn wake_by_ref(arc_self: &Arc) { - _ = arc_self - .proxy - .send_event(UserWindowEvent(EventData::Poll, arc_self.id)); + _ = arc_self.proxy.send_event(UserEvent::Window { + data: EventData::Poll, + window_id: arc_self.id, + }) } } diff --git a/packages/dioxus-blitz/src/window.rs b/packages/dioxus-blitz/src/window.rs index f6bcf884..a541ed8e 100644 --- a/packages/dioxus-blitz/src/window.rs +++ b/packages/dioxus-blitz/src/window.rs @@ -1,4 +1,4 @@ -use crate::waker::UserWindowEvent; +use crate::waker::UserEvent; use blitz::{RenderState, Renderer, Viewport}; use blitz_dom::DocumentLike; use winit::keyboard::PhysicalKey; @@ -277,7 +277,7 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { pub fn resume( &mut self, event_loop: &ActiveEventLoop, - proxy: &EventLoopProxy, + proxy: &EventLoopProxy, rt: &tokio::runtime::Runtime, ) { let window_builder = || { diff --git a/packages/dom/Cargo.toml b/packages/dom/Cargo.toml index f55512f7..6cdad1b1 100644 --- a/packages/dom/Cargo.toml +++ b/packages/dom/Cargo.toml @@ -3,8 +3,6 @@ name = "blitz-dom" version = "0.0.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] style = { workspace = true, features = ["servo"] } selectors = { workspace = true } diff --git a/packages/dom/src/document.rs b/packages/dom/src/document.rs index a21e8990..158072a2 100644 --- a/packages/dom/src/document.rs +++ b/packages/dom/src/document.rs @@ -4,6 +4,7 @@ use crate::{Node, NodeData, TextNodeData}; // use quadtree_rs::Quadtree; use selectors::{matching::QuirksMode, Element}; use slab::Slab; +use std::any::Any; use std::collections::HashMap; use style::invalidation::element::restyle_hints::RestyleHint; use style::selector_parser::ServoElementSnapshot; @@ -37,7 +38,7 @@ impl FontMetricsProvider for DummyFontMetricsProvider { } } -pub trait DocumentLike: AsRef + AsMut + Into { +pub trait DocumentLike: AsRef + AsMut + Into + 'static { fn poll(&mut self, _cx: std::task::Context) -> bool { // Default implementation does nothing false @@ -47,6 +48,10 @@ pub trait DocumentLike: AsRef + AsMut + Into { // Default implementation does nothing false } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } impl DocumentLike for Document {} @@ -218,6 +223,7 @@ impl Document { let node = &self.nodes[node_id]; let node_child_idx = node.child_idx; + let parent_id = node.parent.unwrap(); let parent = &mut self.nodes[parent_id];