From 6be11a8a42c2c73646cc994392bbd628b6de523b Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 5 Feb 2025 18:44:37 +0000 Subject: [PATCH] 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 --- .../src/experimental/ghost_hierarchy.rs | 60 +++++++------------ examples/ui/ghost_nodes.rs | 19 +++--- 2 files changed, 29 insertions(+), 50 deletions(-) diff --git a/crates/bevy_ui/src/experimental/ghost_hierarchy.rs b/crates/bevy_ui/src/experimental/ghost_hierarchy.rs index f00209efac..c5eb46ffe8 100644 --- a/crates/bevy_ui/src/experimental/ghost_hierarchy.rs +++ b/crates/bevy_ui/src/experimental/ghost_hierarchy.rs @@ -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(); diff --git a/examples/ui/ghost_nodes.rs b/examples/ui/ghost_nodes.rs index 52441364ee..b516ad0964 100644 --- a/examples/ui/ghost_nodes.rs +++ b/examples/ui/ghost_nodes.rs @@ -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) { 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) { .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