Skip to content

Commit 2974293

Browse files
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).
1 parent 0887f41 commit 2974293

File tree

3 files changed

+102
-15
lines changed

3 files changed

+102
-15
lines changed

crates/bevy_ui/src/entity.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::Node;
22
use crate::{
33
render::UI_PIPELINE_HANDLE,
44
widget::{Button, Image},
5-
CalculatedSize, FocusPolicy, Interaction, Style,
5+
CalculatedSize, ControlNode, FocusPolicy, Interaction, Style,
66
};
77
use bevy_asset::Handle;
88
use bevy_ecs::bundle::Bundle;
@@ -17,6 +17,15 @@ use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
1717
use bevy_text::Text;
1818
use bevy_transform::prelude::{GlobalTransform, Transform};
1919

20+
/// If you add this to an entity, it should be the *only* bundle on it from bevy_ui.
21+
/// This bundle will mark the entity as transparent to the UI layout system, meaning the
22+
/// children of this entity will be treated as the children of this entity s parent by the layout system.
23+
pub struct ControlBundle {
24+
pub control_node: ControlNode,
25+
pub transform: Transform,
26+
pub global_transform: GlobalTransform,
27+
}
28+
2029
#[derive(Bundle, Clone, Debug)]
2130
pub struct NodeBundle {
2231
pub node: Node,

crates/bevy_ui/src/flex/mod.rs

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod convert;
22

3-
use crate::{CalculatedSize, Node, Style};
3+
use crate::{CalculatedSize, ControlNode, Node, Style};
44
use bevy_app::EventReader;
55
use bevy_ecs::{
66
entity::Entity,
@@ -111,19 +111,62 @@ impl FlexSurface {
111111
}
112112
}
113113

114-
pub fn update_children(&mut self, entity: Entity, children: &Children) {
114+
pub fn update_children(
115+
&mut self,
116+
entity: Entity,
117+
children: &Children,
118+
control_node_query: &mut Query<&mut ControlNode>,
119+
unfiltered_children_query: &Query<&Children>,
120+
) {
115121
let mut stretch_children = Vec::with_capacity(children.len());
116-
for child in children.iter() {
117-
if let Some(stretch_node) = self.entity_to_stretch.get(child) {
118-
stretch_children.push(*stretch_node);
122+
fn inner(
123+
true_parent: Entity,
124+
child: Entity,
125+
control_node_query: &mut Query<&mut ControlNode>,
126+
unfiltered_children_query: &Query<&Children>,
127+
do_on_real: &mut impl FnMut(Entity),
128+
) {
129+
if let Ok(mut control_node) = control_node_query.get_mut(child) {
130+
control_node.true_parent = Some(true_parent);
131+
for &child in unfiltered_children_query
132+
.get(child)
133+
.ok()
134+
.into_iter()
135+
.map(|c| &**c)
136+
.flatten()
137+
{
138+
inner(
139+
true_parent,
140+
child,
141+
control_node_query,
142+
unfiltered_children_query,
143+
do_on_real,
144+
);
145+
}
119146
} else {
120-
warn!(
121-
"Unstyled child in a UI entity hierarchy. You are using an entity \
122-
without UI components as a child of an entity with UI components, results may be unexpected."
123-
);
147+
do_on_real(child);
124148
}
125149
}
126150

151+
for &child in children.iter() {
152+
inner(
153+
entity,
154+
child,
155+
control_node_query,
156+
unfiltered_children_query,
157+
&mut |e| {
158+
if let Some(stretch_node) = self.entity_to_stretch.get(&e) {
159+
stretch_children.push(*stretch_node);
160+
} else {
161+
warn!(
162+
"Unstyled child in a UI entity hierarchy. You are using an entity \
163+
without UI components as a child of an entity with UI components, results may be unexpected."
164+
);
165+
}
166+
},
167+
);
168+
}
169+
127170
let stretch_node = self.entity_to_stretch.get(&entity).unwrap();
128171
self.stretch
129172
.set_children(*stretch_node, stretch_children)
@@ -207,7 +250,10 @@ pub fn flex_node_system(
207250
(Entity, &Style, &CalculatedSize),
208251
(With<Node>, Changed<CalculatedSize>),
209252
>,
210-
children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
253+
changed_children_query: Query<(Entity, &Children), (With<Node>, Changed<Children>)>,
254+
unfiltered_children_query: Query<&Children>,
255+
mut control_node_query: Query<&mut ControlNode>,
256+
changed_cnc_query: Query<Entity, (Changed<Children>, With<ControlNode>)>,
211257
mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>,
212258
) {
213259
// update window root nodes
@@ -262,8 +308,28 @@ pub fn flex_node_system(
262308
}
263309

264310
// update children
265-
for (entity, children) in children_query.iter() {
266-
flex_surface.update_children(entity, children);
311+
for (entity, children) in changed_children_query.iter() {
312+
flex_surface.update_children(
313+
entity,
314+
children,
315+
&mut control_node_query,
316+
&unfiltered_children_query,
317+
);
318+
}
319+
320+
for entity in changed_cnc_query.iter() {
321+
let true_parent = if let Some(e) = control_node_query.get_mut(entity).unwrap().true_parent {
322+
e
323+
} else {
324+
continue;
325+
};
326+
let children = unfiltered_children_query.get(true_parent).unwrap();
327+
flex_surface.update_children(
328+
true_parent,
329+
children,
330+
&mut control_node_query,
331+
&unfiltered_children_query,
332+
);
267333
}
268334

269335
// compute layouts
@@ -284,7 +350,11 @@ pub fn flex_node_system(
284350
position.x = to_logical(layout.location.x + layout.size.width / 2.0);
285351
position.y = to_logical(layout.location.y + layout.size.height / 2.0);
286352
if let Some(parent) = parent {
287-
if let Ok(parent_layout) = flex_surface.get_layout(parent.0) {
353+
let parent = control_node_query
354+
.get_mut(parent.0)
355+
.map(|cn| cn.true_parent.unwrap())
356+
.unwrap_or(parent.0);
357+
if let Ok(parent_layout) = flex_surface.get_layout(parent) {
288358
position.x -= to_logical(parent_layout.size.width / 2.0);
289359
position.y -= to_logical(parent_layout.size.height / 2.0);
290360
}

crates/bevy_ui/src/ui_node.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bevy_ecs::{component::Component, reflect::ReflectComponent};
1+
use bevy_ecs::{prelude::*, reflect::ReflectComponent};
22
use bevy_math::{Rect, Size, Vec2};
33
use bevy_reflect::{Reflect, ReflectDeserialize};
44
use bevy_render::renderer::RenderResources;
@@ -11,6 +11,14 @@ pub struct Node {
1111
pub size: Vec2,
1212
}
1313

14+
/// If you add this to an entity, it should be the *only* component on it from bevy_ui.
15+
/// This component marks an entity as "transparent" to the UI layout system, meaning the
16+
/// children of this entity will be treated as the children of this entity s parent by the layout system.
17+
#[derive(Clone, Default, Component)]
18+
pub struct ControlNode {
19+
pub(crate) true_parent: Option<Entity>,
20+
}
21+
1422
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)]
1523
#[reflect_value(PartialEq, Serialize, Deserialize)]
1624
pub enum Val {

0 commit comments

Comments
 (0)