@@ -7,17 +7,16 @@ use crate::{
7
7
use bevy_ecs:: {
8
8
change_detection:: { DetectChanges , DetectChangesMut } ,
9
9
entity:: Entity ,
10
- hierarchy:: { ChildOf , Children } ,
10
+ hierarchy:: Children ,
11
11
lifecycle:: RemovedComponents ,
12
- query:: With ,
12
+ query:: Added ,
13
13
system:: { Query , ResMut } ,
14
14
world:: Ref ,
15
15
} ;
16
16
17
17
use bevy_math:: { Affine2 , Vec2 } ;
18
18
use bevy_sprite:: BorderRect ;
19
19
use thiserror:: Error ;
20
- use tracing:: warn;
21
20
use ui_surface:: UiSurface ;
22
21
23
22
use bevy_text:: ComputedTextBlock ;
@@ -73,14 +72,14 @@ pub enum LayoutError {
73
72
pub fn ui_layout_system (
74
73
mut ui_surface : ResMut < UiSurface > ,
75
74
ui_root_node_query : UiRootNodes ,
75
+ ui_children : UiChildren ,
76
76
mut node_query : Query < (
77
77
Entity ,
78
78
Ref < Node > ,
79
79
Option < & mut ContentSize > ,
80
80
Ref < ComputedNodeTarget > ,
81
81
) > ,
82
- computed_node_query : Query < ( Entity , Ref < Node > , Option < Ref < ChildOf > > ) , With < ComputedNode > > ,
83
- ui_children : UiChildren ,
82
+ added_node_query : Query < ( ) , Added < Node > > ,
84
83
mut node_update_query : Query < (
85
84
& mut ComputedNode ,
86
85
& UiTransform ,
@@ -126,41 +125,42 @@ pub fn ui_layout_system(
126
125
ui_surface. try_remove_children ( entity) ;
127
126
}
128
127
129
- computed_node_query
130
- . iter ( )
131
- . for_each ( |( entity, node, maybe_child_of) | {
132
- if let Some ( child_of) = maybe_child_of {
133
- // Note: This does not cover the case where a parent's Node component was removed.
134
- // Users are responsible for fixing hierarchies if they do that (it is not recommended).
135
- // Detecting it here would be a permanent perf burden on the hot path.
136
- if child_of. is_changed ( ) && !ui_children. is_ui_node ( child_of. parent ( ) ) {
137
- warn ! (
138
- "Node ({entity}) is in a non-UI entity hierarchy. You are using an entity \
139
- with UI components as a child of an entity without UI components, your UI layout may be broken."
140
- ) ;
141
- }
142
- }
143
-
144
- if node. is_added ( ) || ui_children. is_changed ( entity) {
145
- ui_surface. update_children ( entity, ui_children. iter_ui_children ( entity) ) ;
146
- }
147
- } ) ;
148
-
149
128
// clean up removed nodes after syncing children to avoid potential panic (invalid SlotMap key used)
150
129
ui_surface. remove_entities (
151
130
removed_nodes
152
131
. read ( )
153
132
. filter ( |entity| !node_query. contains ( * entity) ) ,
154
133
) ;
155
134
156
- // Re-sync changed children: avoid layout glitches caused by removed nodes that are still set as a child of another node
157
- computed_node_query. iter ( ) . for_each ( |( entity, node, _) | {
158
- if node. is_added ( ) || ui_children. is_changed ( entity) {
159
- ui_surface. update_children ( entity, ui_children. iter_ui_children ( entity) ) ;
135
+ for ui_root_entity in ui_root_node_query. iter ( ) {
136
+ fn update_children_recursively (
137
+ ui_surface : & mut UiSurface ,
138
+ ui_children : & UiChildren ,
139
+ added_node_query : & Query < ( ) , Added < Node > > ,
140
+ entity : Entity ,
141
+ ) {
142
+ if ui_surface. entity_to_taffy . contains_key ( & entity)
143
+ && ( added_node_query. contains ( entity)
144
+ || ui_children. is_changed ( entity)
145
+ || ui_children
146
+ . iter_ui_children ( entity)
147
+ . any ( |child| added_node_query. contains ( child) ) )
148
+ {
149
+ ui_surface. update_children ( entity, ui_children. iter_ui_children ( entity) ) ;
150
+ }
151
+
152
+ for child in ui_children. iter_ui_children ( entity) {
153
+ update_children_recursively ( ui_surface, ui_children, added_node_query, child) ;
154
+ }
160
155
}
161
- } ) ;
162
156
163
- for ui_root_entity in ui_root_node_query. iter ( ) {
157
+ update_children_recursively (
158
+ & mut ui_surface,
159
+ & ui_children,
160
+ & added_node_query,
161
+ ui_root_entity,
162
+ ) ;
163
+
164
164
let ( _, _, _, computed_target) = node_query. get ( ui_root_entity) . unwrap ( ) ;
165
165
166
166
ui_surface. compute_layout (
@@ -670,6 +670,29 @@ mod tests {
670
670
assert_eq ! ( ui_surface. taffy. child_count( taffy_root. id) , 1 ) ;
671
671
}
672
672
673
+ #[ test]
674
+ fn node_addition_should_sync_parent_and_children ( ) {
675
+ let ( mut world, mut ui_schedule) = setup_ui_test_world ( ) ;
676
+
677
+ let d = world. spawn ( Node :: default ( ) ) . id ( ) ;
678
+ let c = world. spawn ( ( ) ) . add_child ( d) . id ( ) ;
679
+ let b = world. spawn ( Node :: default ( ) ) . id ( ) ;
680
+ let a = world. spawn ( Node :: default ( ) ) . add_children ( & [ b, c] ) . id ( ) ;
681
+
682
+ ui_schedule. run ( & mut world) ;
683
+
684
+ // fix the invalid middle node by inserting a Node
685
+ world. entity_mut ( c) . insert ( Node :: default ( ) ) ;
686
+
687
+ ui_schedule. run ( & mut world) ;
688
+
689
+ let ui_surface = world. resource :: < UiSurface > ( ) ;
690
+ for ( entity, n) in [ ( a, 2 ) , ( b, 0 ) , ( c, 1 ) , ( d, 0 ) ] {
691
+ let taffy_id = ui_surface. entity_to_taffy [ & entity] . id ;
692
+ assert_eq ! ( ui_surface. taffy. child_count( taffy_id) , n) ;
693
+ }
694
+ }
695
+
673
696
/// regression test for >=0.13.1 root node layouts
674
697
/// ensure root nodes act like they are absolutely positioned
675
698
/// without explicitly declaring it.
0 commit comments