mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
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:
parent
0887f41b58
commit
2974293682
3 changed files with 102 additions and 15 deletions
|
@ -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,
|
||||
|
|
|
@ -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<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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue