feat: use walking pattern

This commit is contained in:
Jonathan Kelley committed 2022-10-26 18:04:47 -07:00
1 parent 7cbb4d52dd
commit 22d4bf7346
18 files changed
+751 -843

No files matched your search

-6
View File
@@ -34,17 +34,11 @@ slab = "0.4"
futures-channel = "0.3.21" futures-channel = "0.3.21"
# used for noderefs
once_cell = "1.8"
indexmap = "1.7" indexmap = "1.7"
# Serialize the Edits for use in Webview/Liveview instances # Serialize the Edits for use in Webview/Liveview instances
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
# todo: I want to get rid of this
backtrace = "0.3"
[features] [features]
default = [] default = []
serialize = ["serde"] serialize = ["serde"]
+5
View File
@@ -114,6 +114,11 @@ Some essential reading:
- https://web.dev/rail/ - https://web.dev/rail/
- https://indepth.dev/posts/1008/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react - https://indepth.dev/posts/1008/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react
# Templates
If everything is a template, then we'll have the idea that the only children can b Templates
# What's going on? # What's going on?
Dioxus is a framework for "user experience" - not just "user interfaces." Part of the "experience" is keeping the UI Dioxus is a framework for "user experience" - not just "user interfaces." Part of the "experience" is keeping the UI
+128 -201
View File
@@ -112,9 +112,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
self.scope_stack.push(scopeid); self.scope_stack.push(scopeid);
self.element_stack.push(scope.container); self.element_stack.push(scope.container);
{ self.diff_node(old, new);
self.diff_node(old, new);
}
self.element_stack.pop(); self.element_stack.pop();
self.scope_stack.pop(); self.scope_stack.pop();
@@ -122,58 +120,45 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
} }
pub fn diff_node(&mut self, old_node: &'b VNode<'b>, new_node: &'b VNode<'b>) { pub fn diff_node(&mut self, old_node: &'b VNode<'b>, new_node: &'b VNode<'b>) {
use VNode::{Component, Element, Fragment, Placeholder, Template, Text}; use VNode::{Component, Element, Fragment, Template, Text};
// Same node by ref, no need to diff.
if std::ptr::eq(old_node, new_node) {
return;
}
match (old_node, new_node) { match (old_node, new_node) {
(Text(old), Text(new)) => { (Text(old), Text(new)) => self.diff_text(old, new, old_node, new_node),
self.diff_text_nodes(old, new, old_node, new_node); (Element(old), Element(new)) => self.diff_element(old, new, old_node, new_node),
} (Component(old), Component(new)) => self.diff_component(old_node, new_node, *old, *new),
(Fragment(old), Fragment(new)) => self.diff_fragment(old, new),
(Element(old), Element(new)) => { (Template(old), Template(new)) => self.diff_templates(old, new, old_node, new_node),
self.diff_element_nodes(old, new, old_node, new_node);
}
(Component(old), Component(new)) => {
self.diff_component_nodes(old_node, new_node, *old, *new);
}
(Fragment(old), Fragment(new)) => {
self.diff_fragment_nodes(old, new);
}
(Template(old), Template(new)) => {
self.diff_templates(old, new, old_node, new_node);
}
(Placeholder(_), Placeholder(_)) => {
self.diff_placeholder_nodes(old_node, new_node);
}
( (
Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_) | Placeholder(_), Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_),
Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_) | Placeholder(_), Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_),
) => self.replace_node(old_node, new_node), ) => self.replace_node(old_node, new_node),
} }
} }
pub fn create_node(&mut self, node: &'b VNode<'b>) -> usize { pub fn create_node(&mut self, node: &'b VNode<'b>) -> usize {
match node { match node {
VNode::Text(vtext) => self.create_text_node(vtext, node), VNode::Text(vtext) => self.create_text(vtext, node),
VNode::Element(element) => self.create_element_node(element, node), VNode::Element(element) => self.create_element(element, node),
VNode::Fragment(frag) => self.create_fragment_node(*frag), VNode::Fragment(frag) => self.create_fragment(frag),
VNode::Component(component) => self.create_component_node(*component), VNode::Component(component) => self.create_component_node(*component),
VNode::Template(template) => self.create_template_node(template, node), VNode::Template(template) => self.create_template_node(template, node),
VNode::Placeholder(placeholder) => todo!(),
} }
} }
fn create_text_node(&mut self, text: &'b VText<'b>, node: &'b VNode<'b>) -> usize { fn create_text(&mut self, text: &'b VText<'b>, node: &'b VNode<'b>) -> usize {
let real_id = self.scopes.reserve_node(node); let real_id = self.scopes.reserve_node(node);
text.id.set(Some(real_id)); text.id.set(Some(real_id));
self.mutations.create_text_node(text.text, real_id); self.mutations.create_text_node(text.text, real_id);
1 1
} }
fn create_element_node(&mut self, element: &'b VElement<'b>, node: &'b VNode<'b>) -> usize { fn create_element(&mut self, element: &'b VElement<'b>, node: &'b VNode<'b>) -> usize {
let VElement { let VElement {
tag: tag_name, tag: tag_name,
listeners, listeners,
@@ -198,7 +183,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
let cur_scope_id = self.current_scope(); let cur_scope_id = self.current_scope();
for listener in listeners.iter() { for listener in listeners.iter() {
listener.mounted_node.set(Some(real_id)); listener.mounted_node.set(real_id);
self.mutations.new_event_listener(listener, cur_scope_id); self.mutations.new_event_listener(listener, cur_scope_id);
} }
@@ -216,7 +201,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
1 1
} }
fn create_fragment_node(&mut self, frag: &'b VFragment<'b>) -> usize { fn create_fragment(&mut self, frag: &'b VFragment<'b>) -> usize {
self.create_children(frag.children) self.create_children(frag.children)
} }
@@ -274,17 +259,13 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
created created
} }
pub(crate) fn diff_text_nodes( pub(crate) fn diff_text(
&mut self, &mut self,
old: &'b VText<'b>, old: &'b VText<'b>,
new: &'b VText<'b>, new: &'b VText<'b>,
_old_node: &'b VNode<'b>, _old_node: &'b VNode<'b>,
new_node: &'b VNode<'b>, new_node: &'b VNode<'b>,
) { ) {
if std::ptr::eq(old, new) {
return;
}
// if the node is comming back not assigned, that means it was borrowed but removed // if the node is comming back not assigned, that means it was borrowed but removed
let root = match old.id.get() { let root = match old.id.get() {
Some(id) => id, Some(id) => id,
@@ -307,183 +288,138 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
old_node: &'b VNode<'b>, old_node: &'b VNode<'b>,
new_node: &'b VNode<'b>, new_node: &'b VNode<'b>,
) { ) {
if old.template.id == new.template.id { if old.template.id != new.template.id {
// if they're the same, just diff the dynamic nodes directly return self.replace_node(old_node, new_node);
for (left, right) in old.dynamic_nodes.iter().zip(new.dynamic_nodes.iter()) { }
self.diff_node(left, right);
// if they're the same, just diff the dynamic nodes directly
for (left, right) in old.dynamic_nodes.iter().zip(new.dynamic_nodes.iter()) {
self.diff_node(&left.node, &right.node);
}
for (left, right) in old.dynamic_attrs.iter().zip(new.dynamic_attrs.iter()) {
let id = left.mounted_element.get();
right.mounted_element.set(id);
for (left, right) in right.attrs.iter().zip(left.attrs.iter()) {
if right.value != left.value || right.volatile {
self.mutations
.set_attribute(right.name, right.value, right.namespace, id);
}
} }
// todo: need a way to load up the element bound to these attributes // There's not really any diffing that needs to happen for listeners
for (left, right) in old.dynamic_attrs.iter().zip(new.dynamic_attrs.iter()) { for listener in right.listeners {
// listener.mounted_node.set(id);
} }
// hmm, what we do here?
for (left, right) in old.listeners.iter().zip(new.listeners.iter()) {
//
}
} else {
// else, diff them manually, taking the slow path
self.replace_node(old_node, new_node);
} }
} }
fn create_static_template_nodes(&mut self, node: &'b TemplateNode) { fn create_static_template_nodes(&mut self, node: &'b TemplateNode, id: ElementId) {
let id = ElementId(999999); match *node {
match node {
TemplateNode::Element { TemplateNode::Element {
tag, tag,
attrs, attrs,
children, children,
namespace,
} => { } => {
self.mutations.create_element(tag, None, id); self.mutations.create_element(tag, namespace, id);
for attr in attrs {
for attr in *attrs { self.mutations.set_attribute(
match attr { attr.name,
crate::TemplateAttribute::Dynamic(_) => todo!(), AttributeValue::Text(attr.value),
crate::TemplateAttribute::Static { name, value } => { attr.namespace,
self.mutations.set_attribute( id,
name, );
AttributeValue::Text(value),
None,
id,
);
}
}
} }
for child in children.iter() { for child in children.iter() {
self.create_static_template_nodes(child); self.create_static_template_nodes(child, id);
} }
self.mutations.append_children(children.len() as u32); self.mutations.append_children(children.len() as u32);
} }
TemplateNode::Text(ref text) => self.mutations.create_text_node(text, id), TemplateNode::Text(text) => self.mutations.create_text_node(text, id),
TemplateNode::Dynamic(_) => self.mutations.create_placeholder(id), TemplateNode::Dynamic(_) => self.mutations.create_placeholder(id),
} }
} }
/// Create the template from scratch using instructions, cache it, and then use the instructions to build it /// Create the template from scratch using instructions, cache it, and then use the instructions to build it
///
/// This would be way easier if the ID could just be unique *after* cloning
///
/// If we traversed the template
fn create_template_node(&mut self, template: &'b VTemplate<'b>, node: &'b VNode<'b>) -> usize { fn create_template_node(&mut self, template: &'b VTemplate<'b>, node: &'b VNode<'b>) -> usize {
let template_id = template.template.id; // Reserve a single node for all the template nodes to reuse
let templates = self.scopes.template_cache.borrow_mut(); template.node_id.set(self.scopes.reserve_node(node));
// create and insert the template if it doesn't exist within the VirtualDom (it won't exist on the renderer either) // Save the template if it doesn't exist
// todo: use &mut cache instead of borrowed cache
let mut templates = self.scopes.template_cache.borrow_mut();
if !templates.contains(&template.template) { if !templates.contains(&template.template) {
template template
.template .template
.roots .roots
.into_iter() .into_iter()
.for_each(|node| self.create_static_template_nodes(node)); .for_each(|node| self.create_static_template_nodes(node, template.node_id.get()));
self.mutations self.mutations
.save(template_id, template.template.roots.len() as u32); .save(template.template.id, template.template.roots.len() as u32);
templates.insert(template.template);
} }
self.mutations.load(template_id); // Walk the roots backwards, creating nodes and assigning IDs
let mut dynamic_attrs = template.dynamic_attrs.iter().peekable();
let mut dynamic_nodes = template.dynamic_nodes.iter().peekable();
let mut created = 0; let mut on_stack = 0;
for (root_idx, root) in template.template.roots.iter().enumerate() {
on_stack += match root {
TemplateNode::Dynamic(id) => self.create_node(&template.dynamic_nodes[*id].node),
TemplateNode::Element { .. } | TemplateNode::Text(_) => 1,
};
// create the dynamic nodes // we're on top of a node that has a dynamic attribute for a descndent
for node in template.dynamic_nodes.iter() { // Set that attribute now before the stack gets in a weird state
created += self.create_node(node); // Roots may not be more than 255 nodes long, enforced by the macro
while let Some(loc) = dynamic_attrs.next_if(|attr| attr.pathway[0] == root_idx as u8) {
// Attach all the elementIDs to the nodes with dynamic content
let id = self.scopes.reserve_node(node);
self.mutations.assign_id(&loc.pathway[1..], id);
loc.mounted_element.set(id);
for attr in loc.attrs {
self.mutations
.set_attribute(attr.name, attr.value, attr.namespace, id);
}
for listener in loc.listeners {
listener.mounted_node.set(id);
}
}
while let Some(dyn_node) = dynamic_nodes.next_if(|f| f.pathway[0] == root_idx as u8) {
// we're on top of a node that has a dynamic node for a descndent
// Set that node now
// Roots may not be more than 255 nodes long, enforced by the macro
if dyn_node.pathway[0] == root_idx as u8 {
let created = self.create_node(&dyn_node.node);
self.mutations
.replace_descendant(&dyn_node.pathway[1..], created as u32);
}
}
} }
// set any dynamic attributes on_stack
for attr in template.dynamic_attrs.iter() {
// assign an ID to the element
let id = self.scopes.reserve_node(node);
self.mutations
.set_attribute(attr.name, attr.value, attr.namespace, id);
}
todo!()
// let def = template.template;
// let mut templates = self.scopes.template_cache.borrow_mut();
// if !templates.contains(&def) {
// // self.create_template_def(def);
// templates.insert(def);
// }
// let mut nodes_created = 0;
// let mut dynamic_nodes = template.dynamic_nodes.iter().enumerate().rev();
// for node in template.rendered_nodes {
// match node {
// // Give it an ID
// crate::innerlude::TemplateRoots::Static(_) => todo!(),
// // let the creation step give it an ID
// crate::innerlude::TemplateRoots::Runtime(_) => todo!(),
// }
// }
// // Create all the dynamic nodes and merge them into the template
// for (index, node) in dynamic_nodes {
// let new_created = self.create_node(node);
// self.mutations.edits.push(DomEdit::MergeTemplate {
// index: index as u32,
// num_children: new_created as u32,
// })
// }
// nodes_created
} }
fn create_template_static_node(&mut self, nodes: &'static [VNode<'static>]) -> usize { fn diff_element(
todo!()
// let mut created = 0;
// for node in nodes {
// match *node {
// TemplateNode::Element(el) => {
// for attr in el.attributes {
// match attr {
// crate::template::TemplateAttribute::Dynamic => todo!(),
// crate::template::TemplateAttribute::Static { attr } => {
// self.mutations.set_attribute(attr, 0);
// }
// }
// }
// self.mutations.create_element(el.tag, None, ElementId(0));
// if !nodes.is_empty() {
// let res = self.create_template_static_node(nodes);
// self.mutations.append_children(res as u32);
// }
// created += 1;
// }
// TemplateNode::StaticText(text) => {
// self.mutations.create_text_node(text, ElementId(0));
// created += 1;
// }
// TemplateNode::DynamicText(_)
// | TemplateNode::DynamicExpr(_)
// | TemplateNode::DynamicComponent(_) => {
// self.mutations.create_placeholder(ElementId(0));
// created += 1;
// }
// }
// }
// created
}
fn diff_element_nodes(
&mut self, &mut self,
old: &'b VElement<'b>, old: &'b VElement<'b>,
new: &'b VElement<'b>, new: &'b VElement<'b>,
old_node: &'b VNode<'b>, old_node: &'b VNode<'b>,
new_node: &'b VNode<'b>, new_node: &'b VNode<'b>,
) { ) {
if std::ptr::eq(old, new) {
return;
}
// if the node is comming back not assigned, that means it was borrowed but removed // if the node is comming back not assigned, that means it was borrowed but removed
let root = match old.id.get() { let root = match old.id.get() {
Some(id) => id, Some(id) => id,
@@ -563,7 +499,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
self.mutations.remove_event_listener(listener.event, root); self.mutations.remove_event_listener(listener.event, root);
} }
for listener in new.listeners { for listener in new.listeners {
listener.mounted_node.set(Some(root)); listener.mounted_node.set(root);
self.mutations.new_event_listener(listener, cur_scope_id); self.mutations.new_event_listener(listener, cur_scope_id);
} }
} }
@@ -580,7 +516,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
}; };
} }
fn diff_component_nodes( fn diff_component(
&mut self, &mut self,
old_node: &'b VNode<'b>, old_node: &'b VNode<'b>,
new_node: &'b VNode<'b>, new_node: &'b VNode<'b>,
@@ -592,10 +528,6 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
.get() .get()
.expect("existing component nodes should have a scope"); .expect("existing component nodes should have a scope");
if std::ptr::eq(old, new) {
return;
}
// Make sure we're dealing with the same component (by function pointer) // Make sure we're dealing with the same component (by function pointer)
if old.user_fc == new.user_fc { if old.user_fc == new.user_fc {
self.enter_scope(scope_addr); self.enter_scope(scope_addr);
@@ -654,11 +586,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
} }
} }
fn diff_fragment_nodes(&mut self, old: &'b VFragment<'b>, new: &'b VFragment<'b>) { fn diff_fragment(&mut self, old: &'b VFragment<'b>, new: &'b VFragment<'b>) {
if std::ptr::eq(old, new) {
return;
}
todo!() todo!()
// // This is the case where options or direct vnodes might be used. // // This is the case where options or direct vnodes might be used.
// // In this case, it's faster to just skip ahead to their diff // // In this case, it's faster to just skip ahead to their diff
@@ -1086,7 +1014,8 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
self.scopes.collect_garbage(id); self.scopes.collect_garbage(id);
} }
VNode::Text(_) | VNode::Placeholder(_) => { // | VNode::Placeholder(_)
VNode::Text(_) => {
let id = old let id = old
.try_mounted_id() .try_mounted_id()
.unwrap_or_else(|| panic!("broke on {:?}", old)); .unwrap_or_else(|| panic!("broke on {:?}", old));
@@ -1145,15 +1074,15 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
} }
} }
} }
VNode::Placeholder(a) => { // VNode::Placeholder(a) => {
let id = a.id.get().unwrap(); // let id = a.id.get().unwrap();
self.scopes.collect_garbage(id); // self.scopes.collect_garbage(id);
a.id.set(None); // a.id.set(None);
if gen_muts { // if gen_muts {
self.mutations.remove(id); // self.mutations.remove(id);
} // }
} // }
VNode::Element(e) => { VNode::Element(e) => {
let id = e.id.get().unwrap(); let id = e.id.get().unwrap();
@@ -1242,8 +1171,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
} }
VNode::Template(c) => { VNode::Template(c) => {
todo!() todo!()
} } // VNode::Placeholder(_) => todo!(),
VNode::Placeholder(_) => todo!(),
} }
} }
} }
@@ -1261,8 +1189,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
} }
VNode::Template(t) => { VNode::Template(t) => {
todo!() todo!()
} } // VNode::Placeholder(_) => todo!(),
VNode::Placeholder(_) => todo!(),
} }
} }
} }
@@ -1270,7 +1197,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
// recursively push all the nodes of a tree onto the stack and return how many are there // recursively push all the nodes of a tree onto the stack and return how many are there
fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize { fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
match node { match node {
VNode::Text(_) | VNode::Element(_) | VNode::Placeholder(_) => { VNode::Text(_) | VNode::Element(_) => {
self.mutations.push_root(node.mounted_id()); self.mutations.push_root(node.mounted_id());
1 1
} }
@@ -1294,7 +1221,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
} }
} }
pub(crate) fn diff_placeholder_nodes(&self, old_node: &VNode, new_node: &VNode) { pub(crate) fn diff_placeholder(&self, old_node: &VNode, new_node: &VNode) {
todo!() todo!()
} }
} }
+36 -8
View File
@@ -20,6 +20,7 @@ pub trait Renderer<'a> {
fn pop_root(&mut self); fn pop_root(&mut self);
/// Replace the given element with the next m elements on the stack /// Replace the given element with the next m elements on the stack
fn replace_with(&mut self, root: ElementId, m: u32); fn replace_with(&mut self, root: ElementId, m: u32);
/// Insert the next m elements on the stack after the given element /// Insert the next m elements on the stack after the given element
fn insert_after(&mut self, root: ElementId, n: u32); fn insert_after(&mut self, root: ElementId, n: u32);
/// Insert the next m elements on the stack before the given element /// Insert the next m elements on the stack before the given element
@@ -41,6 +42,8 @@ pub trait Renderer<'a> {
fn remove(&mut self, root: ElementId); fn remove(&mut self, root: ElementId);
/// Remove an attribute from an existing element /// Remove an attribute from an existing element
fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId); fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId);
/// Remove all the children of the given element
fn remove_children(&mut self, root: ElementId);
/// Attach a new listener to the dom /// Attach a new listener to the dom
fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId); fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId);
@@ -58,13 +61,38 @@ pub trait Renderer<'a> {
root: ElementId, root: ElementId,
); );
/// Save the current n nodes to the ID to be loaded later /// General statistics for doing things that extend outside of the renderer
fn save(&mut self, id: &str, num: u32);
/// Loads a set of saved nodes from the ID
fn load(&mut self, id: &str);
// General statistics for doing things that extend outside of the renderer
///
fn mark_dirty_scope(&mut self, scope: ScopeId); fn mark_dirty_scope(&mut self, scope: ScopeId);
/// Save the current n nodes to the ID to be loaded later
fn save(&mut self, id: &'static str, num: u32);
/// Loads a set of saved nodes from the ID into a scratch space
fn load(&mut self, id: &'static str, index: u32);
/// Assign the element on the stack's descendent the given ID
fn assign_id(&mut self, descendent: &'static [u8], id: ElementId);
/// Replace the given element of the topmost element with the next m elements on the stack
/// Is essentially a combination of assign_id and replace_with
fn replace_descendant(&mut self, descendent: &'static [u8], m: u32);
} }
/*
div {
div {
div {
div {}
}
}
}
push_child(0)
push_child(1)
push_child(3)
push_child(4)
pop
pop
clone_node(0)
set_node(el, [1,2,3,4])
set_attribute("class", "foo")
append_child(1)
*/
+76 -58
View File
@@ -1,6 +1,6 @@
use std::{ use std::{
any::Any, any::Any,
fmt::{Arguments, Display, Formatter}, fmt::{Arguments, Formatter},
}; };
use bumpalo::Bump; use bumpalo::Bump;
@@ -8,41 +8,40 @@ use bumpalo::Bump;
/// Possible values for an attribute /// Possible values for an attribute
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum AttributeValue<'a> { pub enum AttributeValue<'a> {
/// Reference strs, most common
Text(&'a str), Text(&'a str),
Float32(f32), /// Basic float values
Float(f32),
/// Basic Int values
Int(i32),
/// Basic bool values
Bool(bool), Bool(bool),
/// Everything else
Any(&'a dyn AnyAttributeValue), Any(&'a dyn AnyAttributeValue),
} }
// #[cfg(feature = "serialize")]
impl<'a> PartialEq for AttributeValue<'a> { impl<'a> PartialEq for AttributeValue<'a> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0, (Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Float32(l0), Self::Float32(r0)) => l0 == r0, (Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0, (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
// (Self::Any(l0), Self::Any(r0)) => l0.cmp(r0), (Self::Any(l0), Self::Any(r0)) => (*l0).cmp_any(*r0),
_ => false, _ => false,
} }
} }
} }
impl<'a> Display for AttributeValue<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
impl std::fmt::Debug for AttributeValue<'_> { impl std::fmt::Debug for AttributeValue<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
todo!() match self {
// match self { AttributeValue::Text(s) => write!(f, "AttributeValue::Text({:?})", s),
// AttributeValue::Text(s) => write!(f, "AttributeValue::Text({:?})", s), AttributeValue::Float(v) => write!(f, "AttributeValue::Float({:?})", v),
// AttributeValue::Float32(f) => write!(f, "AttributeValue::Float32({:?})", f), AttributeValue::Int(v) => write!(f, "AttributeValue::Int({:?})", v),
// AttributeValue::Bool(b) => write!(f, "AttributeValue::Bool({:?})", b), AttributeValue::Bool(b) => write!(f, "AttributeValue::Bool({:?})", b),
// AttributeValue::Any(a) => write!(f, "AttributeValue::Any({:?})", a), AttributeValue::Any(_) => write!(f, "AttributeValue::Any()"),
// } }
} }
} }
@@ -51,24 +50,27 @@ pub trait IntoAttributeValue<'a> {
/// Convert into an attribute value /// Convert into an attribute value
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>; fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
} }
impl<'a> IntoAttributeValue<'a> for f32 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Float32(self)
}
}
impl<'a> IntoAttributeValue<'a> for bool {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Bool(self)
}
}
impl<'a> IntoAttributeValue<'a> for &'a str { impl<'a> IntoAttributeValue<'a> for &'a str {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> { fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Text(self) AttributeValue::Text(self)
} }
} }
impl<'a> IntoAttributeValue<'a> for f32 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Float(self)
}
}
impl<'a> IntoAttributeValue<'a> for i32 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Int(self)
}
}
impl<'a> IntoAttributeValue<'a> for bool {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Bool(self)
}
}
impl<'a> IntoAttributeValue<'a> for Arguments<'_> { impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> { fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
use bumpalo::core_alloc::fmt::Write; use bumpalo::core_alloc::fmt::Write;
@@ -78,29 +80,6 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
} }
} }
impl<'a, T> IntoAttributeValue<'a> for &'a T
where
T: PartialEq,
{
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
todo!()
// AttributeValue::Any(ArbitraryAttributeValue {
// value: self,
// cmp: |a, b| {
// if let Some(a) = a.as_any().downcast_ref::<T>() {
// if let Some(b) = b.as_any().downcast_ref::<T>() {
// a == b
// } else {
// false
// }
// } else {
// false
// }
// },
// })
}
}
// todo // todo
#[allow(missing_docs)] #[allow(missing_docs)]
impl<'a> AttributeValue<'a> { impl<'a> AttributeValue<'a> {
@@ -130,16 +109,55 @@ impl<'a> AttributeValue<'a> {
/// If you want to override the default behavior, you should implement PartialEq through a wrapper type /// If you want to override the default behavior, you should implement PartialEq through a wrapper type
pub trait AnyAttributeValue: Any { pub trait AnyAttributeValue: Any {
/// Perform a comparison between two values /// Perform a comparison between two values
fn cmp_any(&self, _other: &dyn Any) -> bool { fn cmp_any<'a>(&'a self, _other: &'a dyn AnyAttributeValue) -> bool {
false false
} }
} }
impl<T: Any + PartialEq> AnyAttributeValue for T { impl<T: Any + PartialEq> AnyAttributeValue for T {
fn cmp_any(&self, other: &dyn Any) -> bool { fn cmp_any(&self, other: &dyn AnyAttributeValue) -> bool {
match other.downcast_ref::<T>() { // we can't, for whatever reason use other as &dyn Any
Some(t) => self == t, let right: &dyn Any = unsafe { std::mem::transmute(other) };
match right.downcast_ref::<T>() {
Some(right) => self == right,
None => false, None => false,
} }
} }
} }
#[test]
fn cmp_any_works_even_though_it_transmutes() {
// same type, same value
let a = 2;
let b = 2;
assert!(a.cmp_any(&b as &dyn AnyAttributeValue));
// same type, different value
let a = "asds";
let b = "asdsasd";
assert!(!a.cmp_any(&b as &dyn AnyAttributeValue));
// different type, different values
let a = 123;
let b = "asdsasd";
assert!(!a.cmp_any(&b as &dyn AnyAttributeValue));
// Custom structs
#[derive(PartialEq)]
struct CustomStruct {
a: i32,
}
let a = CustomStruct { a: 1 };
let b = CustomStruct { a: 1 };
assert!(a.cmp_any(&b as &dyn AnyAttributeValue));
let a = CustomStruct { a: 1 };
let b = CustomStruct { a: 123 };
assert!(!a.cmp_any(&b as &dyn AnyAttributeValue));
let a = CustomStruct { a: 1 };
let b = "asdasd";
assert!(!a.cmp_any(&b as &dyn AnyAttributeValue));
}
+1 -5
View File
@@ -70,10 +70,6 @@ pub struct Attribute<'a> {
/// Used in controlled components to ensure changes are propagated. /// Used in controlled components to ensure changes are propagated.
pub volatile: bool, pub volatile: bool,
/// A reverse lookup for tracking down attributes for templates
/// Not used for anything else
pub mounted_node: Cell<Option<ElementId>>,
/// The value of the attribute. /// The value of the attribute.
pub value: AttributeValue<'a>, pub value: AttributeValue<'a>,
} }
@@ -83,7 +79,7 @@ pub struct Attribute<'a> {
pub struct Listener<'bump> { pub struct Listener<'bump> {
/// The ID of the node that this listener is mounted to /// The ID of the node that this listener is mounted to
/// Used to generate the event listener's ID on the DOM /// Used to generate the event listener's ID on the DOM
pub mounted_node: Cell<Option<ElementId>>, pub mounted_node: Cell<ElementId>,
/// The type of event to listen for. /// The type of event to listen for.
/// ///
+10 -19
View File
@@ -108,7 +108,6 @@ impl<'a> NodeFactory<'a> {
name, name,
namespace, namespace,
volatile: is_volatile, volatile: is_volatile,
mounted_node: Default::default(),
value: val.into_value(self.bump), value: val.into_value(self.bump),
} }
} }
@@ -125,7 +124,6 @@ impl<'a> NodeFactory<'a> {
name, name,
namespace, namespace,
volatile: is_volatile, volatile: is_volatile,
mounted_node: Default::default(),
value, value,
} }
} }
@@ -172,7 +170,7 @@ impl<'a> NodeFactory<'a> {
pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> { pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
Listener { Listener {
event, event,
mounted_node: Cell::new(None), mounted_node: Cell::new(ElementId(0)),
callback, callback,
} }
} }
@@ -234,27 +232,20 @@ impl<'a> NodeFactory<'a> {
/// Create a refrence to a template /// Create a refrence to a template
pub fn template_ref( pub fn template_ref(
&self, &self,
template: fn() -> Template<'static>, template: Template<'static>,
nodes: &'a [VNode<'a>], nodes: &'a [VNode<'a>],
attributes: &'a [Attribute<'a>], attributes: &'a [Attribute<'a>],
listeners: &'a [Listener<'a>], listeners: &'a [Listener<'a>],
key: Option<Arguments>, key: Option<Arguments>,
) -> VNode<'a> { ) -> VNode<'a> {
// let borrow_ref = self.scope.templates.borrow(); VNode::Template(self.bump.alloc(VTemplate {
// // We only create the template if it doesn't already exist to allow for hot reloading key: None,
// if !borrow_ref.contains_key(&id) { node_id: Cell::new(ElementId(0)),
// drop(borrow_ref); template,
// let mut borrow_mut = self.scope.templates.borrow_mut(); dynamic_nodes: self.bump.alloc([]),
// borrow_mut.insert(id.clone(), Rc::new(RefCell::new(template))); dynamic_attrs: self.bump.alloc([]),
// } listeners,
todo!() }))
// VNode::TemplateRef(self.bump.alloc(VTemplate {
// dynamic_context,
// template_id: id,
// node_ids: RefCell::new(Vec::new()),
// parent: Cell::new(None),
// template_ref_id: Cell::new(None),
// }))
} }
} }
+1 -1
View File
@@ -6,7 +6,7 @@ pub struct VFragment<'src> {
/// The key of the fragment to be used during keyed diffing. /// The key of the fragment to be used during keyed diffing.
pub key: Option<&'src str>, pub key: Option<&'src str>,
/// The [`ElementId`] of the placeholder. /// The [`ElementId`] of the placeholder if it exists
pub placeholder: Cell<Option<ElementId>>, pub placeholder: Cell<Option<ElementId>>,
/// Fragments can never have zero children. Enforced by NodeFactory. /// Fragments can never have zero children. Enforced by NodeFactory.
+1 -12
View File
@@ -5,7 +5,6 @@
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
mod Placeholder;
mod arbitrary_value; mod arbitrary_value;
mod component; mod component;
mod element; mod element;
@@ -25,7 +24,7 @@ pub use suspense::*;
pub use template::*; pub use template::*;
pub use text::*; pub use text::*;
use self::Placeholder::VPlaceholder; use self::placeholder::VPlaceholder;
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM. /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
/// ///
@@ -117,9 +116,6 @@ pub enum VNode<'src> {
/// ///
/// ///
Template(&'src VTemplate<'src>), Template(&'src VTemplate<'src>),
///
Placeholder(&'src VPlaceholder),
} }
/// An Element's unique identifier. /// An Element's unique identifier.
@@ -140,7 +136,6 @@ impl<'src> VNode<'src> {
VNode::Fragment(f) => f.key, VNode::Fragment(f) => f.key,
VNode::Template(t) => t.key, VNode::Template(t) => t.key,
VNode::Text(_t) => None, VNode::Text(_t) => None,
VNode::Placeholder(_p) => None,
} }
} }
@@ -161,7 +156,6 @@ impl<'src> VNode<'src> {
VNode::Fragment(_) => None, VNode::Fragment(_) => None,
VNode::Component(_) => None, VNode::Component(_) => None,
VNode::Template(_) => None, VNode::Template(_) => None,
VNode::Placeholder(el) => el.id.get(),
} }
} }
@@ -173,7 +167,6 @@ impl<'src> VNode<'src> {
VNode::Component(c) => VNode::Component(c), VNode::Component(c) => VNode::Component(c),
VNode::Fragment(f) => VNode::Fragment(f), VNode::Fragment(f) => VNode::Fragment(f),
VNode::Template(t) => VNode::Template(t), VNode::Template(t) => VNode::Template(t),
VNode::Placeholder(p) => VNode::Placeholder(p),
} }
} }
} }
@@ -209,10 +202,6 @@ impl Debug for VNode<'_> {
.debug_struct("VNode::Templates") .debug_struct("VNode::Templates")
.field("template_id", &temp.template.id) .field("template_id", &temp.template.id)
.finish(), .finish(),
VNode::Placeholder(place) => s
.debug_struct("VNode::Placeholder")
.field("id", &place.id)
.finish(),
} }
} }
} }
+2 -2
View File
@@ -1,7 +1,7 @@
use crate::ElementId;
use std::cell::Cell; use std::cell::Cell;
use crate::ElementId; /// A placeholder node
pub struct VPlaceholder { pub struct VPlaceholder {
pub id: Cell<Option<ElementId>>, pub id: Cell<Option<ElementId>>,
pub dynamic_index: Option<usize>, pub dynamic_index: Option<usize>,
+32 -10
View File
@@ -1,16 +1,25 @@
use std::hash::Hash; use std::{cell::Cell, hash::Hash};
use crate::{Attribute, Listener, VNode}; use crate::{Attribute, ElementId, Listener, VNode};
/// A reference to a template along with any context needed to hydrate it /// A reference to a template along with any context needed to hydrate it
pub struct VTemplate<'a> { pub struct VTemplate<'a> {
pub key: Option<&'a str>, pub key: Option<&'a str>,
// The ID assigned for all nodes in this template
pub node_id: Cell<ElementId>,
// Position this template for fragments and stuff
pub head_id: Cell<ElementId>,
pub tail_id: Cell<ElementId>,
pub template: Template<'static>, pub template: Template<'static>,
pub dynamic_nodes: &'a [VNode<'a>], /// All the non-root dynamic nodes
pub dynamic_nodes: &'a [NodeLocation<'a>],
pub dynamic_attrs: &'a [Attribute<'a>], pub dynamic_attrs: &'a [AttributeLocation<'a>],
pub listeners: &'a [Listener<'a>], pub listeners: &'a [Listener<'a>],
} }
@@ -26,13 +35,11 @@ pub struct Template<'a> {
} }
impl<'a> Eq for Template<'a> {} impl<'a> Eq for Template<'a> {}
impl<'a> PartialEq for Template<'a> { impl<'a> PartialEq for Template<'a> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.id == other.id self.id == other.id
} }
} }
impl<'a> Hash for Template<'a> { impl<'a> Hash for Template<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state); self.id.hash(state);
@@ -41,8 +48,10 @@ impl<'a> Hash for Template<'a> {
/// A weird-ish variant of VNodes with way more limited types /// A weird-ish variant of VNodes with way more limited types
pub enum TemplateNode<'a> { pub enum TemplateNode<'a> {
/// A simple element
Element { Element {
tag: &'static str, tag: &'static str,
namespace: Option<&'static str>,
attrs: &'a [TemplateAttribute<'a>], attrs: &'a [TemplateAttribute<'a>],
children: &'a [TemplateNode<'a>], children: &'a [TemplateNode<'a>],
}, },
@@ -50,8 +59,21 @@ pub enum TemplateNode<'a> {
Dynamic(usize), Dynamic(usize),
} }
pub enum TemplateAttribute<'a> { pub struct TemplateAttribute<'a> {
// todo: more values pub name: &'static str,
Static { name: &'static str, value: &'a str }, pub value: &'a str,
Dynamic(usize), pub namespace: Option<&'static str>,
pub volatile: bool,
}
pub struct AttributeLocation<'a> {
pub pathway: &'static [u8],
pub mounted_element: Cell<ElementId>,
pub attrs: &'a [Attribute<'a>],
pub listeners: &'a [Listener<'a>],
}
pub struct NodeLocation<'a> {
pub pathway: &'static [u8],
pub node: VNode<'a>,
} }
+2 -12
View File
@@ -1,15 +1,5 @@
use crate::{ use crate::ElementId;
innerlude::{ use std::cell::Cell;
AttributeValue, ComponentPtr, Element, IntoAttributeValue, Properties, Scope, ScopeId,
ScopeState, Template,
},
AnyEvent, Component, ElementId,
};
use bumpalo::{boxed::Box as BumpBox, Bump};
use std::{
cell::{Cell, RefCell},
fmt::{Arguments, Debug, Formatter},
};
/// A bump-allocated string slice and metadata. /// A bump-allocated string slice and metadata.
pub struct VText<'src> { pub struct VText<'src> {
+7 -1
View File
@@ -12,7 +12,6 @@ use std::{collections::VecDeque, iter::FromIterator, task::Poll};
/// A virtual node system that progresses user events and diffs UI trees. /// A virtual node system that progresses user events and diffs UI trees.
/// ///
///
/// ## Guide /// ## Guide
/// ///
/// Components are defined as simple functions that take [`Scope`] and return an [`Element`]. /// Components are defined as simple functions that take [`Scope`] and return an [`Element`].
@@ -505,6 +504,13 @@ impl VirtualDom {
} }
} }
/// Run the virtualdom, waiting for all async components to finish rendering
///
/// As they finish rendering, the virtualdom will apply the mutations to the renderer.
pub async fn render(&mut self, renderer: &mut impl Renderer<'_>) {
//
}
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch. /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
/// ///
/// The diff machine expects the RealDom's stack to be the root of the application. /// The diff machine expects the RealDom's stack to be the root of the application.
+32 -75
View File
@@ -1,87 +1,44 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_core::{Attribute, TemplateAttribute}; use dioxus_core::{Attribute, TemplateAttribute};
use dioxus_edit_stream::*;
fn basic_syntax_is_a_template(cx: Scope) -> Element { fn basic_syntax_is_a_template(cx: Scope) -> Element {
let asd = 123; let asd = 123;
let var = 123;
let g = rsx! { cx.render(rsx! {
div { div { class: "asd", class: "{asd}",
class: "asd", onclick: move |_| {},
// class: "{asd}", div { "{var}" }
// onclick: move |_| {}, div {
// div { "{var}" } h1 { "var" }
p { "you're great!" }
div {
background_color: "red",
h1 { "var" }
div {
b { "asd" }
"not great"
}
}
p { "you're great!" }
}
} }
}; })
}
let __cx = NodeFactory::new(&cx); fn basic_template(cx: Scope) -> Element {
cx.render(rsx! {
div {"hi!"}
})
}
static attrs: &'static [TemplateAttribute<'static>] = #[test]
&[::dioxus::core::TemplateAttribute::Static( fn basic_prints() {
::dioxus::core::Attribute { let dom = VirtualDom::new(basic_template);
name: "class",
namespace: None,
volatile: false,
mounted_node: Default::default(),
value: ::dioxus::core::AttributeValue::Text("asd"),
},
)];
__cx . template_ref ( let renderer = dioxus_edit_stream::Mutations::default();
|| :: dioxus :: core :: Template { dom.rebuild(&mut renderer);
id : "packages/dioxus/tests/rsx_syntax.rs:7:13:/Users/jonkelley/Development/dioxus/packages/dioxus" ,
roots : &[
:: dioxus :: core :: TemplateNode :: Element {
tag : dioxus_elements :: div :: TAG_NAME ,
attrs : attrs,
children : & [] ,
}] ,
} ,
__cx . bump () . alloc ([]) , __cx . bump () . alloc ([]) , __cx . bump () . alloc ([]) ,
None
);
// let static_attr = ::dioxus::core::TemplateAttribute::Static(::dioxus::core::Attribute { dbg!(renderer.edits);
// name: "class",
// namespace: None,
// volatile: false,
// mounted_node: Default::default(),
// value: ::dioxus::core::AttributeValue::Text("asd"),
// });
// __cx . template_ref (|| :: dioxus :: core :: Template { id : "packages/dioxus/tests/rsx_syntax.rs:7:13:/Users/jonkelley/Development/dioxus/packages/dioxus" , roots : & [:: dioxus :: core :: TemplateNode :: Element { tag : dioxus_elements :: div :: TAG_NAME , attrs : & [static_attr , :: dioxus :: core :: TemplateAttribute :: Dynamic (0usize)] , children : & [] , }] , } , __cx . bump () . alloc ([]) , __cx . bump () . alloc ([__cx . attr (dioxus_elements :: div :: class . 0 , :: core :: fmt :: Arguments :: new_v1 (& [""] , & [:: core :: fmt :: ArgumentV1 :: new_display (& asd)]) , None , false)]) , __cx . bump () . alloc ([]) , None);
cx.render(g)
// let __cx = NodeFactory::new(&cx);
// let t = __cx.template_ref (
// || :: dioxus :: core :: Template {
// id : "packages/dioxus/tests/rsx_syntax.rs:8:13:/Users/jonkelley/Development/dioxus/packages/dioxus" ,
// roots : & [
// :: dioxus :: core :: TemplateNode :: Element {
// tag : dioxus_elements :: div :: TAG_NAME ,
// attrs : & [:: dioxus :: core :: TemplateAttribute :: Dynamic (0usize)] ,
// children : & [] ,
// }
// ],
// },
// &[] ,
// {
// let mut arr = dioxus_core::exports::bumpalo::vec![in __cx.bump()];
// arr.push(Attribute {
// name: "asd",
// namespace: None,
// volatile: false,
// mounted_node: Default::default(),
// value: dioxus_core::AttributeValue::Text(
// __cx.raw_text(format_args!("{asd}")).0
// ),
// });
// arr.into_bump_slice() as &[::dioxus::core::Attribute]
// },
// & [] ,
// None
// );
// Some(t)
} }
+356 -376
View File
@@ -1,378 +1,358 @@
use dioxus_core::*; use dioxus_core::*;
// /// ## Mutations /// ## Mutations
// /// ///
// /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
// /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
// /// applied the edits. /// applied the edits.
// /// ///
// /// Mutations are the only link between the RealDOM and the VirtualDOM. /// Mutations are the only link between the RealDOM and the VirtualDOM.
// pub struct Mutations<'a> { #[derive(Default)]
// /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM. pub struct Mutations<'a> {
// pub edits: Vec<DomEdit<'a>>, /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
pub edits: Vec<DomEdit<'a>>,
// /// The list of Scopes that were diffed, created, and removed during the Diff process.
// pub dirty_scopes: FxHashSet<ScopeId>, /// The list of Scopes that were diffed, created, and removed during the Diff process.
pub dirty_scopes: Vec<ScopeId>,
// /// The list of nodes to connect to the RealDOM. }
// pub refs: Vec<NodeRefMutation<'a>>,
// } /// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
/// network or through FFI boundaries.
// impl Debug for Mutations<'_> { #[derive(Debug, PartialEq)]
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { #[cfg_attr(
// f.debug_struct("Mutations") feature = "serialize",
// .field("edits", &self.edits) derive(serde::Serialize, serde::Deserialize),
// .field("noderefs", &self.refs) serde(tag = "type")
// .finish() )]
// } pub enum DomEdit<'bump> {
// } /// Push the given root node onto our stack.
PushRoot {
// /// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the /// The ID of the root node to push.
// /// network or through FFI boundaries. root: ElementId,
// #[derive(Debug, PartialEq)] },
// #[cfg_attr(
// feature = "serialize", /// Pop the topmost node from our stack and append them to the node
// derive(serde::Serialize, serde::Deserialize), /// at the top of the stack.
// serde(tag = "type") AppendChildren {
// )] /// How many nodes should be popped from the stack.
// pub enum DomEdit<'bump> { /// The node remaining on the stack will be the target for the append.
// /// Push the given root node onto our stack. many: u32,
// PushRoot { },
// /// The ID of the root node to push.
// root: ElementId, /// Replace a given (single) node with a handful of nodes currently on the stack.
// }, ReplaceWith {
/// The ID of the node to be replaced.
// /// Pop the topmost node from our stack and append them to the node root: ElementId,
// /// at the top of the stack.
// AppendChildren { /// How many nodes should be popped from the stack to replace the target node.
// /// How many nodes should be popped from the stack. m: u32,
// /// The node remaining on the stack will be the target for the append. },
// many: u32,
// }, /// Insert a number of nodes after a given node.
InsertAfter {
// /// Replace a given (single) node with a handful of nodes currently on the stack. /// The ID of the node to insert after.
// ReplaceWith { root: ElementId,
// /// The ID of the node to be replaced.
// root: ElementId, /// How many nodes should be popped from the stack to insert after the target node.
n: u32,
// /// How many nodes should be popped from the stack to replace the target node. },
// m: u32,
// }, /// Insert a number of nodes before a given node.
InsertBefore {
// /// Insert a number of nodes after a given node. /// The ID of the node to insert before.
// InsertAfter { root: ElementId,
// /// The ID of the node to insert after.
// root: ElementId, /// How many nodes should be popped from the stack to insert before the target node.
n: u32,
// /// How many nodes should be popped from the stack to insert after the target node. },
// n: u32,
// }, /// Remove a particular node from the DOM
Remove {
// /// Insert a number of nodes before a given node. /// The ID of the node to remove.
// InsertBefore { root: ElementId,
// /// The ID of the node to insert before. },
// root: ElementId,
/// Create a new purely-text node
// /// How many nodes should be popped from the stack to insert before the target node. CreateTextNode {
// n: u32, /// The ID the new node should have.
// }, root: ElementId,
// /// Remove a particular node from the DOM /// The textcontent of the node
// Remove { text: &'bump str,
// /// The ID of the node to remove. },
// root: ElementId,
// }, /// Create a new purely-element node
CreateElement {
// /// Create a new purely-text node /// The ID the new node should have.
// CreateTextNode { root: ElementId,
// /// The ID the new node should have.
// root: ElementId, /// The tagname of the node
tag: &'bump str,
// /// The textcontent of the node },
// text: &'bump str,
// }, /// Create a new purely-comment node with a given namespace
CreateElementNs {
// /// Create a new purely-element node /// The ID the new node should have.
// CreateElement { root: ElementId,
// /// The ID the new node should have.
// root: ElementId, /// The namespace of the node
tag: &'bump str,
// /// The tagname of the node
// tag: &'bump str, /// The namespace of the node (like `SVG`)
// }, ns: &'static str,
},
// /// Create a new purely-comment node with a given namespace
// CreateElementNs { /// Create a new placeholder node.
// /// The ID the new node should have. /// In most implementations, this will either be a hidden div or a comment node.
// root: ElementId, CreatePlaceholder {
/// The ID the new node should have.
// /// The namespace of the node root: ElementId,
// tag: &'bump str, },
// /// The namespace of the node (like `SVG`) /// Create a new Event Listener.
// ns: &'static str, NewEventListener {
// }, /// The name of the event to listen for.
event_name: &'static str,
// /// Create a new placeholder node.
// /// In most implementations, this will either be a hidden div or a comment node. /// The ID of the node to attach the listener to.
// CreatePlaceholder { scope: ScopeId,
// /// The ID the new node should have.
// root: ElementId, /// The ID of the node to attach the listener to.
// }, root: ElementId,
},
// /// Create a new Event Listener.
// NewEventListener { /// Remove an existing Event Listener.
// /// The name of the event to listen for. RemoveEventListener {
// event_name: &'static str, /// The ID of the node to remove.
root: ElementId,
// /// The ID of the node to attach the listener to.
// scope: ScopeId, /// The name of the event to remove.
event: &'static str,
// /// The ID of the node to attach the listener to. },
// root: ElementId,
// }, /// Set the textcontent of a node.
SetText {
// /// Remove an existing Event Listener. /// The ID of the node to set the textcontent of.
// RemoveEventListener { root: ElementId,
// /// The ID of the node to remove.
// root: ElementId, /// The textcontent of the node
text: &'bump str,
// /// The name of the event to remove. },
// event: &'static str,
// }, /// Set the value of a node's attribute.
SetAttribute {
// /// Set the textcontent of a node. /// The ID of the node to set the attribute of.
// SetText { root: ElementId,
// /// The ID of the node to set the textcontent of.
// root: ElementId, /// The name of the attribute to set.
field: &'static str,
// /// The textcontent of the node
// text: &'bump str, /// The value of the attribute.
// }, value: AttributeValue<'bump>,
// /// Set the value of a node's attribute. // value: &'bump str,
// SetAttribute { /// The (optional) namespace of the attribute.
// /// The ID of the node to set the attribute of. /// For instance, "style" is in the "style" namespace.
// root: ElementId, ns: Option<&'bump str>,
},
// /// The name of the attribute to set.
// field: &'static str, /// Remove an attribute from a node.
RemoveAttribute {
// /// The value of the attribute. /// The ID of the node to remove.
// value: AttributeValue<'bump>, root: ElementId,
// // value: &'bump str, /// The name of the attribute to remove.
// /// The (optional) namespace of the attribute. name: &'static str,
// /// For instance, "style" is in the "style" namespace.
// ns: Option<&'bump str>, /// The namespace of the attribute.
// }, ns: Option<&'bump str>,
},
// /// Remove an attribute from a node.
// RemoveAttribute { /// Manually pop a root node from the stack.
// /// The ID of the node to remove. PopRoot {
// root: ElementId, /// The amount of nodes to pop
count: u32,
// /// The name of the attribute to remove. },
// name: &'static str,
/// Remove all the children of an element
// /// The namespace of the attribute. RemoveChildren {
// ns: Option<&'bump str>, /// The root
// }, root: ElementId,
},
// /// Manually pop a root node from the stack.
// PopRoot { /*
// /// The amount of nodes to pop
// count: u32, Template stuff
// },
- load into scratch space
// /// Remove all the children of an element - dump nodes into stack
// RemoveChildren { - assign ids of nodes in template
// /// The root
// root: ElementId, */
// }, /// Create a template using the nodes on the stack
Save {
// /// Create a template using the nodes on the stack /// The ID of the template
// StoreTemplate { name: &'static str,
// /// The ID of the template
// name: &'static str, /// The amount of nodes to pop from the stack into the template
num_children: u32,
// /// The amount of nodes to pop from the stack into the template },
// num_children: u32,
/// Load the template into a scratch space on the stack
// /// Indicies for the nodes to pop from the stack into the template ///
// dynamic_nodes: &'static [&'static [u32]], /// The template body now lives on the stack, but needs to be finished before its nodes can be appended to the DOM.
// }, Load {
/// The ID of the template
// /// Load the template onto the stack name: &'static str,
// LoadTemplate {
// /// The ID of the template id: u32,
// name: &'static str, },
// /// The index of the template body AssignId {
// index: u32, index: &'static str,
id: ElementId,
// /// Give the node a new ID to remove it later },
// root: ElementId,
// }, ReplaceDescendant {
index: &'static str,
// /// Load n nodes into the kth dynamic node of the template m: u32,
// /// },
// /// Assumes that the template is already on the stack }
// MergeTemplate {
// /// The index of the dynamic node to merge into use DomEdit::*;
// index: u32,
impl<'a> dioxus_core::Renderer<'a> for Mutations<'a> {
// /// The amount of nodes to pop from the stack into the template // Navigation
// num_children: u32, fn push_root(&mut self, root: ElementId) {
// }, self.edits.push(PushRoot { root });
// } }
// use DomEdit::*; // Navigation
fn pop_root(&mut self) {
// impl<'a> Mutations<'a> { self.edits.push(PopRoot { count: 1 });
// pub(crate) fn new() -> Self { }
// Self {
// edits: Vec::new(), fn replace_with(&mut self, root: ElementId, m: u32) {
// refs: Vec::new(), self.edits.push(ReplaceWith { m, root });
// dirty_scopes: Default::default(), }
// }
// } fn replace_descendant(&mut self, descendent: &'static [u8], m: u32) {
self.edits.push(ReplaceDescendant {
// // Navigation // serializing is just hijacking ascii
// pub(crate) fn push_root(&mut self, root: ElementId) { index: unsafe { std::str::from_utf8_unchecked(descendent) },
// self.edits.push(PushRoot { root }); m,
// } });
}
// // Navigation
// pub(crate) fn pop_root(&mut self) { fn insert_after(&mut self, root: ElementId, n: u32) {
// self.edits.push(PopRoot { count: 1 }); self.edits.push(InsertAfter { n, root });
// } }
// pub(crate) fn replace_with(&mut self, root: ElementId, m: u32) { fn insert_before(&mut self, root: ElementId, n: u32) {
// self.edits.push(ReplaceWith { m, root }); self.edits.push(InsertBefore { n, root });
// } }
// pub(crate) fn insert_after(&mut self, root: ElementId, n: u32) { fn append_children(&mut self, n: u32) {
// self.edits.push(InsertAfter { n, root }); self.edits.push(AppendChildren { many: n });
// } }
// pub(crate) fn insert_before(&mut self, root: ElementId, n: u32) { // Create
// self.edits.push(InsertBefore { n, root }); fn create_text_node(&mut self, text: &'a str, root: ElementId) {
// } self.edits.push(CreateTextNode { text, root });
}
// pub(crate) fn append_children(&mut self, n: u32) {
// self.edits.push(AppendChildren { many: n }); fn create_element(&mut self, tag: &'static str, ns: Option<&'static str>, id: ElementId) {
// } match ns {
Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
// // Remove Nodes from the dom None => self.edits.push(CreateElement { root: id, tag }),
// pub(crate) fn remove(&mut self, root: ElementId) { }
// self.edits.push(Remove { root }); }
// }
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
// // Create fn create_placeholder(&mut self, id: ElementId) {
// pub(crate) fn create_text_node(&mut self, text: &'a str, root: ElementId) { self.edits.push(CreatePlaceholder { root: id });
// self.edits.push(CreateTextNode { text, root }); }
// }
fn assign_id(&mut self, descendent: &'static [u8], id: ElementId) {
// pub(crate) fn create_element( self.edits.push(AssignId {
// &mut self, index: unsafe { std::str::from_utf8_unchecked(descendent) },
// tag: &'static str, id,
// ns: Option<&'static str>, });
// id: ElementId, }
// ) {
// match ns { // Remove Nodes from the dom
// Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }), fn remove(&mut self, root: ElementId) {
// None => self.edits.push(CreateElement { root: id, tag }), self.edits.push(Remove { root });
// } }
// }
// // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId) {
// pub(crate) fn create_placeholder(&mut self, id: ElementId) { self.edits.push(RemoveAttribute {
// self.edits.push(CreatePlaceholder { root: id }); name: attribute.name,
// } ns: attribute.namespace,
root,
// // events });
// pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) { }
// let Listener {
// event, // events
// mounted_node, fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
// .. let Listener {
// } = listener; event,
mounted_node,
// let element_id = mounted_node.get().unwrap(); ..
} = listener;
// self.edits.push(NewEventListener {
// scope, let element_id = mounted_node.get();
// event_name: event,
// root: element_id, self.edits.push(NewEventListener {
// }); scope,
// } event_name: event,
// pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: ElementId) { root: element_id,
// self.edits.push(RemoveEventListener { event, root }); });
// } }
// // modify fn remove_event_listener(&mut self, event: &'static str, root: ElementId) {
// pub(crate) fn set_text(&mut self, text: &'a str, root: ElementId) { self.edits.push(RemoveEventListener { event, root });
// self.edits.push(SetText { text, root }); }
// }
// modify
// pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: ElementId) { fn set_text(&mut self, text: &'a str, root: ElementId) {
// let Attribute { self.edits.push(SetText { text, root });
// name, }
// value,
// namespace, fn save(&mut self, id: &'static str, num: u32) {
// .. self.edits.push(Save {
// } = attribute; name: id,
num_children: num,
// self.edits.push(SetAttribute { });
// field: name, }
// value: value.clone(),
// ns: *namespace, fn load(&mut self, id: &'static str, index: u32) {
// root, self.edits.push(Load {
// }); name: id,
// } id: index,
});
// pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId) { }
// let Attribute {
// name, namespace, .. fn mark_dirty_scope(&mut self, scope: ScopeId) {
// } = attribute; self.dirty_scopes.push(scope);
}
// self.edits.push(RemoveAttribute {
// name, fn set_attribute(
// ns: *namespace, &mut self,
// root, name: &'static str,
// }); value: AttributeValue<'a>,
// } namespace: Option<&'a str>,
root: ElementId,
// pub(crate) fn mark_dirty_scope(&mut self, scope: ScopeId) { ) {
// self.dirty_scopes.insert(scope); self.edits.push(SetAttribute {
// } field: name,
// } value: value.clone(),
ns: namespace,
// // refs are only assigned once root,
// pub struct NodeRefMutation<'a> { });
// pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>, }
// pub element_id: ElementId,
// } fn remove_children(&mut self, element: ElementId) {
todo!()
// impl<'a> std::fmt::Debug for NodeRefMutation<'a> { }
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { }
// f.debug_struct("NodeRefMutation")
// .field("element_id", &self.element_id)
// .finish()
// }
// }
// impl<'a> NodeRefMutation<'a> {
// pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
// self.element
// .as_ref()
// .and_then(|f| f.get())
// .and_then(|f| f.downcast_ref::<T>())
// }
// pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
// self.element
// .as_mut()
// .and_then(|f| f.get_mut())
// .and_then(|f| f.downcast_mut::<T>())
// }
// }
+3
View File
@@ -10,6 +10,7 @@ macro_rules! no_namespace_trait_methods {
)* )*
) => { ) => {
$( $(
$(#[$attr])*
const $name: AttributeDiscription = ( const $name: AttributeDiscription = (
stringify!($name), stringify!($name),
None, None,
@@ -26,6 +27,7 @@ macro_rules! style_trait_methods {
)* )*
) => { ) => {
$( $(
$(#[$attr])*
const $name: AttributeDiscription = ( const $name: AttributeDiscription = (
$lit, $lit,
Some("style"), Some("style"),
@@ -42,6 +44,7 @@ macro_rules! aria_trait_methods {
)* )*
) => { ) => {
$( $(
$(#[$attr])*
const $name: AttributeDiscription = ( const $name: AttributeDiscription = (
$lit, $lit,
None, None,
+29 -27
View File
@@ -83,41 +83,39 @@ impl ToTokens for CallBody {
fn render_static_node<'a>(root: &'a BodyNode, cx: &mut DynamicContext<'a>) -> TokenStream2 { fn render_static_node<'a>(root: &'a BodyNode, cx: &mut DynamicContext<'a>) -> TokenStream2 {
match root { match root {
BodyNode::Element(el) => { BodyNode::Element(el) => {
let name = &el.name; let el_name = &el.name;
let children = { let children = {
let children = el.children.iter().map(|root| render_static_node(root, cx)); let children = el.children.iter().map(|root| render_static_node(root, cx));
quote! { #(#children),* } quote! { #(#children),* }
}; };
let attrs = el.attributes.iter().map(|attr| { let attrs = el.attributes.iter().filter_map(|attr| {
// //
match &attr.attr { match &attr.attr {
ElementAttr::AttrText { name, value } if value.is_static() => { ElementAttr::AttrText { name, value } if value.is_static() => {
let value = value.source.as_ref().unwrap(); let value = value.source.as_ref().unwrap();
quote! { Some(quote! {
::dioxus::core::TemplateAttribute::Static(::dioxus::core::Attribute { ::dioxus::core::TemplateAttribute {
name: stringify!(#name), name: dioxus_elements::#el_name::#name.0,
namespace: None, namespace: dioxus_elements::#el_name::#name.1,
volatile: false, volatile: dioxus_elements::#el_name::#name.2,
mounted_node: Default::default(), value: #value,
value: ::dioxus::core::AttributeValue::Text(#value), }
}) })
}
} }
ElementAttr::CustomAttrText { name, value } if value.is_static() => { ElementAttr::CustomAttrText { name, value } if value.is_static() => {
let value = value.source.as_ref().unwrap(); let value = value.source.as_ref().unwrap();
quote! { Some(quote! {
::dioxus::core::TemplateAttribute::Static(::dioxus::core::Attribute { ::dioxus::core::TemplateAttribute {
name: stringify!(#name), name: dioxus_elements::#el_name::#name.0,
namespace: None, namespace: dioxus_elements::#el_name::#name.1,
volatile: false, volatile: dioxus_elements::#el_name::#name.2,
mounted_node: Default::default(), value: #value,
value: ::dioxus::core::AttributeValue::Text(#value), }
}) })
} }
},
ElementAttr::AttrExpression { .. } ElementAttr::AttrExpression { .. }
| ElementAttr::AttrText { .. } | ElementAttr::AttrText { .. }
@@ -125,20 +123,24 @@ impl ToTokens for CallBody {
| ElementAttr::CustomAttrExpression { .. } => { | ElementAttr::CustomAttrExpression { .. } => {
let ct = cx.dynamic_attributes.len(); let ct = cx.dynamic_attributes.len();
cx.dynamic_attributes.push(attr); cx.dynamic_attributes.push(attr);
quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) } // quote! {}
None
// quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) }
} }
ElementAttr::EventTokens { .. } => { ElementAttr::EventTokens { .. } => {
let ct = cx.dynamic_listeners.len(); let ct = cx.dynamic_listeners.len();
cx.dynamic_listeners.push(attr); cx.dynamic_listeners.push(attr);
quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) } // quote! {}
None
} }
} }
}); });
quote! { quote! {
::dioxus::core::TemplateNode::Element { ::dioxus::core::TemplateNode::Element {
tag: dioxus_elements::#name::TAG_NAME, tag: dioxus_elements::#el_name::TAG_NAME,
namespace: dioxus_elements::#el_name::NAME_SPACE,
attrs: &[ #(#attrs),* ], attrs: &[ #(#attrs),* ],
children: &[ #children ], children: &[ #children ],
} }
@@ -166,19 +168,19 @@ impl ToTokens for CallBody {
// Render and release the mutable borrow on context // Render and release the mutable borrow on context
let roots = quote! { #( #root_printer ),* }; let roots = quote! { #( #root_printer ),* };
let dyn_printer = &context.dynamic_nodes; let node_printer = &context.dynamic_nodes;
let attr_printer = context.dynamic_attributes.iter(); let attr_printer = context.dynamic_attributes.iter();
let listener_printer = context.dynamic_listeners.iter(); let listener_printer = context.dynamic_listeners.iter();
out_tokens.append_all(quote! { out_tokens.append_all(quote! {
LazyNodes::new(move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode { LazyNodes::new(move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode {
__cx.template_ref( __cx.template_ref(
|| ::dioxus::core::Template { ::dioxus::core::Template {
id: ::dioxus::core::get_line_num!(), id: ::dioxus::core::get_line_num!(),
roots: &[ #roots ] roots: &[ #roots ]
}, },
__cx.bump().alloc([ __cx.bump().alloc([
#( #dyn_printer ),* #( #node_printer ),*
]), ]),
__cx.bump().alloc([ __cx.bump().alloc([
#( #attr_printer ),* #( #attr_printer ),*
+30 -30
View File
@@ -410,36 +410,36 @@ fn render_attributes<'a, 'b: 'a>(
let mut inner_html = None; let mut inner_html = None;
let mut attr_iter = attrs.peekable(); let mut attr_iter = attrs.peekable();
while let Some(attr) = attr_iter.next() { // while let Some(attr) = attr_iter.next() {
match attr.namespace { // match attr.namespace {
None => { // None => {
if attr.name == "dangerous_inner_html" { // if attr.name == "dangerous_inner_html" {
inner_html = Some(attr.value.as_text().unwrap()) // inner_html = Some(attr.value.as_text().unwrap())
} else { // } else {
if is_boolean_attribute(attr.name) && !attr.value.is_truthy() { // if is_boolean_attribute(attr.name) && !attr.value.is_truthy() {
continue; // continue;
} // }
write!(f, " {}=\"{}\"", attr.name, attr.value)? // write!(f, " {}=\"{}\"", attr.name, attr.value)?
} // }
} // }
Some(ns) => { // Some(ns) => {
// write the opening tag // // write the opening tag
write!(f, " {}=\"", ns)?; // write!(f, " {}=\"", ns)?;
let mut cur_ns_el = attr; // let mut cur_ns_el = attr;
loop { // loop {
write!(f, "{}:{};", cur_ns_el.name, cur_ns_el.value)?; // write!(f, "{}:{};", cur_ns_el.name, cur_ns_el.value)?;
match attr_iter.peek() { // match attr_iter.peek() {
Some(next_attr) if next_attr.namespace == Some(ns) => { // Some(next_attr) if next_attr.namespace == Some(ns) => {
cur_ns_el = attr_iter.next().unwrap(); // cur_ns_el = attr_iter.next().unwrap();
} // }
_ => break, // _ => break,
} // }
} // }
// write the closing tag // // write the closing tag
write!(f, "\"")?; // write!(f, "\"")?;
} // }
} // }
} // }
Ok(inner_html) Ok(inner_html)
} }