diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index 7c39664d9f..e6eb7c77c1 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -20,6 +20,7 @@ bevy_core = { path = "../bevy_core", version = "0.4.0" } bevy_derive = { path = "../bevy_derive", version = "0.4.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" } bevy_input = { path = "../bevy_input", version = "0.4.0" } +bevy_log = { path = "../bevy_log", version = "0.4.0" } bevy_math = { path = "../bevy_math", version = "0.4.0" } bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] } bevy_render = { path = "../bevy_render", version = "0.4.0" } diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 10720ddc8a..382fde6e4c 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -2,7 +2,8 @@ mod convert; use crate::{Node, Style}; use bevy_app::{EventReader, Events}; -use bevy_ecs::{Changed, Entity, Local, Query, QueryFilter, Res, ResMut, With, Without}; +use bevy_ecs::{Changed, Entity, Flags, Local, Query, QueryFilter, Res, ResMut, With, Without}; +use bevy_log::warn; use bevy_math::Vec2; use bevy_text::CalculatedSize; use bevy_transform::prelude::{Children, Parent, Transform}; @@ -98,8 +99,14 @@ impl FlexSurface { pub fn update_children(&mut self, entity: Entity, children: &Children) { let mut stretch_children = Vec::with_capacity(children.len()); for child in children.iter() { - let stretch_node = self.entity_to_stretch.get(child).unwrap(); - stretch_children.push(*stretch_node); + if let Some(stretch_node) = self.entity_to_stretch.get(child) { + stretch_children.push(*stretch_node); + } else { + warn!( + "Unstyled child in a UI entity hierarchy. You are using an entity \ +without UI components as a child of an entity with UI components, results may be unexpected." + ); + } } let stretch_node = self.entity_to_stretch.get(&entity).unwrap(); @@ -152,12 +159,27 @@ impl FlexSurface { } } - pub fn get_layout(&self, entity: Entity) -> Result<&stretch::result::Layout, stretch::Error> { - let stretch_node = self.entity_to_stretch.get(&entity).unwrap(); - self.stretch.layout(*stretch_node) + pub fn get_layout(&self, entity: Entity) -> Result<&stretch::result::Layout, FlexError> { + if let Some(stretch_node) = self.entity_to_stretch.get(&entity) { + self.stretch + .layout(*stretch_node) + .map_err(FlexError::StretchError) + } else { + warn!( + "Styled child in a non-UI entity hierarchy. You are using an entity \ +with UI components as a child of an entity without UI components, results may be unexpected." + ); + Err(FlexError::InvalidHierarchy) + } } } +#[derive(Debug)] +pub enum FlexError { + InvalidHierarchy, + StretchError(stretch::Error), +} + // SAFE: as long as MeasureFunc is Send + Sync. https://github.com/vislyhq/stretch/issues/69 unsafe impl Send for FlexSurface {} unsafe impl Sync for FlexSurface {} @@ -176,7 +198,14 @@ pub fn flex_node_system( (With, Changed), >, children_query: Query<(Entity, &Children), (With, Changed)>, - mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>, + mut node_transform_query: Query<( + Entity, + &mut Node, + &mut Transform, + Option<&Parent>, + Flags, + Flags, + )>, ) { // update window root nodes for window in windows.iter() { @@ -241,7 +270,9 @@ pub fn flex_node_system( let to_logical = |v| (physical_to_logical_factor * v as f64) as f32; - for (entity, mut node, mut transform, parent) in node_transform_query.iter_mut() { + for (entity, mut node, mut transform, parent, parent_flags, transform_flags) in + node_transform_query.iter_mut() + { let layout = flex_surface.get_layout(entity).unwrap(); node.size = Vec2::new( to_logical(layout.size.width), @@ -250,10 +281,12 @@ pub fn flex_node_system( let position = &mut transform.translation; position.x = to_logical(layout.location.x + layout.size.width / 2.0); position.y = to_logical(layout.location.y + layout.size.height / 2.0); - if let Some(parent) = parent { - if let Ok(parent_layout) = flex_surface.get_layout(parent.0) { - position.x -= to_logical(parent_layout.size.width / 2.0); - position.y -= to_logical(parent_layout.size.height / 2.0); + if parent_flags.changed() || transform_flags.changed() { + if let Some(parent) = parent { + if let Ok(parent_layout) = flex_surface.get_layout(parent.0) { + position.x -= to_logical(parent_layout.size.width / 2.0); + position.y -= to_logical(parent_layout.size.height / 2.0); + } } } }