feat: get desktop working with new template strategy
This commit is contained in:
1 parent
df4a1b6c7a
commit
7b1cdb7d85
11 files changed
+79
-31
No files matched your search
@@ -40,7 +40,7 @@ impl<'b> VirtualDom {
|
|||||||
for (root_idx, root) in template.template.roots.iter().enumerate() {
|
for (root_idx, root) in template.template.roots.iter().enumerate() {
|
||||||
// We might need to generate an ID for the root node
|
// We might need to generate an ID for the root node
|
||||||
on_stack += match root {
|
on_stack += match root {
|
||||||
TemplateNode::DynamicText(id) | TemplateNode::Dynamic(id) => {
|
TemplateNode::DynamicText { id } | TemplateNode::Dynamic { id } => {
|
||||||
match &template.dynamic_nodes[*id] {
|
match &template.dynamic_nodes[*id] {
|
||||||
// a dynamic text node doesn't replace a template node, instead we create it on the fly
|
// a dynamic text node doesn't replace a template node, instead we create it on the fly
|
||||||
DynamicNode::Text(VText { id: slot, value }) => {
|
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);
|
let this_id = self.next_root(template, root_idx);
|
||||||
|
|
||||||
template.root_ids[root_idx].set(this_id);
|
template.root_ids[root_idx].set(this_id);
|
||||||
@@ -215,7 +215,7 @@ impl<'b> VirtualDom {
|
|||||||
.filter(|root| {
|
.filter(|root| {
|
||||||
matches!(
|
matches!(
|
||||||
root,
|
root,
|
||||||
TemplateNode::Dynamic(_) | TemplateNode::DynamicText(_)
|
TemplateNode::Dynamic { .. } | TemplateNode::DynamicText { .. }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
|
|||||||
@@ -911,7 +911,7 @@ fn matching_components<'a>(
|
|||||||
.zip(right.template.roots.iter())
|
.zip(right.template.roots.iter())
|
||||||
.map(|(l, r)| {
|
.map(|(l, r)| {
|
||||||
let (l, r) = match (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,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::{arena::ElementId, ScopeId, Template};
|
|||||||
/// Templates, however, apply to all subtrees, not just target subtree.
|
/// Templates, however, apply to all subtrees, not just target subtree.
|
||||||
///
|
///
|
||||||
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
|
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
|
||||||
pub struct Mutations<'a> {
|
pub struct Mutations<'a> {
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ impl<'a> VNode<'a> {
|
|||||||
/// Returns [`None`] if the root is actually a static node (Element/Text)
|
/// Returns [`None`] if the root is actually a static node (Element/Text)
|
||||||
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> {
|
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> {
|
||||||
match &self.template.roots[idx] {
|
match &self.template.roots[idx] {
|
||||||
TemplateNode::Element { .. } | TemplateNode::Text(_) => None,
|
TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
|
||||||
TemplateNode::Dynamic(id) | TemplateNode::DynamicText(id) => {
|
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
|
||||||
Some(&self.dynamic_nodes[*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
|
/// This can be created at compile time, saving the VirtualDom time when diffing the tree
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
#[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> {
|
pub enum TemplateNode<'a> {
|
||||||
/// An statically known element in the dom.
|
/// 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
|
/// 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.
|
/// 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 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
|
/// This is separate from the pure Dynamic variant for various optimizations
|
||||||
DynamicText(usize),
|
DynamicText { id: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A node created at runtime
|
/// A node created at runtime
|
||||||
@@ -252,7 +252,11 @@ pub struct VText<'a> {
|
|||||||
|
|
||||||
/// An attribute of the TemplateNode, created at compile time
|
/// An attribute of the TemplateNode, created at compile time
|
||||||
#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
#[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> {
|
pub enum TemplateAttribute<'a> {
|
||||||
/// This attribute is entirely known at compile time, enabling
|
/// This attribute is entirely known at compile time, enabling
|
||||||
Static {
|
Static {
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ impl DesktopController {
|
|||||||
{
|
{
|
||||||
let edits = dom.rebuild();
|
let edits = dom.rebuild();
|
||||||
let mut queue = edit_queue.lock().unwrap();
|
let mut queue = edit_queue.lock().unwrap();
|
||||||
queue.push(serde_json::to_string(&edits.templates).unwrap());
|
queue.push(serde_json::to_string(&edits).unwrap());
|
||||||
queue.push(serde_json::to_string(&edits.edits).unwrap());
|
|
||||||
proxy.send_event(UserWindowEvent::EditsReady).unwrap();
|
proxy.send_event(UserWindowEvent::EditsReady).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +115,6 @@ impl DesktopController {
|
|||||||
|
|
||||||
let (_id, view) = self.webviews.iter_mut().next().unwrap();
|
let (_id, view) = self.webviews.iter_mut().next().unwrap();
|
||||||
|
|
||||||
println!("processing pending edits {:?}", new_queue.len());
|
|
||||||
|
|
||||||
for edit in new_queue.drain(..) {
|
for edit in new_queue.drain(..) {
|
||||||
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
|
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
//! Convert a serialized event to an event trigger
|
//! Convert a serialized event to an event trigger
|
||||||
|
|
||||||
use dioxus_core::ElementId;
|
|
||||||
use dioxus_html::events::*;
|
use dioxus_html::events::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::from_value;
|
use serde_json::from_value;
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ fn render_template_node(node: &TemplateNode, out: &mut String) -> std::fmt::Resu
|
|||||||
}
|
}
|
||||||
write!(out, "</{tag}>")?;
|
write!(out, "</{tag}>")?;
|
||||||
}
|
}
|
||||||
TemplateNode::Text(t) => write!(out, "{t}")?,
|
TemplateNode::Text { text: t } => write!(out, "{t}")?,
|
||||||
TemplateNode::Dynamic(_) => write!(out, "<pre hidden />")?,
|
TemplateNode::Dynamic { id: _ } => write!(out, "<pre hidden />")?,
|
||||||
TemplateNode::DynamicText(t) => write!(out, "<!-- --> {t} <!-- -->")?,
|
TemplateNode::DynamicText { id: t } => write!(out, "<!-- --> {t} <!-- -->")?,
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -76,9 +76,6 @@ export class Interpreter {
|
|||||||
pop() {
|
pop() {
|
||||||
return this.stack.pop();
|
return this.stack.pop();
|
||||||
}
|
}
|
||||||
SaveTemplate(nodes, name) {
|
|
||||||
this.templates[name] = nodes;
|
|
||||||
}
|
|
||||||
MountToRoot() {
|
MountToRoot() {
|
||||||
this.AppendChildren(this.stack.length - 1);
|
this.AppendChildren(this.stack.length - 1);
|
||||||
}
|
}
|
||||||
@@ -213,10 +210,57 @@ export class Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleEdits(edits) {
|
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);
|
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) {
|
AssignId(path, id) {
|
||||||
this.nodes[id] = this.LoadChild(path);
|
this.nodes[id] = this.LoadChild(path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ impl<'a> DynamicContext<'a> {
|
|||||||
|
|
||||||
BodyNode::Text(text) if text.is_static() => {
|
BodyNode::Text(text) if text.is_static() => {
|
||||||
let text = text.source.as_ref().unwrap();
|
let text = text.source.as_ref().unwrap();
|
||||||
quote! { ::dioxus::core::TemplateNode::Text(#text) }
|
quote! { ::dioxus::core::TemplateNode::Text{ text: #text } }
|
||||||
}
|
}
|
||||||
|
|
||||||
BodyNode::RawExpr(_)
|
BodyNode::RawExpr(_)
|
||||||
@@ -258,8 +258,10 @@ impl<'a> DynamicContext<'a> {
|
|||||||
self.node_paths.push(self.current_path.clone());
|
self.node_paths.push(self.current_path.clone());
|
||||||
|
|
||||||
match root {
|
match root {
|
||||||
BodyNode::Text(_) => quote! { ::dioxus::core::TemplateNode::DynamicText(#ct) },
|
BodyNode::Text(_) => {
|
||||||
_ => quote! { ::dioxus::core::TemplateNode::Dynamic(#ct) },
|
quote! { ::dioxus::core::TemplateNode::DynamicText { id: #ct } }
|
||||||
|
}
|
||||||
|
_ => quote! { ::dioxus::core::TemplateNode::Dynamic { id: #ct } },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ impl StringCache {
|
|||||||
}
|
}
|
||||||
cur_path.pop();
|
cur_path.pop();
|
||||||
}
|
}
|
||||||
TemplateNode::Text(text) => write!(chain, "{}", text)?,
|
TemplateNode::Text { text } => write!(chain, "{}", text)?,
|
||||||
TemplateNode::Dynamic(idx) | TemplateNode::DynamicText(idx) => {
|
TemplateNode::Dynamic { id: idx } | TemplateNode::DynamicText { id: idx } => {
|
||||||
chain.segments.push(Segment::Node(*idx))
|
chain.segments.push(Segment::Node(*idx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,9 +101,9 @@ impl WebsysDom {
|
|||||||
}
|
}
|
||||||
el.dyn_into().unwrap()
|
el.dyn_into().unwrap()
|
||||||
}
|
}
|
||||||
Text(t) => self.document.create_text_node(t).dyn_into().unwrap(),
|
Text { text: t } => self.document.create_text_node(t).dyn_into().unwrap(),
|
||||||
DynamicText(_) => self.document.create_text_node("p").dyn_into().unwrap(),
|
DynamicText { id: _ } => self.document.create_text_node("p").dyn_into().unwrap(),
|
||||||
Dynamic(_) => {
|
Dynamic { id: _ } => {
|
||||||
let el = self.document.create_element("pre").unwrap();
|
let el = self.document.create_element("pre").unwrap();
|
||||||
el.toggle_attribute("hidden");
|
el.toggle_attribute("hidden");
|
||||||
el.dyn_into().unwrap()
|
el.dyn_into().unwrap()
|
||||||
@@ -116,6 +116,7 @@ impl WebsysDom {
|
|||||||
let i = &self.interpreter;
|
let i = &self.interpreter;
|
||||||
for edit in edits.drain(..) {
|
for edit in edits.drain(..) {
|
||||||
match edit {
|
match edit {
|
||||||
|
AppendChildren { id, m } => i.AppendChildren(id, m),
|
||||||
AssignId { path, id } => i.AssignId(path, id.0 as u32),
|
AssignId { path, id } => i.AssignId(path, id.0 as u32),
|
||||||
CreatePlaceholder { id } => i.CreatePlaceholder(id.0 as u32),
|
CreatePlaceholder { id } => i.CreatePlaceholder(id.0 as u32),
|
||||||
CreateTextNode { value, id } => i.CreateTextNode(value.into(), id.0 as u32),
|
CreateTextNode { value, id } => i.CreateTextNode(value.into(), id.0 as u32),
|
||||||
|
|||||||
Reference in new issue
Block a user