Skip to content

Commit 6b75589

Browse files
committed
Fix inconsistent children removal behavior (#6017)
# Objective Fixes #6010 ## Solution As discussed in #6010, this makes it so the `Children` component is removed from the entity whenever all of its children are removed. The behavior is now consistent between all of the commands that may remove children from a parent, and this is tested via two new test functions (one for world functions and one for commands). Documentation was also added to `insert_children`, `push_children`, `add_child` and `remove_children` commands to make this behavior clearer for users. ## Changelog - Fixed `Children` component not getting removed from entity when all its children are moved to a new parent. ## Migration Guide - Queries with `Changed<Children>` will no longer match entities that had all of their children removed using `remove_children`. - `RemovedComponents<Children>` will now contain entities that had all of their children remove using `remove_children`.
1 parent cfba731 commit 6b75589

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

crates/bevy_hierarchy/src/child_builder.rs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,15 @@ fn remove_children(parent: Entity, children: &[Entity], world: &mut World) {
7777
}
7878
push_events(world, events);
7979

80-
if let Some(mut parent_children) = world.get_mut::<Children>(parent) {
80+
let mut parent = world.entity_mut(parent);
81+
if let Some(mut parent_children) = parent.get_mut::<Children>() {
8182
parent_children
8283
.0
8384
.retain(|parent_child| !children.contains(parent_child));
85+
86+
if parent_children.is_empty() {
87+
parent.remove::<Children>();
88+
}
8489
}
8590
}
8691

@@ -258,12 +263,26 @@ pub trait BuildChildren {
258263
/// ```
259264
fn add_children<T>(&mut self, f: impl FnOnce(&mut ChildBuilder) -> T) -> T;
260265
/// Pushes children to the back of the builder's children
266+
///
267+
/// If the children were previously children of another parent, that parent's [`Children`] component
268+
/// will have those children removed from its list. Removing all children from a parent causes its
269+
/// [`Children`] component to be removed from the entity.
261270
fn push_children(&mut self, children: &[Entity]) -> &mut Self;
262271
/// Inserts children at the given index
272+
///
273+
/// If the children were previously children of another parent, that parent's [`Children`] component
274+
/// will have those children removed from its list. Removing all children from a parent causes its
275+
/// [`Children`] component to be removed from the entity.
263276
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self;
264277
/// Removes the given children
278+
///
279+
/// Removing all children from a parent causes its [`Children`] component to be removed from the entity.
265280
fn remove_children(&mut self, children: &[Entity]) -> &mut Self;
266281
/// Adds a single child
282+
///
283+
/// If the children were previously children of another parent, that parent's [`Children`] component
284+
/// will have those children removed from its list. Removing all children from a parent causes its
285+
/// [`Children`] component to be removed from the entity.
267286
fn add_child(&mut self, child: Entity) -> &mut Self;
268287
}
269288

@@ -672,6 +691,96 @@ mod tests {
672691
assert!(world.get::<Parent>(child4).is_none());
673692
}
674693

694+
/// Tests what happens when all children are removed from a parent using world functions
695+
#[test]
696+
fn children_removed_when_empty_world() {
697+
let mut world = World::default();
698+
let entities = world
699+
.spawn_batch(vec![(C(1),), (C(2),), (C(3),)])
700+
.collect::<Vec<Entity>>();
701+
702+
let parent1 = entities[0];
703+
let parent2 = entities[1];
704+
let child = entities[2];
705+
706+
// push child into parent1
707+
world.entity_mut(parent1).push_children(&[child]);
708+
assert_eq!(
709+
world.get::<Children>(parent1).unwrap().0.as_slice(),
710+
&[child]
711+
);
712+
713+
// move only child from parent1 with `push_children`
714+
world.entity_mut(parent2).push_children(&[child]);
715+
assert!(world.get::<Children>(parent1).is_none());
716+
717+
// move only child from parent2 with `insert_children`
718+
world.entity_mut(parent1).insert_children(0, &[child]);
719+
assert!(world.get::<Children>(parent2).is_none());
720+
721+
// remove only child from parent1 with `remove_children`
722+
world.entity_mut(parent1).remove_children(&[child]);
723+
assert!(world.get::<Children>(parent1).is_none());
724+
}
725+
726+
/// Tests what happens when all children are removed form a parent using commands
727+
#[test]
728+
fn children_removed_when_empty_commands() {
729+
let mut world = World::default();
730+
let entities = world
731+
.spawn_batch(vec![(C(1),), (C(2),), (C(3),)])
732+
.collect::<Vec<Entity>>();
733+
734+
let parent1 = entities[0];
735+
let parent2 = entities[1];
736+
let child = entities[2];
737+
738+
let mut queue = CommandQueue::default();
739+
740+
// push child into parent1
741+
{
742+
let mut commands = Commands::new(&mut queue, &world);
743+
commands.entity(parent1).push_children(&[child]);
744+
queue.apply(&mut world);
745+
}
746+
assert_eq!(
747+
world.get::<Children>(parent1).unwrap().0.as_slice(),
748+
&[child]
749+
);
750+
751+
// move only child from parent1 with `push_children`
752+
{
753+
let mut commands = Commands::new(&mut queue, &world);
754+
commands.entity(parent2).push_children(&[child]);
755+
queue.apply(&mut world);
756+
}
757+
assert!(world.get::<Children>(parent1).is_none());
758+
759+
// move only child from parent2 with `insert_children`
760+
{
761+
let mut commands = Commands::new(&mut queue, &world);
762+
commands.entity(parent1).insert_children(0, &[child]);
763+
queue.apply(&mut world);
764+
}
765+
assert!(world.get::<Children>(parent2).is_none());
766+
767+
// move only child from parent1 with `add_child`
768+
{
769+
let mut commands = Commands::new(&mut queue, &world);
770+
commands.entity(parent2).add_child(child);
771+
queue.apply(&mut world);
772+
}
773+
assert!(world.get::<Children>(parent1).is_none());
774+
775+
// remove only child from parent2 with `remove_children`
776+
{
777+
let mut commands = Commands::new(&mut queue, &world);
778+
commands.entity(parent2).remove_children(&[child]);
779+
queue.apply(&mut world);
780+
}
781+
assert!(world.get::<Children>(parent2).is_none());
782+
}
783+
675784
#[test]
676785
fn regression_push_children_same_archetype() {
677786
let mut world = World::new();

crates/bevy_ui/src/flex/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ without UI components as a child of an entity with UI components, results may be
130130
.unwrap();
131131
}
132132

133+
/// Removes children from the entity's taffy node if it exists. Does nothing otherwise.
134+
pub fn try_remove_children(&mut self, entity: Entity) {
135+
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
136+
self.taffy.set_children(*taffy_node, &[]).unwrap();
137+
}
138+
}
139+
133140
pub fn update_window(&mut self, window: &Window) {
134141
let taffy = &mut self.taffy;
135142
let node = self.window_nodes.entry(window.id()).or_insert_with(|| {
@@ -216,6 +223,7 @@ pub fn flex_node_system(
216223
(With<Node>, Changed<CalculatedSize>),
217224
>,
218225
children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
226+
removed_children: RemovedComponents<Children>,
219227
mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>,
220228
removed_nodes: RemovedComponents<Node>,
221229
) {
@@ -262,7 +270,10 @@ pub fn flex_node_system(
262270
flex_surface.set_window_children(primary_window.id(), root_node_query.iter());
263271
}
264272

265-
// update children
273+
// update and remove children
274+
for entity in &removed_children {
275+
flex_surface.try_remove_children(entity);
276+
}
266277
for (entity, children) in &children_query {
267278
flex_surface.update_children(entity, children);
268279
}

0 commit comments

Comments
 (0)