perf(doc,engine): rebuild a widget by removing its children in one pass (#1967)

This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-11-03 17:58:27 -05:00
1 parent cfb29a4a96
commit fb4d0b5035
3 files changed
+28 -3

No files matched your search

+13
View File
@@ -574,6 +574,19 @@ impl Document {
Ok(())
}
/// Remove every child of `id` with its subtree, in one pass over `id`'s child list (removing
/// them one by one would search that list once per child).
pub fn remove_children(&mut self, id: NodeId) -> DocResult<()> {
let kids = std::mem::take(&mut self.get_mut(id)?.children);
for c in kids {
for d in self.subtree(c) {
self.nodes.remove(&d);
self.dirty.touch(d);
}
}
Ok(())
}
/// Whether sections may go in `parent`: a page or another section (never a frame or a group).
pub fn takes_sections(&self, parent: NodeId) -> bool {
self.node(parent).is_some_and(|p| p.is_page() || matches!(p.kind, NodeKind::Section { .. }))
+14
View File
@@ -846,3 +846,17 @@ fn the_colour_walker_visits_calculation_literals() {
let ColorOperand::Literal { literal } = from else { panic!("literal operand") };
assert_eq!(literal.a, 0.5, "the mix's stored literal was converted like every other colour");
}
#[test]
fn remove_children_removes_every_subtree_and_keeps_the_parent() {
let mut doc = Document::new("T");
let page = doc.first_page().unwrap();
let frame = doc.create(page, NodeKind::Frame, "F", 0.0, 0.0, 100.0, 100.0).unwrap();
let inner = doc.create(frame, NodeKind::Frame, "Inner", 0.0, 0.0, 10.0, 10.0).unwrap();
let leaf = doc.create(inner, NodeKind::Rectangle, "Leaf", 0.0, 0.0, 5.0, 5.0).unwrap();
let other = doc.create(page, NodeKind::Rectangle, "Other", 0.0, 0.0, 5.0, 5.0).unwrap();
doc.remove_children(frame).unwrap();
assert!(doc.contains(frame) && doc.contains(other));
assert!(!doc.contains(inner) && !doc.contains(leaf));
assert!(doc.get(frame).unwrap().children.is_empty());
}
+1 -3
View File
@@ -243,9 +243,7 @@ fn build(s: &mut Session, plugin: &str, w: NodeId, r: &WidgetRender) -> Result<(
if !is_widget(n) || n.plugin_data.get(PLUGIN).map(String::as_str) != Some(plugin) {
return Err(bad(cmd, "the layer is no longer this plugin's widget"));
}
for c in n.children.clone() {
doc.remove(c)?;
}
doc.remove_children(w)?;
crate::cmd::props::apply(cmd, doc, w, &props(&r.root, true))?;
let n = doc.get_mut(w)?;
n.locked = false;