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).
This commit is contained in:
TheRawMeatball 2021-10-06 19:00:36 +00:00
parent 0887f41b58
commit 2974293682
3 changed files with 102 additions and 15 deletions

View file

@ -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,

View file

@ -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,17 +111,60 @@ 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) {
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 {
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."
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();
@ -207,7 +250,10 @@ pub fn flex_node_system(
(Entity, &Style, &CalculatedSize),
(With<Node>, Changed<CalculatedSize>),
>,
children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
changed_children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
unfiltered_children_query: Query<&Children>,
mut control_node_query: Query<&mut ControlNode>,
changed_cnc_query: Query<Entity, (Changed<Children>, With<ControlNode>)>,
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);
}

View file

@ -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<Entity>,
}
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)]
#[reflect_value(PartialEq, Serialize, Deserialize)]
pub enum Val {