From 29742936822dbf766c063247f8f235f9d35f936e Mon Sep 17 00:00:00 2001 From: TheRawMeatball Date: Wed, 6 Oct 2021 19:00:36 +0000 Subject: [PATCH] Add ControlNode for UI (#2908) This PR adds a ControlNode which 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(s). --- crates/bevy_ui/src/entity.rs | 11 +++- crates/bevy_ui/src/flex/mod.rs | 96 +++++++++++++++++++++++++++++----- crates/bevy_ui/src/ui_node.rs | 10 +++- 3 files changed, 102 insertions(+), 15 deletions(-) diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index d7312b4baf10a..7957a4f0adea6 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -2,7 +2,7 @@ use super::Node; use crate::{ render::UI_PIPELINE_HANDLE, widget::{Button, Image}, - CalculatedSize, FocusPolicy, Interaction, Style, + CalculatedSize, ControlNode, FocusPolicy, Interaction, Style, }; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; @@ -17,6 +17,15 @@ use bevy_sprite::{ColorMaterial, QUAD_HANDLE}; use bevy_text::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)] pub struct NodeBundle { pub node: Node, diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 0bb7a573d302a..1e6a2a7b92860 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/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/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index be196e8b44e81..5e70117e684cf 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -1,4 +1,4 @@ -use bevy_ecs::{component::Component, reflect::ReflectComponent}; +use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_math::{Rect, Size, Vec2}; use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_render::renderer::RenderResources; @@ -11,6 +11,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 {