Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ui/ghost_nodes.rs
6595 views
1
//! This example demonstrates the use of Ghost Nodes.
2
//!
3
//! UI layout will ignore ghost nodes, and treat their children as if they were direct descendants of the first non-ghost ancestor.
4
//!
5
//! # Warning
6
//!
7
//! This is an experimental feature, and should be used with caution,
8
//! especially in concert with 3rd party plugins or systems that may not be aware of ghost nodes.
9
//!
10
//! In order to use [`GhostNode`]s you must enable the `ghost_nodes` feature flag.
11
12
use bevy::{prelude::*, ui::experimental::GhostNode};
13
14
fn main() {
15
App::new()
16
.add_plugins(DefaultPlugins)
17
.add_systems(Startup, setup)
18
.add_systems(Update, button_system)
19
.run();
20
}
21
22
#[derive(Component)]
23
struct Counter(i32);
24
25
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
26
let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf");
27
28
commands.spawn(Camera2d);
29
30
// Ghost UI root
31
commands.spawn(GhostNode).with_children(|ghost_root| {
32
ghost_root.spawn(Node::default()).with_child(create_label(
33
"This text node is rendered under a ghost root",
34
font_handle.clone(),
35
));
36
});
37
38
// Normal UI root
39
commands
40
.spawn(Node {
41
width: percent(100),
42
height: percent(100),
43
align_items: AlignItems::Center,
44
justify_content: JustifyContent::Center,
45
..default()
46
})
47
.with_children(|parent| {
48
parent
49
.spawn((Node::default(), Counter(0)))
50
.with_children(|layout_parent| {
51
layout_parent
52
.spawn((GhostNode, Counter(0)))
53
.with_children(|ghost_parent| {
54
// Ghost children using a separate counter state
55
// These buttons are being treated as children of layout_parent in the context of UI
56
ghost_parent
57
.spawn(create_button())
58
.with_child(create_label("0", font_handle.clone()));
59
ghost_parent
60
.spawn(create_button())
61
.with_child(create_label("0", font_handle.clone()));
62
});
63
64
// A normal child using the layout parent counter
65
layout_parent
66
.spawn(create_button())
67
.with_child(create_label("0", font_handle.clone()));
68
});
69
});
70
}
71
72
fn create_button() -> impl Bundle {
73
(
74
Button,
75
Node {
76
width: px(150),
77
height: px(65),
78
border: UiRect::all(px(5)),
79
// horizontally center child text
80
justify_content: JustifyContent::Center,
81
// vertically center child text
82
align_items: AlignItems::Center,
83
..default()
84
},
85
BorderColor::all(Color::BLACK),
86
BorderRadius::MAX,
87
BackgroundColor(Color::srgb(0.15, 0.15, 0.15)),
88
)
89
}
90
91
fn create_label(text: &str, font: Handle<Font>) -> (Text, TextFont, TextColor) {
92
(
93
Text::new(text),
94
TextFont {
95
font,
96
font_size: 33.0,
97
..default()
98
},
99
TextColor(Color::srgb(0.9, 0.9, 0.9)),
100
)
101
}
102
103
fn button_system(
104
mut interaction_query: Query<(&Interaction, &ChildOf), (Changed<Interaction>, With<Button>)>,
105
labels_query: Query<(&Children, &ChildOf), With<Button>>,
106
mut text_query: Query<&mut Text>,
107
mut counter_query: Query<&mut Counter>,
108
) {
109
// Update parent counter on click
110
for (interaction, child_of) in &mut interaction_query {
111
if matches!(interaction, Interaction::Pressed) {
112
let mut counter = counter_query.get_mut(child_of.parent()).unwrap();
113
counter.0 += 1;
114
}
115
}
116
117
// Update button labels to match their parent counter
118
for (children, child_of) in &labels_query {
119
let counter = counter_query.get(child_of.parent()).unwrap();
120
let mut text = text_query.get_mut(children[0]).unwrap();
121
122
**text = counter.0.to_string();
123
}
124
}
125
126