diff --git a/Cargo.lock b/Cargo.lock index 5fa77fedb4..c99a6e9832 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -471,9 +471,9 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "anyrender" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bfc8499b47c819e1f2fdbd04b1b85ca34a6e02eca996a58d750644cd847d0d" +checksum = "e6d136c78cf4c024f4433e2f775048c72c4912df168f9534652c5389217ee3bd" dependencies = [ "kurbo", "peniko", @@ -482,9 +482,9 @@ dependencies = [ [[package]] name = "anyrender_svg" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3197d2e8de16569a3335c15bb33c331b60e7aefa91466705cd2afa32f6bd5c8d" +checksum = "6811ea5c49d3ebad565d14d82564d21e24462cb81a2480818b9b57ce599eca1a" dependencies = [ "anyrender", "image", @@ -496,9 +496,9 @@ dependencies = [ [[package]] name = "anyrender_vello" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b39c05cc1c08be92d300e09a55cc9d32ffdd5b88a41e55677c75506f35b2180" +checksum = "3baba14afab3466d9c2cb61afc8d27810e2209807affd945295ec6d47c2166c3" dependencies = [ "anyrender", "futures-intrusive", @@ -2790,9 +2790,9 @@ dependencies = [ [[package]] name = "blitz-dom" -version = "0.1.0-alpha.5" +version = "0.1.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428233615df6116571f41c6cb6ebe7ecc41367177c6e224273149a7b7bdb09dc" +checksum = "5b715adfa6d85cad5da636aac5785d40e26dcf3f4195403fe8c46be501b77d72" dependencies = [ "accesskit 0.17.1", "app_units", @@ -2827,9 +2827,9 @@ dependencies = [ [[package]] name = "blitz-net" -version = "0.1.0-alpha.5" +version = "0.1.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75d0a4c29d831daccb752dd55a2e017394356da11e0ed246216b4bfda239c3cc" +checksum = "a4b0b681ed5c32d0c3dafc2288cf10b9aa46e08de1a58350e5842d4d761c33ae" dependencies = [ "blitz-traits", "data-url 0.3.1", @@ -2839,9 +2839,9 @@ dependencies = [ [[package]] name = "blitz-paint" -version = "0.1.0-alpha.5" +version = "0.1.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1597bca15e7eb885e34906d9241d2e3e39e0edcdba225afc85652f94122735df" +checksum = "b6b26d3c0645d2b502ec7b3984354b3c42a2af7e4bde92ed5d189ac160e4e1f5" dependencies = [ "anyrender", "anyrender_svg", @@ -2860,9 +2860,9 @@ dependencies = [ [[package]] name = "blitz-shell" -version = "0.1.0-alpha.5" +version = "0.1.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16507db6a4db31693dee0f421146a93555afb7998b0fdab78fba93d6f9649e56" +checksum = "cc0706c841eb5df72be68f0f0e56b9b0e6f4fab6e918c995a4c50cbd6ec127ff" dependencies = [ "accesskit 0.17.1", "accesskit_winit 0.23.1", @@ -2879,9 +2879,9 @@ dependencies = [ [[package]] name = "blitz-traits" -version = "0.1.0-alpha.5" +version = "0.1.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec1cf930b6013434896f63aab332a6dece6a068d40873f5a8cd861ac407bd63" +checksum = "f559ae4a8b1c7dafc09df5e1249f8a27c6b9a20658a15041128d202334e8e2c2" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5675,6 +5675,7 @@ version = "0.7.0-alpha.3" dependencies = [ "blitz-dom", "blitz-traits", + "dioxus", "dioxus-core", "dioxus-html", "futures-util", @@ -15370,9 +15371,9 @@ checksum = "500f379645e8a87fd03fe88607a5edcb0d8e4e423baa74ba52db198a06a0c261" [[package]] name = "stylo_taffy" -version = "0.1.0-alpha.5" +version = "0.1.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea77ce9f0efa0e18b375749879bf65b8b432e95e37ca7222a7818b0445ff78c" +checksum = "94014fa36fdc4698a0b36179385d70810b6f0825b90c1df52de2aa5ee670328a" dependencies = [ "stylo", "taffy 0.8.3", diff --git a/Cargo.toml b/Cargo.toml index 3a8477af5e..750403f7cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,11 +201,13 @@ depinfo = { path = "packages/depinfo", version = "0.7.0-alpha.3" } warnings = { version = "0.2.1" } # blitz -blitz-dom = { version = "=0.1.0-alpha.5", default-features = false } -blitz-net = { version = "=0.1.0-alpha.5" } -blitz-paint = { version = "=0.1.0-alpha.5" } -blitz-traits = { version = "=0.1.0-alpha.5" } -blitz-shell = { version = "=0.1.0-alpha.5", default-features = false } +blitz-dom = { version = "=0.1.0-rc.1", default-features = false } +blitz-net = { version = "=0.1.0-rc.1" } +blitz-paint = { version = "=0.1.0-rc.1" } +blitz-traits = { version = "=0.1.0-rc.1" } +blitz-shell = { version = "=0.1.0-rc.1", default-features = false } +anyrender = { version = "0.5", default-features = false } +anyrender_vello = { version = "0.5", default-features = false } wgpu = { version = "24.0" } # a fork of pretty please for tests - let's get off of this if we can! diff --git a/examples/native-headless-in-bevy/Cargo.toml b/examples/native-headless-in-bevy/Cargo.toml index 117ca42fda..8225a28685 100644 --- a/examples/native-headless-in-bevy/Cargo.toml +++ b/examples/native-headless-in-bevy/Cargo.toml @@ -12,18 +12,18 @@ publish = false tracing = ["dep:tracing-subscriber", "dioxus-native-dom/tracing"] [dependencies] -anyrender_vello = "0.4.1" -async-std = "1.13" -bevy = { version = "0.16" } +dioxus = { workspace = true } +dioxus-native-dom = { workspace = true } blitz-dom = { workspace = true, default-features = true } blitz-paint = { workspace = true, default-features = true } blitz-traits = { workspace = true, default-features = true } +anyrender_vello = { workspace = true } +wgpu = { workspace = true } +tracing-subscriber = { workspace = true, optional = true } +bevy = { version = "0.16" } +async-std = "1.13" crossbeam-channel = "0.5" -dioxus = { workspace = true } -dioxus-native-dom = { path = "../../packages/native-dom" } paste = "1.0" rustc-hash = "1" -tracing-subscriber = { workspace = true, optional = true } vello = "0.5" -wgpu = { workspace = true } diff --git a/examples/native-headless-in-bevy/src/dioxus_in_bevy_plugin.rs b/examples/native-headless-in-bevy/src/dioxus_in_bevy_plugin.rs index 1b56ea8c78..25b439abee 100644 --- a/examples/native-headless-in-bevy/src/dioxus_in_bevy_plugin.rs +++ b/examples/native-headless-in-bevy/src/dioxus_in_bevy_plugin.rs @@ -17,7 +17,7 @@ use bevy::{ }; use anyrender_vello::{CustomPaintSource, VelloScenePainter}; -use blitz_dom::Document as _; +use blitz_dom::{Document as _, DocumentConfig}; use blitz_paint::paint_scene; use blitz_traits::events::{ BlitzKeyEvent, BlitzMouseButtonEvent, KeyState, MouseEventButton, MouseEventButtons, UiEvent, @@ -49,7 +49,7 @@ impl { mouse_state.buttons |= buttons_blitz; - dioxus_doc.handle_event(UiEvent::MouseDown(BlitzMouseButtonEvent { + dioxus_doc.handle_ui_event(UiEvent::MouseDown(BlitzMouseButtonEvent { x: mouse_state.x, y: mouse_state.y, button: button_blitz, @@ -418,7 +418,7 @@ fn handle_mouse_events( } ButtonState::Released => { mouse_state.buttons &= !buttons_blitz; - dioxus_doc.handle_event(UiEvent::MouseUp(BlitzMouseButtonEvent { + dioxus_doc.handle_ui_event(UiEvent::MouseUp(BlitzMouseButtonEvent { x: mouse_state.x, y: mouse_state.y, button: button_blitz, @@ -495,10 +495,10 @@ fn handle_keyboard_events( match key_state { KeyState::Pressed => { - dioxus_doc.handle_event(UiEvent::KeyDown(blitz_key_event)); + dioxus_doc.handle_ui_event(UiEvent::KeyDown(blitz_key_event)); } KeyState::Released => { - dioxus_doc.handle_event(UiEvent::KeyUp(blitz_key_event)); + dioxus_doc.handle_ui_event(UiEvent::KeyUp(blitz_key_event)); } } } diff --git a/examples/native-headless/Cargo.toml b/examples/native-headless/Cargo.toml index eff60ecbfb..641b7bcdb2 100644 --- a/examples/native-headless/Cargo.toml +++ b/examples/native-headless/Cargo.toml @@ -10,8 +10,8 @@ tracing = ["dep:tracing-subscriber", "dioxus-native-dom/tracing"] [dependencies] dioxus = { workspace = true, default-features = false, features = ["html", "hooks", "signals"] } -dioxus-native-dom = { path = "../../packages/native-dom" } -anyrender_vello = "0.4.1" +dioxus-native-dom = { workspace = true } +anyrender_vello = { workspace = true } vello = "0.5" rustc-hash = "1" futures-util = { workspace = true } diff --git a/examples/native-headless/src/main.rs b/examples/native-headless/src/main.rs index a1fd0c46be..ee8c19c877 100644 --- a/examples/native-headless/src/main.rs +++ b/examples/native-headless/src/main.rs @@ -4,7 +4,7 @@ //! (this example is not really intended to be run as-is, and requires you to fill //! in the missing pieces) use anyrender_vello::{wgpu_context::WGPUContext, CustomPaintSource, VelloScenePainter}; -use blitz_dom::Document as _; +use blitz_dom::{Document as _, DocumentConfig}; use blitz_paint::paint_scene; use blitz_traits::{ events::{BlitzMouseButtonEvent, MouseEventButton, MouseEventButtons, UiEvent}, @@ -49,8 +49,13 @@ fn main() { // Create the dioxus virtual dom and the dioxus-native document // It is important to set the width, height, and scale factor on the document as these are used for layout. let vdom = VirtualDom::new(app); - let mut dioxus_doc = DioxusDocument::new(vdom, None); - dioxus_doc.set_viewport(Viewport::new(WIDTH, HEIGHT, SCALE_FACTOR, COLOR_SCHEME)); + let mut dioxus_doc = DioxusDocument::new( + vdom, + DocumentConfig { + viewport: Some(Viewport::new(WIDTH, HEIGHT, SCALE_FACTOR, COLOR_SCHEME)), + ..Default::default() + }, + ); // Setup a WGPU Device and Queue // @@ -95,7 +100,7 @@ fn main() { // ============= // Poll the vdom - dioxus_doc.poll(Context::from_waker(&waker)); + dioxus_doc.poll(Some(Context::from_waker(&waker))); // Create a `VelloScenePainter` to paint into let mut custom_paint_sources = @@ -151,7 +156,7 @@ fn main() { buttons: MouseEventButtons::Primary, // keep track of all pressed buttons mods: Modifiers::empty(), // ctrl, alt, shift, etc }); - dioxus_doc.handle_event(event); + dioxus_doc.handle_ui_event(event); // Trigger a poll via your event loop (or wait for next frame) } diff --git a/packages/native-dom/Cargo.toml b/packages/native-dom/Cargo.toml index 7f5c206a22..6c8839a590 100644 --- a/packages/native-dom/Cargo.toml +++ b/packages/native-dom/Cargo.toml @@ -19,8 +19,8 @@ autofocus = [] [dependencies] # Blitz dependencies -blitz-dom = { version = "=0.1.0-alpha.5", default-features = false } -blitz-traits = { version = "=0.1.0-alpha.5" } +blitz-dom = { workspace = true, default-features = false } +blitz-traits = { workspace = true } # DioxusLabs dependencies dioxus-core = { workspace = true } @@ -35,6 +35,9 @@ tracing = { workspace = true, optional = true } rustc-hash = { workspace = true } futures-util = { workspace = true } +[dev-dependencies] +dioxus = { workspace = true } + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/packages/native-dom/src/dioxus_document.rs b/packages/native-dom/src/dioxus_document.rs index f26abe6d52..c2bd539903 100644 --- a/packages/native-dom/src/dioxus_document.rs +++ b/packages/native-dom/src/dioxus_document.rs @@ -3,22 +3,18 @@ use crate::events::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeF use crate::mutation_writer::{DioxusState, MutationWriter}; use crate::qual_name; use crate::NodeId; - -use blitz_dom::Attribute; use blitz_dom::{ - net::Resource, BaseDocument, Document, EventDriver, EventHandler, Node, DEFAULT_CSS, -}; -use blitz_traits::{ - events::{DomEvent, DomEventData, EventState, UiEvent}, - net::NetProvider, - shell::{ColorScheme, Viewport}, + Attribute, BaseDocument, Document, DocumentConfig, EventDriver, EventHandler, Node, DEFAULT_CSS, }; - +use blitz_traits::events::{DomEvent, DomEventData, EventState, UiEvent}; use dioxus_core::{ElementId, Event, VirtualDom}; use dioxus_html::{set_event_converter, PlatformEventData}; +use futures_util::task::noop_waker; use futures_util::{pin_mut, FutureExt}; use std::ops::{Deref, DerefMut}; -use std::{any::Any, collections::HashMap, rc::Rc, sync::Arc}; +use std::sync::LazyLock; +use std::task::{Context as TaskContext, Waker}; +use std::{any::Any, collections::HashMap, rc::Rc}; fn wrap_event_data(value: T) -> Rc { Rc::new(PlatformEventData::new(Box::new(value))) @@ -34,6 +30,34 @@ fn get_dioxus_id(node: &Node) -> Option { .map(ElementId) } +/// Integrates [`BaseDocument`] from [`blitz-dom`](blitz_dom) with [`VirtualDom`] from [`dioxus-core`](dioxus_core) +/// +/// ### Example +/// +/// ```rust +/// use blitz_traits::shell::{Viewport, ColorScheme}; +/// use dioxus_native_dom::{DioxusDocument, DocumentConfig}; +/// use dioxus::prelude::*; +/// +/// // Example Dioxus app +/// fn app() -> Element { +/// rsx! { +/// div { "Hello, world!" } +/// } +/// } +/// +/// fn main() { +/// let vdom = VirtualDom::new(app); +/// let mut doc = DioxusDocument::new(vdom, DocumentConfig { +/// viewport: Some(Viewport::new(800, 600, 1.0, ColorScheme::Light)), +/// ..Default::default() +/// }); +/// doc.initial_build(); +/// } +/// ``` +/// +/// You can just push events into the [`DioxusDocument`] with [`doc.handle_ui_event(..)`](Self::handle_ui_event) +/// and then flush the changes with [`doc.poll(..)`](Self::poll) pub struct DioxusDocument { pub inner: BaseDocument, pub vdom: VirtualDom, @@ -50,16 +74,32 @@ pub struct DioxusDocument { } impl DioxusDocument { - pub fn new(vdom: VirtualDom, net_provider: Option>>) -> Self { - let viewport = Viewport::new(0, 0, 1.0, ColorScheme::Light); - let mut doc = BaseDocument::new(viewport); + /// Create a new [`DioxusDocument`] from a [`VirtualDom`]. + pub fn new(vdom: VirtualDom, mut config: DocumentConfig) -> Self { + // Only really needs to happen once + set_event_converter(Box::new(NativeConverter {})); - // Set net provider - if let Some(net_provider) = net_provider { - doc.set_net_provider(net_provider); - } + config.base_url = Some( + config + .base_url + .unwrap_or_else(|| String::from("dioxus://index.html")), + ); + let mut doc = BaseDocument::new(config); + + // Include default stylesheet + doc.add_user_agent_stylesheet(DEFAULT_CSS); // Create some minimal HTML to render the app into. + // HTML is equivalent to: + // + // + // + // + //
+ // + // + // + // TODO: Support arbitrary "index.html" templates // Create the html element let mut mutr = doc.mutate(); @@ -84,11 +124,8 @@ impl DioxusDocument { drop(mutr); - // Include default and user-specified stylesheets - doc.add_user_agent_stylesheet(DEFAULT_CSS); - let vdom_state = DioxusState::create(main_element_id); - let mut doc = Self { + Self { vdom, vdom_state, inner: doc, @@ -96,20 +133,18 @@ impl DioxusDocument { head_element_id, body_element_id, main_element_id, - }; - - doc.inner.set_base_url("dioxus://index.html"); - //doc.initial_build(); - doc.inner.print_tree(); - - doc + } } + /// Run an initial build of the Dioxus vdom pub fn initial_build(&mut self) { let mut writer = MutationWriter::new(&mut self.inner, &mut self.vdom_state); self.vdom.rebuild(&mut writer); } + /// Used to respond to a `CreateHeadElement` event generated by Dioxus. These + /// events allow Dioxus to create elements in the `` of the document. + #[doc(hidden)] pub fn create_head_element( &mut self, name: &str, @@ -150,11 +185,13 @@ impl Document for DioxusDocument { self } - fn poll(&mut self, mut cx: std::task::Context) -> bool { + fn poll(&mut self, cx: Option) -> bool { { let fut = self.vdom.wait_for_work(); pin_mut!(fut); + static NOOP_WAKER: LazyLock = LazyLock::new(noop_waker); + let mut cx = cx.unwrap_or_else(|| TaskContext::from_waker(&NOOP_WAKER)); match fut.poll_unpin(&mut cx) { std::task::Poll::Ready(_) => {} std::task::Poll::Pending => return false, @@ -167,8 +204,7 @@ impl Document for DioxusDocument { true } - fn handle_event(&mut self, event: UiEvent) { - set_event_converter(Box::new(NativeConverter {})); + fn handle_ui_event(&mut self, event: UiEvent) { let handler = DioxusEventHandler { vdom: &mut self.vdom, vdom_state: &mut self.vdom_state, @@ -209,6 +245,14 @@ impl EventHandler for DioxusEventHandler<'_> { mutr: &mut blitz_dom::DocumentMutator<'_>, event_state: &mut EventState, ) { + // As an optimisation we maintain a count of the total number event handlers of a given type + // If this count is zero then we can skip handling that kind of event entirely. + let event_kind_idx = event.data.discriminant() as usize; + let event_kind_count = self.vdom_state.event_handler_counts[event_kind_idx]; + if event_kind_count == 0 { + return; + } + let event_data = match &event.data { DomEventData::MouseMove(mevent) | DomEventData::MouseDown(mevent) diff --git a/packages/native-dom/src/lib.rs b/packages/native-dom/src/lib.rs index c0feb00ef0..3e770a9941 100644 --- a/packages/native-dom/src/lib.rs +++ b/packages/native-dom/src/lib.rs @@ -12,6 +12,7 @@ mod dioxus_document; mod events; mod mutation_writer; +pub use blitz_dom::DocumentConfig; pub use dioxus_document::DioxusDocument; use blitz_dom::{ns, LocalName, Namespace, QualName}; diff --git a/packages/native-dom/src/mutation_writer.rs b/packages/native-dom/src/mutation_writer.rs index 88f940d09e..b61033770d 100644 --- a/packages/native-dom/src/mutation_writer.rs +++ b/packages/native-dom/src/mutation_writer.rs @@ -1,20 +1,24 @@ //! Integration between Dioxus and Blitz use crate::{qual_name, trace, NodeId}; use blitz_dom::{Attribute, BaseDocument, DocumentMutator}; +use blitz_traits::events::DomEventKind; use dioxus_core::{ AttributeValue, ElementId, Template, TemplateAttribute, TemplateNode, WriteMutations, }; use rustc_hash::FxHashMap; +use std::str::FromStr as _; /// The state of the Dioxus integration with the RealDom #[derive(Debug)] pub struct DioxusState { /// Store of templates keyed by unique name - templates: FxHashMap>, + pub(crate) templates: FxHashMap>, /// Stack machine state for applying dioxus mutations - stack: Vec, + pub(crate) stack: Vec, /// Mapping from vdom ElementId -> rdom NodeId - node_id_mapping: Vec>, + pub(crate) node_id_mapping: Vec>, + /// Count of each handler type + pub(crate) event_handler_counts: [u32; 32], } impl DioxusState { @@ -24,6 +28,7 @@ impl DioxusState { templates: FxHashMap::default(), stack: vec![root_id], node_id_mapping: vec![Some(root_id)], + event_handler_counts: [0; 32], } } @@ -254,10 +259,18 @@ impl WriteMutations for MutationWriter<'_> { self.set_attribute("data-dioxus-id", None, &value, id); // node.add_event_listener(name); + + if let Ok(kind) = DomEventKind::from_str(name) { + let idx = kind.discriminant() as usize; + self.state.event_handler_counts[idx] += 1; + } } - fn remove_event_listener(&mut self, _name: &'static str, _id: ElementId) { - // node.remove_event_listener(name); + fn remove_event_listener(&mut self, name: &'static str, _id: ElementId) { + if let Ok(kind) = DomEventKind::from_str(name) { + let idx = kind.discriminant() as usize; + self.state.event_handler_counts[idx] -= 1; + } } } diff --git a/packages/native/Cargo.toml b/packages/native/Cargo.toml index 615ccabf05..a02b0936e0 100644 --- a/packages/native/Cargo.toml +++ b/packages/native/Cargo.toml @@ -21,13 +21,13 @@ autofocus = [] [dependencies] # Blitz dependencies -anyrender = { version = "0.4", default-features = false } -anyrender_vello = { version = "0.4", default-features = false } -blitz-dom = { workspace = true, default-features = false } +anyrender = { workspace = true } +anyrender_vello = { workspace = true } +blitz-dom = { workspace = true } blitz-net = { workspace = true, optional = true } blitz-paint = { workspace = true, optional = true } blitz-traits = { workspace = true } -blitz-shell = { workspace = true, default-features = false } +blitz-shell = { workspace = true } # DioxusLabs dependencies dioxus-core = { workspace = true } diff --git a/packages/native/src/lib.rs b/packages/native/src/lib.rs index 1c1a8cbf2b..2bad1c483d 100644 --- a/packages/native/src/lib.rs +++ b/packages/native/src/lib.rs @@ -124,7 +124,13 @@ pub fn launch_cfg_with_props( let net_provider = None; // Create document + window from the baked virtualdom - let doc = DioxusDocument::new(vdom, net_provider); + let doc = DioxusDocument::new( + vdom, + DocumentConfig { + net_provider, + ..Default::default() + }, + ); let renderer = DioxusNativeWindowRenderer::with_features_and_limits(features, limits); let config = WindowConfig::with_attributes( Box::new(doc) as _,