Change GhostNode into a unit type (#17692)

# Objective

The feature gates for the `UiChildren` and `UiRootNodes` system params
make the unconstructable `GhostNode` `PhantomData` trick redundant.


## Solution

Remove the `GhostNode::new` method and change `GhostNode` into a unit
struct.

## Testing

```cargo run --example ghost_nodes```

still works
This commit is contained in:
ickshonpe 2025-02-05 18:44:37 +00:00 committed by GitHub
parent 6f39e44c48
commit 6be11a8a42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 50 deletions

View File

@ -2,11 +2,13 @@
use crate::Node;
use bevy_ecs::{prelude::*, system::SystemParam};
use bevy_reflect::prelude::*;
use bevy_render::view::Visibility;
use bevy_transform::prelude::Transform;
use core::marker::PhantomData;
#[cfg(feature = "ghost_nodes")]
use bevy_reflect::prelude::*;
#[cfg(feature = "ghost_nodes")]
use bevy_render::view::Visibility;
#[cfg(feature = "ghost_nodes")]
use bevy_transform::prelude::Transform;
#[cfg(feature = "ghost_nodes")]
use smallvec::SmallVec;
@ -15,30 +17,12 @@ use smallvec::SmallVec;
/// The UI systems will traverse past these and treat their first non-ghost descendants as direct children of their first non-ghost ancestor.
///
/// Any components necessary for transform and visibility propagation will be added automatically.
///
/// Instances of this type cannot be constructed unless the `ghost_nodes` feature is enabled.
#[cfg(feature = "ghost_nodes")]
#[derive(Component, Debug, Copy, Clone, Reflect)]
#[cfg_attr(feature = "ghost_nodes", derive(Default))]
#[reflect(Component, Debug)]
#[require(Visibility, Transform)]
pub struct GhostNode {
// This is a workaround to ensure that GhostNode is only constructable when the appropriate feature flag is enabled
#[reflect(ignore)]
unconstructable: PhantomData<()>, // Spooky!
}
#[cfg(feature = "ghost_nodes")]
impl GhostNode {
/// Creates a new ghost node.
///
/// This method is only available when the `ghost_node` feature is enabled,
/// and will eventually be deprecated then removed in favor of simply using `GhostNode` as no meaningful data is stored.
pub const fn new() -> Self {
GhostNode {
unconstructable: PhantomData,
}
}
}
pub struct GhostNode;
#[cfg(feature = "ghost_nodes")]
/// System param that allows iteration of all UI root nodes.
@ -232,20 +216,18 @@ mod tests {
.with_children(|parent| {
parent.spawn((A(2), Node::default()));
parent
.spawn((A(3), GhostNode::new()))
.spawn((A(3), GhostNode))
.with_child((A(4), Node::default()));
});
// Ghost root
world
.spawn((A(5), GhostNode::new()))
.with_children(|parent| {
parent.spawn((A(6), Node::default()));
parent
.spawn((A(7), GhostNode::new()))
.with_child((A(8), Node::default()))
.with_child(A(9));
});
world.spawn((A(5), GhostNode)).with_children(|parent| {
parent.spawn((A(6), Node::default()));
parent
.spawn((A(7), GhostNode))
.with_child((A(8), Node::default()))
.with_child(A(9));
});
let mut system_state = SystemState::<(UiRootNodes, Query<&A>)>::new(world);
let (ui_root_nodes, a_query) = system_state.get(world);
@ -260,15 +242,15 @@ mod tests {
let world = &mut World::new();
let n1 = world.spawn((A(1), Node::default())).id();
let n2 = world.spawn((A(2), GhostNode::new())).id();
let n3 = world.spawn((A(3), GhostNode::new())).id();
let n2 = world.spawn((A(2), GhostNode)).id();
let n3 = world.spawn((A(3), GhostNode)).id();
let n4 = world.spawn((A(4), Node::default())).id();
let n5 = world.spawn((A(5), Node::default())).id();
let n6 = world.spawn((A(6), GhostNode::new())).id();
let n7 = world.spawn((A(7), GhostNode::new())).id();
let n6 = world.spawn((A(6), GhostNode)).id();
let n7 = world.spawn((A(7), GhostNode)).id();
let n8 = world.spawn((A(8), Node::default())).id();
let n9 = world.spawn((A(9), GhostNode::new())).id();
let n9 = world.spawn((A(9), GhostNode)).id();
let n10 = world.spawn((A(10), Node::default())).id();
let no_ui = world.spawn_empty().id();

View File

@ -7,8 +7,7 @@
//! This is an experimental feature, and should be used with caution,
//! especially in concert with 3rd party plugins or systems that may not be aware of ghost nodes.
//!
//! To add [`GhostNode`] components to entities, you must enable the `ghost_nodes` feature flag,
//! as they are otherwise unconstructable even though the type is defined.
//! In order to use [`GhostNode`]s you must enable the `ghost_nodes` feature flag.
use bevy::{prelude::*, ui::experimental::GhostNode, winit::WinitSettings};
@ -30,14 +29,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
// Ghost UI root
commands
.spawn(GhostNode::new())
.with_children(|ghost_root| {
ghost_root.spawn(Node::default()).with_child(create_label(
"This text node is rendered under a ghost root",
font_handle.clone(),
));
});
commands.spawn(GhostNode).with_children(|ghost_root| {
ghost_root.spawn(Node::default()).with_child(create_label(
"This text node is rendered under a ghost root",
font_handle.clone(),
));
});
// Normal UI root
commands
@ -53,7 +50,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.spawn((Node::default(), Counter(0)))
.with_children(|layout_parent| {
layout_parent
.spawn((GhostNode::new(), Counter(0)))
.spawn((GhostNode, Counter(0)))
.with_children(|ghost_parent| {
// Ghost children using a separate counter state
// These buttons are being treated as children of layout_parent in the context of UI