diff --git a/.vscode/settings.json b/.vscode/settings.json index 2f418701b..80f51cff8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.inlayHints.enable": true + "rust-analyzer.inlayHints.enable": false } \ No newline at end of file diff --git a/packages/core/src/changelist.rs b/packages/core/src/changelist.rs index 254293e67..831be1ab9 100644 --- a/packages/core/src/changelist.rs +++ b/packages/core/src/changelist.rs @@ -19,7 +19,7 @@ //! //! -use crate::innerlude::Listener; +use crate::innerlude::{Listener, VirtualDom}; /// Renderers need to implement the interpreter trait /// @@ -60,6 +60,7 @@ pub struct ChangeList<'src> { next_temporary: u32, forcing_new_listeners: bool, emitter: &'src mut dyn Inrerpreter, + domlock: &'src VirtualDom, } /// Traversal methods. @@ -158,19 +159,19 @@ impl ChangeList<'_> { temp_base } - pub fn push_temporary(&self, temp: u32) { + pub fn push_temporary(&mut self, temp: u32) { debug_assert!(self.traversal_is_committed()); // debug!("emit: push_temporary({})", temp); self.emitter.push_temporary(temp); } - pub fn remove_child(&self, child: usize) { + pub fn remove_child(&mut self, child: usize) { debug_assert!(self.traversal_is_committed()); // debug!("emit: remove_child({})", child); self.emitter.remove_child(child as u32); } - pub fn insert_before(&self) { + pub fn insert_before(&mut self) { debug_assert!(self.traversal_is_committed()); // debug!("emit: insert_before()"); self.emitter.insert_before(); @@ -181,20 +182,20 @@ impl ChangeList<'_> { // self.strings.ensure_string(string, &self.emitter) } - pub fn set_text(&self, text: &str) { + pub fn set_text(&mut self, text: &str) { debug_assert!(self.traversal_is_committed()); // debug!("emit: set_text({:?})", text); self.emitter.set_text(text); // .set_text(text.as_ptr() as u32, text.len() as u32); } - pub fn remove_self_and_next_siblings(&self) { + pub fn remove_self_and_next_siblings(&mut self) { debug_assert!(self.traversal_is_committed()); // debug!("emit: remove_self_and_next_siblings()"); self.emitter.remove_self_and_next_siblings(); } - pub fn replace_with(&self) { + pub fn replace_with(&mut self) { debug_assert!(self.traversal_is_committed()); // debug!("emit: replace_with()"); self.emitter.replace_with(); @@ -218,38 +219,42 @@ impl ChangeList<'_> { } pub fn remove_attribute(&mut self, name: &str) { - debug_assert!(self.traversal_is_committed()); - // debug!("emit: remove_attribute({:?})", name); - let name_id = self.ensure_string(name); - self.emitter.remove_attribute(name_id.into()); + // todo!("figure out how to get this working with ensure string"); + self.emitter.remove_attribute(name); + // debug_assert!(self.traversal_is_committed()); + // // debug!("emit: remove_attribute({:?})", name); + // let name_id = self.ensure_string(name); + // self.emitter.remove_attribute(name_id.into()); } - pub fn append_child(&self) { + pub fn append_child(&mut self) { debug_assert!(self.traversal_is_committed()); // debug!("emit: append_child()"); self.emitter.append_child(); } - pub fn create_text_node(&self, text: &str) { + pub fn create_text_node(&mut self, text: &str) { debug_assert!(self.traversal_is_committed()); // debug!("emit: create_text_node({:?})", text); self.emitter.create_text_node(text); } pub fn create_element(&mut self, tag_name: &str) { - debug_assert!(self.traversal_is_committed()); + // debug_assert!(self.traversal_is_committed()); // debug!("emit: create_element({:?})", tag_name); - let tag_name_id = self.ensure_string(tag_name); - self.emitter.create_element(tag_name_id.into()); + // let tag_name_id = self.ensure_string(tag_name); + self.emitter.create_element(tag_name); + // self.emitter.create_element(tag_name_id.into()); } pub fn create_element_ns(&mut self, tag_name: &str, ns: &str) { debug_assert!(self.traversal_is_committed()); // debug!("emit: create_element_ns({:?}, {:?})", tag_name, ns); - let tag_name_id = self.ensure_string(tag_name); - let ns_id = self.ensure_string(ns); - self.emitter - .create_element_ns(tag_name_id.into(), ns_id.into()); + // let tag_name_id = self.ensure_string(tag_name); + // let ns_id = self.ensure_string(ns); + self.emitter.create_element_ns(tag_name, ns); + // self.emitter + // .create_element_ns(tag_name_id.into(), ns_id.into()); } pub fn push_force_new_listeners(&mut self) -> bool { @@ -265,11 +270,12 @@ impl ChangeList<'_> { pub fn new_event_listener(&mut self, listener: &Listener) { debug_assert!(self.traversal_is_committed()); + todo!("Event listener not wired up yet"); // debug!("emit: new_event_listener({:?})", listener); - let (a, b) = listener.get_callback_parts(); - debug_assert!(a != 0); - let event_id = self.ensure_string(listener.event); - self.emitter.new_event_listener(event_id.into(), a, b); + // let (a, b) = listener.get_callback_parts(); + // debug_assert!(a != 0); + // let event_id = self.ensure_string(listener.event); + // self.emitter.new_event_listener(event_id.into(), a, b); } pub fn update_event_listener(&mut self, listener: &Listener) { @@ -281,17 +287,19 @@ impl ChangeList<'_> { } // debug!("emit: update_event_listener({:?})", listener); - let (a, b) = listener.get_callback_parts(); - debug_assert!(a != 0); + todo!("Event listener not wired up yet"); + // let (a, b) = listener.get_callback_parts(); + // debug_assert!(a != 0); let event_id = self.ensure_string(listener.event); - self.emitter.update_event_listener(event_id.into(), a, b); + // self.emitter.update_event_listener(event_id.into(), a, b); } pub fn remove_event_listener(&mut self, event: &str) { debug_assert!(self.traversal_is_committed()); // debug!("emit: remove_event_listener({:?})", event); let event_id = self.ensure_string(event); - self.emitter.remove_event_listener(event_id.into()); + todo!("Event listener not wired up yet"); + // self.emitter.remove_event_listener(event_id.into()); } // #[inline] diff --git a/packages/core/src/debug_renderer.rs b/packages/core/src/debug_renderer.rs index b9961ea5a..3b63294c3 100644 --- a/packages/core/src/debug_renderer.rs +++ b/packages/core/src/debug_renderer.rs @@ -5,12 +5,12 @@ use crate::prelude::{Properties, VirtualDom}; -pub struct DebugRenderer<'a, P: Properties> { - vdom: &'a mut VirtualDom

