Fix the double leaf node updates in flex_node_system (#8264)

# Objective

If a UI node has a changed `CalculatedSize` component and either the UI
does a full update or the node also has a changed `Style` component, the
node's corresponding Taffy node will be updated twice by
`flex_node_system`.

## Solution

Add a `Without<Calculated>` query filter so that the two changed node
queries in `flex_node_system` are mutually exclusive and move the
`CalculatedSize` node updater into the else block of the full-update if
conditional.
This commit is contained in:
ickshonpe 2023-04-21 15:23:46 +01:00 committed by GitHub
parent e900bd9e12
commit 0cf3000ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5,7 +5,7 @@ use bevy_ecs::{
change_detection::DetectChanges,
entity::Entity,
event::EventReader,
query::{Changed, ReadOnlyWorldQuery, With, Without},
query::{Changed, Or, With, Without},
removal_detection::RemovedComponents,
system::{Query, Res, ResMut, Resource},
};
@ -244,11 +244,14 @@ pub fn ui_layout_system(
mut resize_events: EventReader<bevy_window::WindowResized>,
mut ui_surface: ResMut<UiSurface>,
root_node_query: Query<Entity, (With<Node>, Without<Parent>)>,
node_query: Query<(Entity, &Style, Option<&CalculatedSize>), (With<Node>, Changed<Style>)>,
full_node_query: Query<(Entity, &Style, Option<&CalculatedSize>), With<Node>>,
changed_style_query: Query<
(Entity, &Style),
(With<Node>, Without<CalculatedSize>, Changed<Style>),
>,
changed_size_query: Query<
(Entity, &Style, &CalculatedSize),
(With<Node>, Changed<CalculatedSize>),
(With<Node>, Or<(Changed<CalculatedSize>, Changed<Style>)>),
>,
children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
mut removed_children: RemovedComponents<Children>,
@ -282,34 +285,28 @@ pub fn ui_layout_system(
}
let scale_factor = logical_to_physical_factor * ui_scale.scale;
let viewport_values = LayoutContext::new(scale_factor, physical_size);
fn update_changed<F: ReadOnlyWorldQuery>(
ui_surface: &mut UiSurface,
viewport_values: &LayoutContext,
query: Query<(Entity, &Style, Option<&CalculatedSize>), F>,
) {
// update changed nodes
for (entity, style, calculated_size) in &query {
// TODO: remove node from old hierarchy if its root has changed
if let Some(calculated_size) = calculated_size {
ui_surface.upsert_leaf(entity, style, calculated_size, viewport_values);
} else {
ui_surface.upsert_node(entity, style, viewport_values);
}
}
}
let layout_context = LayoutContext::new(scale_factor, physical_size);
if !scale_factor_events.is_empty() || ui_scale.is_changed() || resized {
scale_factor_events.clear();
update_changed(&mut ui_surface, &viewport_values, full_node_query);
// update all nodes
for (entity, style, calculated_size) in &full_node_query {
if let Some(calculated_size) = calculated_size {
ui_surface.upsert_leaf(entity, style, calculated_size, &layout_context);
} else {
ui_surface.upsert_node(entity, style, &layout_context);
}
}
} else {
update_changed(&mut ui_surface, &viewport_values, node_query);
}
// update changed nodes without a calculated size
for (entity, style) in changed_style_query.iter() {
ui_surface.upsert_node(entity, style, &layout_context);
}
for (entity, style, calculated_size) in &changed_size_query {
ui_surface.upsert_leaf(entity, style, calculated_size, &viewport_values);
// update changed nodes with a calculated size
for (entity, style, calculated_size) in changed_size_query.iter() {
ui_surface.upsert_leaf(entity, style, calculated_size, &layout_context);
}
}
// clean up removed nodes