mirror of
https://github.com/danbulant/cushy
synced 2026-06-19 22:41:10 +00:00
Implemented nested widget unmounted events
Closes #138 This implementation works around most of the locking issues that arose the first few times I tried fixing this. Unfortunately it's been just long enough for me to forget how I triggered some catastrophic issues in the past, but all of the current examples that would invoke this behavior continue to work, and some of the side projects that have some weird usages also still work.
This commit is contained in:
parent
0e02a513bb
commit
9c4ae939e1
3 changed files with 64 additions and 29 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WidgetId>,
|
||||
active: Option<WidgetId>,
|
||||
unmounting: bool,
|
||||
unmount_queue: Vec<WidgetId>,
|
||||
}
|
||||
|
||||
impl PendingState<'_> {
|
||||
|
|
|
|||
63
src/tree.rs
63
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<WidgetId>,
|
||||
) {
|
||||
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<WidgetId>,
|
||||
) {
|
||||
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<MountedWidget> {
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue