use crate::dom::renderer::build::build_children_internal;
use crate::dom::renderer::types::DomRenderer;
use crate::dom::DomId;
use crate::dom::WidgetMeta;
use crate::widget::View;
#[allow(dead_code)]
impl DomRenderer {
pub fn build<V: View>(&mut self, root: &V) {
if self.tree.is_empty() {
self.build_fresh(root);
} else {
self.build_incremental(root);
}
}
pub(crate) fn build_incremental<V: View>(&mut self, root: &V) {
let Some(root_id) = self.tree.root_id() else {
self.build_fresh(root);
return;
};
let new_meta = root.meta();
if !update_node_meta_internal(self, root_id, &new_meta) {
self.build_fresh(root);
return;
}
update_children_internal(self, root_id, root.children());
}
pub(crate) fn update_node_meta(&mut self, node_id: DomId, new_meta: &WidgetMeta) -> bool {
update_node_meta_internal(self, node_id, new_meta)
}
pub(crate) fn update_children(&mut self, parent_id: DomId, new_children: &[Box<dyn View>]) {
update_children_internal(self, parent_id, new_children);
}
pub(crate) fn remove_subtree(&mut self, node_id: DomId) {
let descendants = collect_descendants_internal(&self.tree, node_id);
self.styles.remove(&node_id);
for &id in &descendants {
self.styles.remove(&id);
}
self.tree.remove(node_id);
}
pub(crate) fn collect_descendants(&self, node_id: DomId) -> Vec<DomId> {
collect_descendants_internal(&self.tree, node_id)
}
}
fn update_node_meta_internal(
renderer: &mut DomRenderer,
node_id: DomId,
new_meta: &WidgetMeta,
) -> bool {
let Some(node) = renderer.tree.get(node_id) else {
return false;
};
if node.meta.widget_type != new_meta.widget_type {
return false;
}
if node.meta.id != new_meta.id {
return false;
}
if node.meta.classes != new_meta.classes {
if let Some(node) = renderer.tree.get_mut(node_id) {
node.meta.classes = new_meta.classes.clone();
node.state.dirty = true;
}
renderer.styles.remove(&node_id);
}
true
}
fn update_children_internal(
renderer: &mut DomRenderer,
parent_id: DomId,
new_children: &[Box<dyn View>],
) {
let old_children: Vec<DomId> = renderer
.tree
.get(parent_id)
.map(|n| n.children.clone())
.unwrap_or_default();
let mut old_by_id: std::collections::HashMap<String, DomId> = std::collections::HashMap::new();
for &child_id in &old_children {
if let Some(node) = renderer.tree.get(child_id) {
if let Some(ref id) = node.meta.id {
old_by_id.insert(id.clone(), child_id);
}
}
}
let old_types: Vec<String> = old_children
.iter()
.filter_map(|&id| renderer.tree.get(id).map(|n| n.meta.widget_type.clone()))
.collect();
let mut matched_old: std::collections::HashSet<DomId> = std::collections::HashSet::new();
let mut new_child_ids: Vec<DomId> = Vec::new();
for (pos, child_view) in new_children.iter().enumerate() {
let child_meta = child_view.meta();
let matched_id = if let Some(ref id) = child_meta.id {
old_by_id.get(id).copied()
} else {
old_children.get(pos).and_then(|&old_id| {
if !matched_old.contains(&old_id) {
let old_type = old_types.get(pos)?;
if old_type == &child_meta.widget_type {
Some(old_id)
} else {
None
}
} else {
None
}
})
};
let child_id = if let Some(existing_id) = matched_id {
if !matched_old.contains(&existing_id) {
matched_old.insert(existing_id);
if !update_node_meta_internal(renderer, existing_id, &child_meta) {
renderer.remove_subtree(existing_id);
let new_id = renderer.tree.add_child(parent_id, child_meta);
build_children_internal(renderer, new_id, child_view.children());
new_id
} else {
update_children_internal(renderer, existing_id, child_view.children());
existing_id
}
} else {
let new_id = renderer.tree.add_child(parent_id, child_meta);
build_children_internal(renderer, new_id, child_view.children());
new_id
}
} else {
let new_id = renderer.tree.add_child(parent_id, child_meta);
build_children_internal(renderer, new_id, child_view.children());
new_id
};
new_child_ids.push(child_id);
}
for old_id in old_children {
if !matched_old.contains(&old_id) && !new_child_ids.contains(&old_id) {
renderer.remove_subtree(old_id);
}
}
if let Some(parent) = renderer.tree.get_mut(parent_id) {
parent.children = new_child_ids;
}
}
fn collect_descendants_internal(tree: &crate::dom::DomTree, node_id: DomId) -> Vec<DomId> {
let mut result = Vec::new();
let mut stack = vec![node_id];
while let Some(id) = stack.pop() {
if let Some(node) = tree.get(id) {
for &child_id in &node.children {
result.push(child_id);
stack.push(child_id);
}
}
}
result
}