diff --git a/pipelined/bevy_ui2/src/entity.rs b/pipelined/bevy_ui2/src/entity.rs index 1e1f39e228bc72..d05e760f1ef930 100644 --- a/pipelined/bevy_ui2/src/entity.rs +++ b/pipelined/bevy_ui2/src/entity.rs @@ -1,6 +1,7 @@ use crate::{ widget::{Button, ImageMode}, - CalculatedSize, FocusPolicy, Interaction, Node, Style, UiColor, UiImage, CAMERA_UI, + CalculatedSize, ControlNode, FocusPolicy, Interaction, Node, Style, UiColor, UiImage, + CAMERA_UI, }; use bevy_ecs::bundle::Bundle; use bevy_render2::{ @@ -10,6 +11,15 @@ use bevy_render2::{ use bevy_text2::Text; use bevy_transform::prelude::{GlobalTransform, Transform}; +/// If you add this to an entity, it should be the *only* bundle on it from bevy_ui. +/// This bundle will mark the entity as transparent to the UI layout system, meaning the +/// children of this entity will be treated as the children of this entity s parent by the layout system. +pub struct ControlBundle { + pub control_node: ControlNode, + pub transform: Transform, + pub global_transform: GlobalTransform, +} + #[derive(Bundle, Clone, Debug, Default)] pub struct NodeBundle { pub node: Node, diff --git a/pipelined/bevy_ui2/src/flex/mod.rs b/pipelined/bevy_ui2/src/flex/mod.rs index 0bb7a573d302ab..1e6a2a7b92860d 100644 --- a/pipelined/bevy_ui2/src/flex/mod.rs +++ b/pipelined/bevy_ui2/src/flex/mod.rs @@ -1,6 +1,6 @@ mod convert; -use crate::{CalculatedSize, Node, Style}; +use crate::{CalculatedSize, ControlNode, Node, Style}; use bevy_app::EventReader; use bevy_ecs::{ entity::Entity, @@ -111,19 +111,62 @@ impl FlexSurface { } } - pub fn update_children(&mut self, entity: Entity, children: &Children) { + pub fn update_children( + &mut self, + entity: Entity, + children: &Children, + control_node_query: &mut Query<&mut ControlNode>, + unfiltered_children_query: &Query<&Children>, + ) { let mut stretch_children = Vec::with_capacity(children.len()); - for child in children.iter() { - if let Some(stretch_node) = self.entity_to_stretch.get(child) { - stretch_children.push(*stretch_node); + fn inner( + true_parent: Entity, + child: Entity, + control_node_query: &mut Query<&mut ControlNode>, + unfiltered_children_query: &Query<&Children>, + do_on_real: &mut impl FnMut(Entity), + ) { + if let Ok(mut control_node) = control_node_query.get_mut(child) { + control_node.true_parent = Some(true_parent); + for &child in unfiltered_children_query + .get(child) + .ok() + .into_iter() + .map(|c| &**c) + .flatten() + { + inner( + true_parent, + child, + control_node_query, + unfiltered_children_query, + do_on_real, + ); + } } 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." - ); + do_on_real(child); } } + for &child in children.iter() { + inner( + entity, + child, + control_node_query, + unfiltered_children_query, + &mut |e| { + if let Some(stretch_node) = self.entity_to_stretch.get(&e) { + 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(); self.stretch .set_children(*stretch_node, stretch_children) @@ -207,7 +250,10 @@ pub fn flex_node_system( (Entity, &Style, &CalculatedSize), (With, Changed), >, - children_query: Query<(Entity, &Children), (With, Changed)>, + changed_children_query: Query<(Entity, &Children), (With, Changed)>, + unfiltered_children_query: Query<&Children>, + mut control_node_query: Query<&mut ControlNode>, + changed_cnc_query: Query, With)>, mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>, ) { // update window root nodes @@ -262,8 +308,28 @@ pub fn flex_node_system( } // update children - for (entity, children) in children_query.iter() { - flex_surface.update_children(entity, children); + for (entity, children) in changed_children_query.iter() { + flex_surface.update_children( + entity, + children, + &mut control_node_query, + &unfiltered_children_query, + ); + } + + for entity in changed_cnc_query.iter() { + let true_parent = if let Some(e) = control_node_query.get_mut(entity).unwrap().true_parent { + e + } else { + continue; + }; + let children = unfiltered_children_query.get(true_parent).unwrap(); + flex_surface.update_children( + true_parent, + children, + &mut control_node_query, + &unfiltered_children_query, + ); } // compute layouts @@ -284,7 +350,11 @@ pub fn flex_node_system( 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) { + let parent = control_node_query + .get_mut(parent.0) + .map(|cn| cn.true_parent.unwrap()) + .unwrap_or(parent.0); + if let Ok(parent_layout) = flex_surface.get_layout(parent) { position.x -= to_logical(parent_layout.size.width / 2.0); position.y -= to_logical(parent_layout.size.height / 2.0); } diff --git a/pipelined/bevy_ui2/src/ui_node.rs b/pipelined/bevy_ui2/src/ui_node.rs index b03d4f94ea15bb..e8e0d098315917 100644 --- a/pipelined/bevy_ui2/src/ui_node.rs +++ b/pipelined/bevy_ui2/src/ui_node.rs @@ -1,5 +1,5 @@ use bevy_asset::Handle; -use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; +use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_math::{Rect, Size, Vec2}; use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_render2::{color::Color, texture::Image}; @@ -12,6 +12,14 @@ pub struct Node { pub size: Vec2, } +/// If you add this to an entity, it should be the *only* component on it from bevy_ui. +/// This component marks an entity as "transparent" to the UI layout system, meaning the +/// children of this entity will be treated as the children of this entity s parent by the layout system. +#[derive(Clone, Default, Component)] +pub struct ControlNode { + pub(crate) true_parent: Option, +} + #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum Val {