, +pub struct DebugRenderer { + vdom: VirtualDom, } -impl<'a, P: Properties> DebugRenderer<'a, P> { - pub fn new(vdom: &'a mut VirtualDom

) -> Self { +impl DebugRenderer { + pub fn new(vdom: VirtualDom) -> Self { Self { vdom } } diff --git a/packages/core/src/error.rs b/packages/core/src/error.rs index 71e6d4adf..83f528c02 100644 --- a/packages/core/src/error.rs +++ b/packages/core/src/error.rs @@ -7,6 +7,9 @@ pub enum Error { #[error("No event to progress")] NoEvent, + #[error("Wrong Properties Type")] + WrongProps, + // #[error("Out of compute credits")] // OutOfCredits, diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 325341bff..e5120a9a7 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -1,4 +1,4 @@ -use crate::nodes::VNode; +use crate::{changelist::ChangeList, nodes::VNode}; use crate::{events::EventTrigger, innerlude::*}; use any::Any; use bumpalo::Bump; @@ -14,7 +14,8 @@ use std::{ /// An integrated virtual node system that progresses events and diffs UI trees. /// Differences are converted into patches which a renderer can use to draw the UI. -pub struct VirtualDom { +pub struct VirtualDom { + // pub struct VirtualDom { /// All mounted components are arena allocated to make additions, removals, and references easy to work with /// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena. pub(crate) components: Arena, @@ -28,11 +29,15 @@ pub struct VirtualDom { /// event_queue: Rc>>, - #[doc(hidden)] // mark the root props with P, even though they're held by the root component - _root_props: PhantomData

, + // Mark the root props with P, even though they're held by the root component + // This is done so we don't have a "generic" vdom, making it easier to hold references to it, especially when the holders + // don't care about the generic props type + // Most implementations that use the VirtualDom won't care about the root props anyways. + #[doc(hidden)] + _root_prop_type: std::any::TypeId, } -impl VirtualDom<()> { +impl VirtualDom { /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component. /// /// This means that the root component must either consumes its own context, or statics are used to generate the page. @@ -42,13 +47,14 @@ impl VirtualDom<()> { } } -impl VirtualDom

{ +impl VirtualDom { + // impl VirtualDom

{ /// Start a new VirtualDom instance with a dependent props. /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders. /// /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive /// to toss out the entire tree. - pub fn new_with_props(root: FC

, root_props: P) -> Self { + pub fn new_with_props(root: FC

, root_props: P) -> Self { // 1. Create the component arena // 2. Create the base scope (can never be removed) // 3. Create the lifecycle queue @@ -67,28 +73,41 @@ impl VirtualDom

{ // Create an event queue with a mount for the base scope let event_queue = Rc::new(RefCell::new(vec![first_event])); + let _root_prop_type = TypeId::of::

(); + Self { components, base_scope, event_queue, - _root_props: PhantomData {}, + _root_prop_type, } } /// With access to the virtual dom, schedule an update to the Root component's props - pub fn update_props(&mut self, new_props: P) { + pub fn update_props(&mut self, new_props: P) -> Result<()> { + // Ensure the props match + if TypeId::of::

() != self._root_prop_type { + return Err(Error::WrongProps); + } + self.event_queue.borrow_mut().push(LifecycleEvent { event_type: LifecycleType::PropsChanged { props: Box::new(new_props), }, index: self.base_scope, }); + + Ok(()) } + /// Schedule a future update for a component from outside the vdom! + /// + /// This lets services external to the virtual dom interact directly with the component and event system. + pub fn queue_update() {} + /// Pop an event off the event queue and process it /// Update the root props, and progress /// Takes a bump arena to allocate into, making the diff phase as fast as possible - /// pub fn progress(&mut self) -> Result<()> { let event = self.event_queue.borrow_mut().pop().ok_or(Error::NoEvent)?; process_event(&mut self.components, event) @@ -109,7 +128,7 @@ impl VirtualDom

{ /// /// /// ``` - pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<()> { + pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result> { let EventTrigger { component_id, listener_id, @@ -144,7 +163,8 @@ impl VirtualDom

{ process_event(&mut self.components, event)?; } - Ok(()) + todo!() + // Ok(()) } pub async fn progress_completely(&mut self) -> Result<()> { diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 34026213f..0b6fdfd0e 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -15,6 +15,7 @@ wasm-bindgen-futures = "0.4.20" futures = "0.3.12" wasm-logger = "0.2.0" log = "0.4.14" +fxhash = "0.2.1" # html-validation = { path = "../html-validation", version = "0.1.1" } [dependencies.web-sys] @@ -33,4 +34,5 @@ features = [ "Event", "MouseEvent", "InputEvent", + "DocumentType", ] diff --git a/packages/web/src/interpreter.rs b/packages/web/src/interpreter.rs index 7929ca46d..ba9473240 100644 --- a/packages/web/src/interpreter.rs +++ b/packages/web/src/interpreter.rs @@ -1,9 +1,12 @@ -use crate::cached_set::CacheId; -use crate::{Element, EventsTrampoline}; +// use crate::cached_set::CacheId; +// use crate::{Element, EventsTrampoline}; use fxhash::FxHashMap; use log::{debug, info, log}; use wasm_bindgen::{closure::Closure, JsCast}; -use web_sys::{window, Document, Event, Node}; +use web_sys::{window, Document, Element, Event, Node}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub(crate) struct CacheId(u32); #[derive(Debug)] pub(crate) struct ChangeListInterpreter { @@ -86,27 +89,29 @@ impl ChangeListInterpreter { self.templates.get(&id) } - pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) { - self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| { - let target = event - .target() - .expect("missing target") - .dyn_into::() - .expect("not a valid element"); - let typ = event.type_(); - let a: u32 = target - .get_attribute(&format!("dodrio-a-{}", typ)) - .and_then(|v| v.parse().ok()) - .unwrap_or_default(); + pub fn init_events_trampoline(&mut self, mut trampoline: ()) { + todo!("Event trampoline not a thing anymore") + // pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) { + // self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| { + // let target = event + // .target() + // .expect("missing target") + // .dyn_into::() + // .expect("not a valid element"); + // let typ = event.type_(); + // let a: u32 = target + // .get_attribute(&format!("dodrio-a-{}", typ)) + // .and_then(|v| v.parse().ok()) + // .unwrap_or_default(); - let b: u32 = target - .get_attribute(&format!("dodrio-b-{}", typ)) - .and_then(|v| v.parse().ok()) - .unwrap_or_default(); + // let b: u32 = target + // .get_attribute(&format!("dodrio-b-{}", typ)) + // .and_then(|v| v.parse().ok()) + // .unwrap_or_default(); - // get a and b from the target - trampoline(event.clone(), a, b); - }) as Box)); + // // get a and b from the target + // trampoline(event.clone(), a, b); + // }) as Box)); } // 0 diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 2cedd1372..748bbf857 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -29,12 +29,12 @@ pub mod interpreter; /// Under the hood, we leverage WebSys and interact directly with the DOM /// -pub struct WebsysRenderer { - internal_dom: VirtualDom, +pub struct WebsysRenderer { + internal_dom: VirtualDom, } /// Implement VirtualDom with no props for components that initialize their state internal to the VDom rather than externally. -impl WebsysRenderer<()> { +impl WebsysRenderer { /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component. /// /// This means that the root component must either consumes its own context, or statics are used to generate the page. @@ -44,18 +44,18 @@ impl WebsysRenderer<()> { } } -impl WebsysRenderer { +impl WebsysRenderer { /// Create a new text-renderer instance from a functional component root. /// Automatically progresses the creation of the VNode tree to completion. /// /// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom` - pub fn new_with_props(root: FC, root_props: T) -> Self { + pub fn new_with_props(root: FC, root_props: T) -> Self { Self::from_vdom(VirtualDom::new_with_props(root, root_props)) } /// Create a new text renderer from an existing Virtual DOM. /// This will progress the existing VDom's events to completion. - pub fn from_vdom(dom: VirtualDom) -> Self { + pub fn from_vdom(dom: VirtualDom) -> Self { Self { internal_dom: dom } } @@ -76,7 +76,7 @@ impl WebsysRenderer { // Iterate through the nodes, attaching the closure and sender to the listener { let mut remote_sender = sender.clone(); - let f = move || { + let listener = move || { let event = EventTrigger::new(); wasm_bindgen_futures::spawn_local(async move { remote_sender @@ -108,6 +108,6 @@ impl WebsysRenderer { /// For any listeners in the tree, attach the sender closure. /// When a event is triggered, we convert it into the synthetic event type and dump it back in the Virtual Dom's queu -fn attach_listeners(sender: &UnboundedSender, dom: &VirtualDom

) {} +fn attach_listeners(sender: &UnboundedSender, dom: &VirtualDom) {} fn render_diffs() {}