wip: more work on diffing
This commit is contained in:
1 parent
f4fb5bb454
commit
21685fbb16
4 files changed
+40
-50
No files matched your search
+21
-24
@@ -30,22 +30,20 @@ use fxhash::{FxHashMap, FxHashSet};
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::Cell,
|
|
||||||
cmp::Ordering,
|
|
||||||
marker::PhantomData,
|
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The accompanying "real dom" exposes an imperative API for controlling the UI layout
|
/// The accompanying "real dom" exposes an imperative API for controlling the UI layout
|
||||||
///
|
///
|
||||||
/// Instead of having handles directly over nodes, Dioxus uses simple u32s as node IDs.
|
/// Instead of having handles directly over nodes, Dioxus uses simple u64s as node IDs.
|
||||||
/// This allows layouts with up to 4,294,967,295 nodes. If we use nohasher, then retrieving is very fast.
|
/// The expectation is that the underlying renderer will mainain their Nodes in something like slotmap or an ECS memory
|
||||||
|
/// where indexing is very fast. For reference, the slotmap in the WebSys renderer takes about 3ns to randomly access any
|
||||||
/// The "RealDom" abstracts over the... real dom. Elements are mapped by ID. The RealDom is inteded to maintain a stack
|
/// node.
|
||||||
/// of real nodes as the diffing algorithm descenes through the tree. This means that whatever is on top of the stack
|
///
|
||||||
/// will receive modifications. However, instead of using child-based methods for descending through the tree, we instead
|
/// The "RealDom" abstracts over the... real dom. The RealDom trait assumes that the renderer maintains a stack of real
|
||||||
/// ask the RealDom to either push or pop real nodes onto the stack. This saves us the indexing cost while working on a
|
/// nodes as the diffing algorithm descenes through the tree. This means that whatever is on top of the stack will receive
|
||||||
/// single node
|
/// any modifications that follow. This technique enables the diffing algorithm to avoid directly handling or storing any
|
||||||
|
/// target-specific Node type as well as easily serializing the edits to be sent over a network or IPC connection.
|
||||||
pub trait RealDom<'a> {
|
pub trait RealDom<'a> {
|
||||||
// Navigation
|
// Navigation
|
||||||
fn push_root(&mut self, root: RealDomNode);
|
fn push_root(&mut self, root: RealDomNode);
|
||||||
@@ -60,8 +58,8 @@ pub trait RealDom<'a> {
|
|||||||
|
|
||||||
// Create
|
// Create
|
||||||
fn create_text_node(&mut self, text: &'a str) -> RealDomNode;
|
fn create_text_node(&mut self, text: &'a str) -> RealDomNode;
|
||||||
fn create_element(&mut self, tag: &'static str) -> RealDomNode;
|
fn create_element(&mut self, tag: &'static str, ns: Option<&'static str>) -> RealDomNode;
|
||||||
fn create_element_ns(&mut self, tag: &'static str, namespace: &'static str) -> RealDomNode;
|
|
||||||
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
||||||
fn create_placeholder(&mut self) -> RealDomNode;
|
fn create_placeholder(&mut self) -> RealDomNode;
|
||||||
|
|
||||||
@@ -73,12 +71,11 @@ pub trait RealDom<'a> {
|
|||||||
element_id: usize,
|
element_id: usize,
|
||||||
realnode: RealDomNode,
|
realnode: RealDomNode,
|
||||||
);
|
);
|
||||||
// fn new_event_listener(&mut self, event: &str);
|
|
||||||
fn remove_event_listener(&mut self, event: &'static str);
|
fn remove_event_listener(&mut self, event: &'static str);
|
||||||
|
|
||||||
// modify
|
// modify
|
||||||
fn set_text(&mut self, text: &'a str);
|
fn set_text(&mut self, text: &'a str);
|
||||||
fn set_attribute(&mut self, name: &'static str, value: &'a str, is_namespaced: bool);
|
fn set_attribute(&mut self, name: &'static str, value: &'a str, ns: Option<&'a str>);
|
||||||
fn remove_attribute(&mut self, name: &'static str);
|
fn remove_attribute(&mut self, name: &'static str);
|
||||||
|
|
||||||
// node ref
|
// node ref
|
||||||
@@ -152,7 +149,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
new.dom_id.set(old.dom_id.get());
|
new.dom_id.set(old.dom_id.get());
|
||||||
|
|
||||||
self.diff_listeners(old.listeners, new.listeners);
|
self.diff_listeners(old.listeners, new.listeners);
|
||||||
self.diff_attr(old.attributes, new.attributes, new.namespace.is_some());
|
self.diff_attr(old.attributes, new.attributes, new.namespace);
|
||||||
self.diff_children(old.children, new.children);
|
self.diff_children(old.children, new.children);
|
||||||
}
|
}
|
||||||
// New node is a text element, need to replace the element with a simple text node
|
// New node is a text element, need to replace the element with a simple text node
|
||||||
@@ -348,9 +345,9 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
} = el;
|
} = el;
|
||||||
// log::info!("Creating {:#?}", node);
|
// log::info!("Creating {:#?}", node);
|
||||||
let real_id = if let Some(namespace) = namespace {
|
let real_id = if let Some(namespace) = namespace {
|
||||||
self.dom.create_element_ns(tag_name, namespace)
|
self.dom.create_element(tag_name, Some(namespace))
|
||||||
} else {
|
} else {
|
||||||
self.dom.create_element(tag_name)
|
self.dom.create_element(tag_name, None)
|
||||||
};
|
};
|
||||||
el.dom_id.set(real_id);
|
el.dom_id.set(real_id);
|
||||||
|
|
||||||
@@ -361,8 +358,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for attr in *attributes {
|
for attr in *attributes {
|
||||||
self.dom
|
self.dom.set_attribute(&attr.name, &attr.value, *namespace);
|
||||||
.set_attribute(&attr.name, &attr.value, namespace.is_some());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast path: if there is a single text child, it is faster to
|
// Fast path: if there is a single text child, it is faster to
|
||||||
@@ -564,7 +560,8 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
old: &'bump [Attribute<'bump>],
|
old: &'bump [Attribute<'bump>],
|
||||||
new: &'bump [Attribute<'bump>],
|
new: &'bump [Attribute<'bump>],
|
||||||
is_namespaced: bool,
|
namespace: Option<&'bump str>,
|
||||||
|
// is_namespaced: bool,
|
||||||
) {
|
) {
|
||||||
// Do O(n^2) passes to add/update and remove attributes, since
|
// Do O(n^2) passes to add/update and remove attributes, since
|
||||||
// there are almost always very few attributes.
|
// there are almost always very few attributes.
|
||||||
@@ -575,14 +572,14 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
|||||||
if new_attr.is_volatile() {
|
if new_attr.is_volatile() {
|
||||||
// self.dom.commit_traversal();
|
// self.dom.commit_traversal();
|
||||||
self.dom
|
self.dom
|
||||||
.set_attribute(new_attr.name, new_attr.value, is_namespaced);
|
.set_attribute(new_attr.name, new_attr.value, namespace);
|
||||||
} else {
|
} else {
|
||||||
for old_attr in old {
|
for old_attr in old {
|
||||||
if old_attr.name == new_attr.name {
|
if old_attr.name == new_attr.name {
|
||||||
if old_attr.value != new_attr.value {
|
if old_attr.value != new_attr.value {
|
||||||
// self.dom.commit_traversal();
|
// self.dom.commit_traversal();
|
||||||
self.dom
|
self.dom
|
||||||
.set_attribute(new_attr.name, new_attr.value, is_namespaced);
|
.set_attribute(new_attr.name, new_attr.value, namespace);
|
||||||
}
|
}
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
} else {
|
} else {
|
||||||
@@ -592,7 +589,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
|||||||
|
|
||||||
// self.dom.commit_traversal();
|
// self.dom.commit_traversal();
|
||||||
self.dom
|
self.dom
|
||||||
.set_attribute(new_attr.name, new_attr.value, is_namespaced);
|
.set_attribute(new_attr.name, new_attr.value, namespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,11 @@ impl<'a> RealDom<'a> for DebugDom {
|
|||||||
RealDomNode::new(self.counter)
|
RealDomNode::new(self.counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_element(&mut self, tag: &str) -> RealDomNode {
|
fn create_element(&mut self, tag: &str, ns: Option<&'a str>) -> RealDomNode {
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
RealDomNode::new(self.counter)
|
RealDomNode::new(self.counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode {
|
|
||||||
self.counter += 1;
|
|
||||||
RealDomNode::new(self.counter)
|
|
||||||
}
|
|
||||||
fn create_placeholder(&mut self) -> RealDomNode {
|
fn create_placeholder(&mut self) -> RealDomNode {
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
RealDomNode::new(self.counter)
|
RealDomNode::new(self.counter)
|
||||||
@@ -51,7 +47,7 @@ impl<'a> RealDom<'a> for DebugDom {
|
|||||||
|
|
||||||
fn set_text(&mut self, text: &str) {}
|
fn set_text(&mut self, text: &str) {}
|
||||||
|
|
||||||
fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {}
|
fn set_attribute(&mut self, name: &str, value: &str, namespace: Option<&str>) {}
|
||||||
|
|
||||||
fn remove_attribute(&mut self, name: &str) {}
|
fn remove_attribute(&mut self, name: &str) {}
|
||||||
|
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.inlayHints.enable": false
|
||||||
|
}
|
||||||
+14
-20
@@ -58,11 +58,11 @@ pub enum SerializedDom<'bump> {
|
|||||||
id: u64,
|
id: u64,
|
||||||
},
|
},
|
||||||
NewEventListener {
|
NewEventListener {
|
||||||
|
event: &'bump str,
|
||||||
scope: ScopeIdx,
|
scope: ScopeIdx,
|
||||||
node: u64,
|
node: u64,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
RemoveEventListener {
|
RemoveEventListener {
|
||||||
event: &'bump str,
|
event: &'bump str,
|
||||||
},
|
},
|
||||||
@@ -72,6 +72,7 @@ pub enum SerializedDom<'bump> {
|
|||||||
SetAttribute {
|
SetAttribute {
|
||||||
field: &'bump str,
|
field: &'bump str,
|
||||||
value: &'bump str,
|
value: &'bump str,
|
||||||
|
ns: Option<&'bump str>,
|
||||||
},
|
},
|
||||||
RemoveAttribute {
|
RemoveAttribute {
|
||||||
name: &'bump str,
|
name: &'bump str,
|
||||||
@@ -85,7 +86,7 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn append_child(&mut self) {
|
fn append_child(&mut self) {
|
||||||
self.edits.push(AppendChild {});
|
self.edits.push(AppendChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_with(&mut self) {
|
fn replace_with(&mut self) {
|
||||||
@@ -107,25 +108,17 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_element(&mut self, tag: &'bump str) -> dioxus_core::virtual_dom::RealDomNode {
|
fn create_element(
|
||||||
self.node_counter += 1;
|
|
||||||
let id = RealDomNode::new(self.node_counter);
|
|
||||||
self.edits.push(CreateElement { id: id.0, tag });
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_element_ns(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
tag: &'static str,
|
tag: &'bump str,
|
||||||
namespace: &'static str,
|
ns: Option<&'bump str>,
|
||||||
) -> dioxus_core::virtual_dom::RealDomNode {
|
) -> dioxus_core::virtual_dom::RealDomNode {
|
||||||
self.node_counter += 1;
|
self.node_counter += 1;
|
||||||
let id = RealDomNode::new(self.node_counter);
|
let id = RealDomNode::new(self.node_counter);
|
||||||
self.edits.push(CreateElementNs {
|
match ns {
|
||||||
id: id.0,
|
Some(ns) => self.edits.push(CreateElementNs { id: id.0, ns, tag }),
|
||||||
ns: namespace,
|
None => self.edits.push(CreateElement { id: id.0, tag }),
|
||||||
tag,
|
}
|
||||||
});
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,13 +131,14 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
|||||||
|
|
||||||
fn new_event_listener(
|
fn new_event_listener(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &str,
|
event: &'static str,
|
||||||
scope: dioxus_core::prelude::ScopeIdx,
|
scope: dioxus_core::prelude::ScopeIdx,
|
||||||
element_id: usize,
|
element_id: usize,
|
||||||
realnode: dioxus_core::virtual_dom::RealDomNode,
|
realnode: dioxus_core::virtual_dom::RealDomNode,
|
||||||
) {
|
) {
|
||||||
self.edits.push(NewEventListener {
|
self.edits.push(NewEventListener {
|
||||||
scope,
|
scope,
|
||||||
|
event,
|
||||||
idx: element_id,
|
idx: element_id,
|
||||||
node: realnode.0,
|
node: realnode.0,
|
||||||
});
|
});
|
||||||
@@ -158,8 +152,8 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
|||||||
self.edits.push(SetText { text });
|
self.edits.push(SetText { text });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_attribute(&mut self, field: &'static str, value: &'bump str, is_namespaced: bool) {
|
fn set_attribute(&mut self, field: &'static str, value: &'bump str, ns: Option<&'bump str>) {
|
||||||
self.edits.push(SetAttribute { field, value });
|
self.edits.push(SetAttribute { field, value, ns });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_attribute(&mut self, name: &'static str) {
|
fn remove_attribute(&mut self, name: &'static str) {
|
||||||
|
|||||||
Reference in new issue
Block a user