diff --git a/CHANGELOG.md b/CHANGELOG.md index 697ad49..0867e3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 The rendering behavior remains unchanged, and the image will scale correctly within whatever bounds it is given. +- `Widget::unmounted` is now invoked for all widgets in the hierarchy. + Previously, only the parent widget was having its unmounted event invoked. ### Changed diff --git a/src/context.rs b/src/context.rs index 7281075..c0ce13a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -811,11 +811,25 @@ pub trait AsEventContext { /// Removes a widget from the hierarchy. fn remove_child(&mut self, child: &MountedWidget) { let mut context = self.as_event_context(); - child - .lock() - .as_widget() - .unmounted(&mut context.for_other(child)); - context.tree.remove_child(child, &context.current_node); + if context.pending_state.unmounting { + context.pending_state.unmount_queue.push(child.id()); + } else { + context.pending_state.unmounting = true; + context.pending_state.unmount_queue.push(child.id()); + while let Some(to_unmount) = context.pending_state.unmount_queue.pop() { + let Some(mut unmount_context) = context.for_other(&to_unmount) else { + continue; + }; + child.lock().as_widget().unmounted(&mut unmount_context); + unmount_context.widget.tree.remove_child( + child, + &unmount_context.widget.current_node, + &mut unmount_context.widget.pending_state.unmount_queue, + ); + } + + context.pending_state.unmounting = false; + } } } @@ -865,6 +879,8 @@ impl<'context> WidgetContext<'context> { .active_widget() .and_then(|id| tree.widget_from_node(id).map(|w| w.id())), focus_is_advancing: false, + unmount_queue: Vec::new(), + unmounting: false, }), tree, effective_styles: current_node.effective_styles(), @@ -1215,6 +1231,8 @@ struct PendingWidgetState { focus_is_advancing: bool, focus: Option, active: Option, + unmounting: bool, + unmount_queue: Vec, } impl PendingState<'_> { diff --git a/src/tree.rs b/src/tree.rs index 9423e7c..5f4aa78 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -66,9 +66,14 @@ impl Tree { } } - pub fn remove_child(&self, child: &MountedWidget, parent: &MountedWidget) { + pub fn remove_child( + &self, + child: &MountedWidget, + parent: &MountedWidget, + children_to_unmount: &mut Vec, + ) { let mut data = self.data.lock().ignore_poison(); - data.remove_child(child.node_id, parent.node_id); + data.remove_child(child.node_id, parent.node_id, children_to_unmount); if child.widget.is_default() { data.defaults.retain(|id| *id != child.node_id); @@ -479,32 +484,37 @@ impl TreeData { } } - fn remove_child(&mut self, child: LotId, parent: LotId) { - let removed_node = self.nodes.remove(child).expect("widget already removed"); + fn remove_child( + &mut self, + child: LotId, + parent: LotId, + children_to_unmount: &mut Vec, + ) { + let Some(removed_node) = self.nodes.remove(child) else { + return; + }; self.nodes_by_id.remove(&removed_node.widget.id()); - let parent = &mut self.nodes[parent]; - let index = parent - .children - .iter() - .enumerate() - .find_map(|(index, c)| (*c == child).then_some(index)) - .expect("child not found in parent"); - parent.children.remove(index); - let mut detached_nodes = removed_node.children; + if let Some(parent) = self.nodes.get_mut(parent) { + let index = parent + .children + .iter() + .enumerate() + .find_map(|(index, c)| (*c == child).then_some(index)) + .expect("child not found in parent"); + parent.children.remove(index); + } + + children_to_unmount.extend( + removed_node + .children + .into_iter() + .map(|id| self.nodes[id].widget.id()), + ); if let Some(next_focus) = removed_node.widget.next_focus() { self.previous_focuses.remove(&next_focus); } - - while let Some(node) = detached_nodes.pop() { - let mut node = self.nodes.remove(node).expect("detached node missing"); - self.nodes_by_id.remove(&node.widget.id()); - if let Some(next_focus) = node.widget.next_focus() { - self.previous_focuses.remove(&next_focus); - } - detached_nodes.append(&mut node.children); - } } pub(crate) fn widget_hierarchy(&self, mut widget: LotId, tree: &Tree) -> Vec { @@ -540,7 +550,9 @@ impl TreeData { } fn invalidate(&mut self, id: LotId, include_hierarchy: bool) { - let mut node = &mut self.nodes[id]; + let Some(mut node) = self.nodes.get_mut(id) else { + return; + }; loop { node.layout = None; node.last_layout_query = None; @@ -548,7 +560,10 @@ impl TreeData { let (true, Some(parent)) = (include_hierarchy, node.parent) else { break; }; - node = &mut self.nodes[parent]; + let Some(parent_node) = self.nodes.get_mut(parent) else { + break; + }; + node = parent_node; } } }