Path: blob/main/crates/bevy_ui/src/experimental/ghost_hierarchy.rs
6599 views
//! This module contains [`GhostNode`] and utilities to flatten the UI hierarchy, traversing past ghost nodes.12#[cfg(feature = "ghost_nodes")]3use crate::ui_node::ComputedUiTargetCamera;4use crate::Node;5#[cfg(feature = "ghost_nodes")]6use bevy_camera::visibility::Visibility;7use bevy_ecs::{prelude::*, system::SystemParam};8#[cfg(feature = "ghost_nodes")]9use bevy_reflect::prelude::*;10#[cfg(feature = "ghost_nodes")]11use bevy_transform::prelude::Transform;12#[cfg(feature = "ghost_nodes")]13use smallvec::SmallVec;14/// Marker component for entities that should be ignored within UI hierarchies.15///16/// The UI systems will traverse past these and treat their first non-ghost descendants as direct children of their first non-ghost ancestor.17///18/// Any components necessary for transform and visibility propagation will be added automatically.19#[cfg(feature = "ghost_nodes")]20#[derive(Component, Debug, Copy, Clone, Reflect)]21#[cfg_attr(feature = "ghost_nodes", derive(Default))]22#[reflect(Component, Debug, Clone)]23#[require(Visibility, Transform, ComputedUiTargetCamera)]24pub struct GhostNode;2526#[cfg(feature = "ghost_nodes")]27/// System param that allows iteration of all UI root nodes.28///29/// A UI root node is either a [`Node`] without a [`ChildOf`], or with only [`GhostNode`] ancestors.30#[derive(SystemParam)]31pub struct UiRootNodes<'w, 's> {32root_node_query: Query<'w, 's, Entity, (With<Node>, Without<ChildOf>)>,33root_ghost_node_query: Query<'w, 's, Entity, (With<GhostNode>, Without<ChildOf>)>,34all_nodes_query: Query<'w, 's, Entity, With<Node>>,35ui_children: UiChildren<'w, 's>,36}3738#[cfg(not(feature = "ghost_nodes"))]39pub type UiRootNodes<'w, 's> = Query<'w, 's, Entity, (With<Node>, Without<ChildOf>)>;4041#[cfg(feature = "ghost_nodes")]42impl<'w, 's> UiRootNodes<'w, 's> {43pub fn iter(&'s self) -> impl Iterator<Item = Entity> + 's {44self.root_node_query45.iter()46.chain(self.root_ghost_node_query.iter().flat_map(|root_ghost| {47self.all_nodes_query48.iter_many(self.ui_children.iter_ui_children(root_ghost))49}))50}51}5253#[cfg(feature = "ghost_nodes")]54/// System param that gives access to UI children utilities, skipping over [`GhostNode`].55#[derive(SystemParam)]56pub struct UiChildren<'w, 's> {57ui_children_query: Query<58'w,59's,60(Option<&'static Children>, Has<GhostNode>),61Or<(With<Node>, With<GhostNode>)>,62>,63changed_children_query: Query<'w, 's, Entity, Changed<Children>>,64children_query: Query<'w, 's, &'static Children>,65ghost_nodes_query: Query<'w, 's, Entity, With<GhostNode>>,66parents_query: Query<'w, 's, &'static ChildOf>,67}6869#[cfg(not(feature = "ghost_nodes"))]70/// System param that gives access to UI children utilities.71#[derive(SystemParam)]72pub struct UiChildren<'w, 's> {73ui_children_query: Query<'w, 's, Option<&'static Children>, With<Node>>,74changed_children_query: Query<'w, 's, Entity, Changed<Children>>,75parents_query: Query<'w, 's, &'static ChildOf>,76}7778#[cfg(feature = "ghost_nodes")]79impl<'w, 's> UiChildren<'w, 's> {80/// Iterates the children of `entity`, skipping over [`GhostNode`].81///82/// Traverses the hierarchy depth-first to ensure child order.83///84/// # Performance85///86/// This iterator allocates if the `entity` node has more than 8 children (including ghost nodes).87pub fn iter_ui_children(&'s self, entity: Entity) -> UiChildrenIter<'w, 's> {88UiChildrenIter {89stack: self90.ui_children_query91.get(entity)92.map_or(SmallVec::new(), |(children, _)| {93children.into_iter().flatten().rev().copied().collect()94}),95query: &self.ui_children_query,96}97}9899/// Returns the UI parent of the provided entity, skipping over [`GhostNode`].100pub fn get_parent(&'s self, entity: Entity) -> Option<Entity> {101self.parents_query102.iter_ancestors(entity)103.find(|entity| !self.ghost_nodes_query.contains(*entity))104}105106/// Iterates the [`GhostNode`]s between this entity and its UI children.107pub fn iter_ghost_nodes(&'s self, entity: Entity) -> Box<dyn Iterator<Item = Entity> + 's> {108Box::new(109self.children_query110.get(entity)111.into_iter()112.flat_map(|children| {113self.ghost_nodes_query114.iter_many(children)115.flat_map(|entity| {116core::iter::once(entity).chain(self.iter_ghost_nodes(entity))117})118}),119)120}121122/// Given an entity in the UI hierarchy, check if its set of children has changed, e.g if children has been added/removed or if the order has changed.123pub fn is_changed(&'s self, entity: Entity) -> bool {124self.changed_children_query.contains(entity)125|| self126.iter_ghost_nodes(entity)127.any(|entity| self.changed_children_query.contains(entity))128}129130/// Returns `true` if the given entity is either a [`Node`] or a [`GhostNode`].131pub fn is_ui_node(&'s self, entity: Entity) -> bool {132self.ui_children_query.contains(entity)133}134}135136#[cfg(not(feature = "ghost_nodes"))]137impl<'w, 's> UiChildren<'w, 's> {138/// Iterates the children of `entity`.139pub fn iter_ui_children(&'s self, entity: Entity) -> impl Iterator<Item = Entity> + 's {140self.ui_children_query141.get(entity)142.ok()143.flatten()144.map(|children| children.as_ref())145.unwrap_or(&[])146.iter()147.copied()148}149150/// Returns the UI parent of the provided entity.151pub fn get_parent(&'s self, entity: Entity) -> Option<Entity> {152self.parents_query.get(entity).ok().map(ChildOf::parent)153}154155/// Given an entity in the UI hierarchy, check if its set of children has changed, e.g if children has been added/removed or if the order has changed.156pub fn is_changed(&'s self, entity: Entity) -> bool {157self.changed_children_query.contains(entity)158}159160/// Returns `true` if the given entity is either a [`Node`] or a [`GhostNode`].161pub fn is_ui_node(&'s self, entity: Entity) -> bool {162self.ui_children_query.contains(entity)163}164}165166#[cfg(feature = "ghost_nodes")]167pub struct UiChildrenIter<'w, 's> {168stack: SmallVec<[Entity; 8]>,169query: &'s Query<170'w,171's,172(Option<&'static Children>, Has<GhostNode>),173Or<(With<Node>, With<GhostNode>)>,174>,175}176177#[cfg(feature = "ghost_nodes")]178impl<'w, 's> Iterator for UiChildrenIter<'w, 's> {179type Item = Entity;180fn next(&mut self) -> Option<Self::Item> {181loop {182let entity = self.stack.pop()?;183if let Ok((children, has_ghost_node)) = self.query.get(entity) {184if !has_ghost_node {185return Some(entity);186}187if let Some(children) = children {188self.stack.extend(children.iter().rev());189}190}191}192}193}194195#[cfg(all(test, feature = "ghost_nodes"))]196mod tests {197use bevy_ecs::{198prelude::Component,199system::{Query, SystemState},200world::World,201};202203use super::{GhostNode, Node, UiChildren, UiRootNodes};204205#[derive(Component, PartialEq, Debug)]206struct A(usize);207208#[test]209fn iterate_ui_root_nodes() {210let world = &mut World::new();211212// Normal root213world214.spawn((A(1), Node::default()))215.with_children(|parent| {216parent.spawn((A(2), Node::default()));217parent218.spawn((A(3), GhostNode))219.with_child((A(4), Node::default()));220});221222// Ghost root223world.spawn((A(5), GhostNode)).with_children(|parent| {224parent.spawn((A(6), Node::default()));225parent226.spawn((A(7), GhostNode))227.with_child((A(8), Node::default()))228.with_child(A(9));229});230231let mut system_state = SystemState::<(UiRootNodes, Query<&A>)>::new(world);232let (ui_root_nodes, a_query) = system_state.get(world);233234let result: Vec<_> = a_query.iter_many(ui_root_nodes.iter()).collect();235236assert_eq!([&A(1), &A(6), &A(8)], result.as_slice());237}238239#[test]240fn iterate_ui_children() {241let world = &mut World::new();242243let n1 = world.spawn((A(1), Node::default())).id();244let n2 = world.spawn((A(2), GhostNode)).id();245let n3 = world.spawn((A(3), GhostNode)).id();246let n4 = world.spawn((A(4), Node::default())).id();247let n5 = world.spawn((A(5), Node::default())).id();248249let n6 = world.spawn((A(6), GhostNode)).id();250let n7 = world.spawn((A(7), GhostNode)).id();251let n8 = world.spawn((A(8), Node::default())).id();252let n9 = world.spawn((A(9), GhostNode)).id();253let n10 = world.spawn((A(10), Node::default())).id();254255let no_ui = world.spawn_empty().id();256257world.entity_mut(n1).add_children(&[n2, n3, n4, n6]);258world.entity_mut(n2).add_children(&[n5]);259260world.entity_mut(n6).add_children(&[n7, no_ui, n9]);261world.entity_mut(n7).add_children(&[n8]);262world.entity_mut(n9).add_children(&[n10]);263264let mut system_state = SystemState::<(UiChildren, Query<&A>)>::new(world);265let (ui_children, a_query) = system_state.get(world);266267let result: Vec<_> = a_query268.iter_many(ui_children.iter_ui_children(n1))269.collect();270271assert_eq!([&A(5), &A(4), &A(8), &A(10)], result.as_slice());272}273}274275276