diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index 71c5d2198..b41bacb7c 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -40,7 +40,7 @@ impl<'b> VirtualDom { for (root_idx, root) in template.template.roots.iter().enumerate() { // We might need to generate an ID for the root node on_stack += match root { - TemplateNode::DynamicText(id) | TemplateNode::Dynamic(id) => { + TemplateNode::DynamicText { id } | TemplateNode::Dynamic { id } => { match &template.dynamic_nodes[*id] { // a dynamic text node doesn't replace a template node, instead we create it on the fly DynamicNode::Text(VText { id: slot, value }) => { @@ -70,7 +70,7 @@ impl<'b> VirtualDom { } } - TemplateNode::Element { .. } | TemplateNode::Text(_) => { + TemplateNode::Element { .. } | TemplateNode::Text { .. } => { let this_id = self.next_root(template, root_idx); template.root_ids[root_idx].set(this_id); @@ -215,7 +215,7 @@ impl<'b> VirtualDom { .filter(|root| { matches!( root, - TemplateNode::Dynamic(_) | TemplateNode::DynamicText(_) + TemplateNode::Dynamic { .. } | TemplateNode::DynamicText { .. } ) }) .count(); diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index fca6a45e0..423cf677b 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -911,7 +911,7 @@ fn matching_components<'a>( .zip(right.template.roots.iter()) .map(|(l, r)| { let (l, r) = match (l, r) { - (TemplateNode::Dynamic(l), TemplateNode::Dynamic(r)) => (l, r), + (TemplateNode::Dynamic { id: l }, TemplateNode::Dynamic { id: r }) => (l, r), _ => return None, }; diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index 3687cd956..2512ae9fa 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -13,6 +13,7 @@ use crate::{arena::ElementId, ScopeId, Template}; /// Templates, however, apply to all subtrees, not just target subtree. /// /// Mutations are the only link between the RealDOM and the VirtualDOM. +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[derive(Debug, Default)] #[must_use = "not handling edits can lead to visual inconsistencies in UI"] pub struct Mutations<'a> { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index a7968abf5..7b6764417 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -78,8 +78,8 @@ impl<'a> VNode<'a> { /// Returns [`None`] if the root is actually a static node (Element/Text) pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> { match &self.template.roots[idx] { - TemplateNode::Element { .. } | TemplateNode::Text(_) => None, - TemplateNode::Dynamic(id) | TemplateNode::DynamicText(id) => { + TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None, + TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => { Some(&self.dynamic_nodes[*id]) } } @@ -132,7 +132,7 @@ pub struct Template<'a> { /// /// This can be created at compile time, saving the VirtualDom time when diffing the tree #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "serialize", derive(serde::Serialize), serde(tag = "type"))] pub enum TemplateNode<'a> { /// An statically known element in the dom. /// @@ -159,15 +159,15 @@ pub enum TemplateNode<'a> { }, /// This template node is just a piece of static text - Text(&'a str), + Text { text: &'a str }, /// This template node is unknown, and needs to be created at runtime. - Dynamic(usize), + Dynamic { 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(usize), + DynamicText { id: usize }, } /// A node created at runtime @@ -252,7 +252,11 @@ pub struct VText<'a> { /// An attribute of the TemplateNode, created at compile time #[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "serialize", + derive(serde::Serialize, serde::Deserialize), + serde(tag = "type") +)] pub enum TemplateAttribute<'a> { /// This attribute is entirely known at compile time, enabling Static { diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index d01b6545f..a71c43b47 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -54,8 +54,7 @@ impl DesktopController { { let edits = dom.rebuild(); let mut queue = edit_queue.lock().unwrap(); - queue.push(serde_json::to_string(&edits.templates).unwrap()); - queue.push(serde_json::to_string(&edits.edits).unwrap()); + queue.push(serde_json::to_string(&edits).unwrap()); proxy.send_event(UserWindowEvent::EditsReady).unwrap(); } @@ -116,8 +115,6 @@ impl DesktopController { let (_id, view) = self.webviews.iter_mut().next().unwrap(); - println!("processing pending edits {:?}", new_queue.len()); - for edit in new_queue.drain(..) { view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit)) .unwrap(); diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index c407013ea..1dba8978f 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -1,6 +1,5 @@ //! Convert a serialized event to an event trigger -use dioxus_core::ElementId; use dioxus_html::events::*; use serde::{Deserialize, Serialize}; use serde_json::from_value; diff --git a/packages/html/src/render_template.rs b/packages/html/src/render_template.rs index 5fbff5ff0..e608f6aa1 100644 --- a/packages/html/src/render_template.rs +++ b/packages/html/src/render_template.rs @@ -33,9 +33,9 @@ fn render_template_node(node: &TemplateNode, out: &mut String) -> std::fmt::Resu } write!(out, "{tag}>")?; } - TemplateNode::Text(t) => write!(out, "{t}")?, - TemplateNode::Dynamic(_) => write!(out, "
")?, - TemplateNode::DynamicText(t) => write!(out, " {t} ")?, + TemplateNode::Text { text: t } => write!(out, "{t}")?, + TemplateNode::Dynamic { id: _ } => write!(out, "")?, + TemplateNode::DynamicText { id: t } => write!(out, " {t} ")?, }; Ok(()) } diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 13414b6e2..1d28cbc6a 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -76,9 +76,6 @@ export class Interpreter { pop() { return this.stack.pop(); } - SaveTemplate(nodes, name) { - this.templates[name] = nodes; - } MountToRoot() { this.AppendChildren(this.stack.length - 1); } @@ -213,10 +210,57 @@ export class Interpreter { } } handleEdits(edits) { - for (let edit of edits) { + // a json blob of things + for (let template of edits.templates) { + this.SaveTemplate(template); + } + + for (let edit of edits.edits) { this.handleEdit(edit); } } + SaveTemplate(template) { + console.log("saving template", template); + let roots = []; + for (let root of template.roots) { + roots.push(this.MakeTemplateNode(root)); + } + console.log("saving template", template.name, roots); + this.templates[template.name] = roots; + } + MakeTemplateNode(node) { + console.log("making template node", node); + switch (node.type) { + case "Text": + return document.createTextNode(node.text); + case "Dynamic": + let dyn = document.createElement("pre"); + dyn.hidden = true; + return dyn; + case "DynamicText": + return document.createTextNode("placeholder"); + case "Element": + let el; + + if (node.namespace != null) { + el = document.createElementNS(node.namespace, node.tag); + } else { + el = document.createElement(node.tag); + } + + for (let attr of node.attrs) { + if (attr.type == "Static") { + this.SetAttributeInner(el, attr.name, attr.value, attr.namespace); + } + } + + for (let child of node.children) { + el.appendChild(this.MakeTemplateNode(child)); + } + + return el; + } + } AssignId(path, id) { this.nodes[id] = this.LoadChild(path); } diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 822cf71a6..ff8d3e782 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -245,7 +245,7 @@ impl<'a> DynamicContext<'a> { BodyNode::Text(text) if text.is_static() => { let text = text.source.as_ref().unwrap(); - quote! { ::dioxus::core::TemplateNode::Text(#text) } + quote! { ::dioxus::core::TemplateNode::Text{ text: #text } } } BodyNode::RawExpr(_) @@ -258,8 +258,10 @@ impl<'a> DynamicContext<'a> { self.node_paths.push(self.current_path.clone()); match root { - BodyNode::Text(_) => quote! { ::dioxus::core::TemplateNode::DynamicText(#ct) }, - _ => quote! { ::dioxus::core::TemplateNode::Dynamic(#ct) }, + BodyNode::Text(_) => { + quote! { ::dioxus::core::TemplateNode::DynamicText { id: #ct } } + } + _ => quote! { ::dioxus::core::TemplateNode::Dynamic { id: #ct } }, } } } diff --git a/packages/ssr/src/cache.rs b/packages/ssr/src/cache.rs index d7c2d9ca0..27b5a72ac 100644 --- a/packages/ssr/src/cache.rs +++ b/packages/ssr/src/cache.rs @@ -81,8 +81,8 @@ impl StringCache { } cur_path.pop(); } - TemplateNode::Text(text) => write!(chain, "{}", text)?, - TemplateNode::Dynamic(idx) | TemplateNode::DynamicText(idx) => { + TemplateNode::Text { text } => write!(chain, "{}", text)?, + TemplateNode::Dynamic { id: idx } | TemplateNode::DynamicText { id: idx } => { chain.segments.push(Segment::Node(*idx)) } } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index cf248df95..eac87adeb 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -101,9 +101,9 @@ impl WebsysDom { } el.dyn_into().unwrap() } - Text(t) => self.document.create_text_node(t).dyn_into().unwrap(), - DynamicText(_) => self.document.create_text_node("p").dyn_into().unwrap(), - Dynamic(_) => { + Text { text: t } => self.document.create_text_node(t).dyn_into().unwrap(), + DynamicText { id: _ } => self.document.create_text_node("p").dyn_into().unwrap(), + Dynamic { id: _ } => { let el = self.document.create_element("pre").unwrap(); el.toggle_attribute("hidden"); el.dyn_into().unwrap() @@ -116,6 +116,7 @@ impl WebsysDom { let i = &self.interpreter; for edit in edits.drain(..) { match edit { + AppendChildren { id, m } => i.AppendChildren(id, m), 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),