Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ui/display_and_visibility.rs
6595 views
1
//! Demonstrates how Display and Visibility work in the UI.
2
3
use bevy::{
4
color::palettes::css::{DARK_CYAN, DARK_GRAY, YELLOW},
5
ecs::{component::Mutable, hierarchy::ChildSpawnerCommands},
6
prelude::*,
7
};
8
9
const PALETTE: [&str; 4] = ["27496D", "466B7A", "669DB3", "ADCBE3"];
10
const HIDDEN_COLOR: Color = Color::srgb(1.0, 0.7, 0.7);
11
12
fn main() {
13
App::new()
14
.add_plugins(DefaultPlugins)
15
.add_systems(Startup, setup)
16
.add_systems(
17
Update,
18
(
19
buttons_handler::<Display>,
20
buttons_handler::<Visibility>,
21
text_hover,
22
),
23
)
24
.run();
25
}
26
27
#[derive(Component)]
28
struct Target<T> {
29
id: Entity,
30
phantom: std::marker::PhantomData<T>,
31
}
32
33
impl<T> Target<T> {
34
fn new(id: Entity) -> Self {
35
Self {
36
id,
37
phantom: std::marker::PhantomData,
38
}
39
}
40
}
41
42
trait TargetUpdate {
43
type TargetComponent: Component<Mutability = Mutable>;
44
const NAME: &'static str;
45
fn update_target(&self, target: &mut Self::TargetComponent) -> String;
46
}
47
48
impl TargetUpdate for Target<Display> {
49
type TargetComponent = Node;
50
const NAME: &'static str = "Display";
51
fn update_target(&self, node: &mut Self::TargetComponent) -> String {
52
node.display = match node.display {
53
Display::Flex => Display::None,
54
Display::None => Display::Flex,
55
Display::Block | Display::Grid => unreachable!(),
56
};
57
format!("{}::{:?} ", Self::NAME, node.display)
58
}
59
}
60
61
impl TargetUpdate for Target<Visibility> {
62
type TargetComponent = Visibility;
63
const NAME: &'static str = "Visibility";
64
fn update_target(&self, visibility: &mut Self::TargetComponent) -> String {
65
*visibility = match *visibility {
66
Visibility::Inherited => Visibility::Visible,
67
Visibility::Visible => Visibility::Hidden,
68
Visibility::Hidden => Visibility::Inherited,
69
};
70
format!("{}::{visibility:?}", Self::NAME)
71
}
72
}
73
74
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
75
let palette: [Color; 4] = PALETTE.map(|hex| Srgba::hex(hex).unwrap().into());
76
77
let text_font = TextFont {
78
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
79
..default()
80
};
81
82
commands.spawn(Camera2d);
83
commands
84
.spawn((
85
Node {
86
width: percent(100),
87
height: percent(100),
88
flex_direction: FlexDirection::Column,
89
align_items: AlignItems::Center,
90
justify_content: JustifyContent::SpaceEvenly,
91
..Default::default()
92
},
93
BackgroundColor(Color::BLACK),
94
))
95
.with_children(|parent| {
96
parent.spawn((
97
Text::new("Use the panel on the right to change the Display and Visibility properties for the respective nodes of the panel on the left"),
98
text_font.clone(),
99
TextLayout::new_with_justify(Justify::Center),
100
Node {
101
margin: UiRect::bottom(px(10)),
102
..Default::default()
103
},
104
));
105
106
parent
107
.spawn(Node {
108
width: percent(100),
109
..default()
110
})
111
.with_children(|parent| {
112
let mut target_ids = vec![];
113
parent
114
.spawn(Node {
115
width: percent(50),
116
height: px(520),
117
justify_content: JustifyContent::Center,
118
..default()
119
})
120
.with_children(|parent| {
121
target_ids = spawn_left_panel(parent, &palette);
122
});
123
124
parent
125
.spawn(Node {
126
width: percent(50),
127
justify_content: JustifyContent::Center,
128
..default()
129
})
130
.with_children(|parent| {
131
spawn_right_panel(parent, text_font, &palette, target_ids);
132
});
133
});
134
135
parent
136
.spawn(Node {
137
flex_direction: FlexDirection::Row,
138
align_items: AlignItems::Start,
139
justify_content: JustifyContent::Start,
140
column_gap: px(10),
141
..default()
142
})
143
.with_children(|builder| {
144
let text_font = TextFont {
145
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
146
..default()
147
};
148
149
builder.spawn((
150
Text::new("Display::None\nVisibility::Hidden\nVisibility::Inherited"),
151
text_font.clone(),
152
TextColor(HIDDEN_COLOR),
153
TextLayout::new_with_justify(Justify::Center),
154
));
155
builder.spawn((
156
Text::new("-\n-\n-"),
157
text_font.clone(),
158
TextColor(DARK_GRAY.into()),
159
TextLayout::new_with_justify(Justify::Center),
160
));
161
builder.spawn((Text::new("The UI Node and its descendants will not be visible and will not be allotted any space in the UI layout.\nThe UI Node will not be visible but will still occupy space in the UI layout.\nThe UI node will inherit the visibility property of its parent. If it has no parent it will be visible."), text_font));
162
});
163
});
164
}
165
166
fn spawn_left_panel(builder: &mut ChildSpawnerCommands, palette: &[Color; 4]) -> Vec<Entity> {
167
let mut target_ids = vec![];
168
builder
169
.spawn((
170
Node {
171
padding: UiRect::all(px(10)),
172
..default()
173
},
174
BackgroundColor(Color::WHITE),
175
))
176
.with_children(|parent| {
177
parent
178
.spawn((Node::default(), BackgroundColor(Color::BLACK)))
179
.with_children(|parent| {
180
let id = parent
181
.spawn((
182
Node {
183
align_items: AlignItems::FlexEnd,
184
justify_content: JustifyContent::FlexEnd,
185
..default()
186
},
187
BackgroundColor(palette[0]),
188
Outline {
189
width: px(4),
190
color: DARK_CYAN.into(),
191
offset: px(10),
192
},
193
))
194
.with_children(|parent| {
195
parent.spawn(Node {
196
width: px(100),
197
height: px(500),
198
..default()
199
});
200
201
let id = parent
202
.spawn((
203
Node {
204
height: px(400),
205
align_items: AlignItems::FlexEnd,
206
justify_content: JustifyContent::FlexEnd,
207
..default()
208
},
209
BackgroundColor(palette[1]),
210
))
211
.with_children(|parent| {
212
parent.spawn(Node {
213
width: px(100),
214
height: px(400),
215
..default()
216
});
217
218
let id = parent
219
.spawn((
220
Node {
221
height: px(300),
222
align_items: AlignItems::FlexEnd,
223
justify_content: JustifyContent::FlexEnd,
224
..default()
225
},
226
BackgroundColor(palette[2]),
227
))
228
.with_children(|parent| {
229
parent.spawn(Node {
230
width: px(100),
231
height: px(300),
232
..default()
233
});
234
235
let id = parent
236
.spawn((
237
Node {
238
width: px(200),
239
height: px(200),
240
..default()
241
},
242
BackgroundColor(palette[3]),
243
))
244
.id();
245
target_ids.push(id);
246
})
247
.id();
248
target_ids.push(id);
249
})
250
.id();
251
target_ids.push(id);
252
})
253
.id();
254
target_ids.push(id);
255
});
256
});
257
target_ids
258
}
259
260
fn spawn_right_panel(
261
parent: &mut ChildSpawnerCommands,
262
text_font: TextFont,
263
palette: &[Color; 4],
264
mut target_ids: Vec<Entity>,
265
) {
266
let spawn_buttons = |parent: &mut ChildSpawnerCommands, target_id| {
267
spawn_button::<Display>(parent, text_font.clone(), target_id);
268
spawn_button::<Visibility>(parent, text_font.clone(), target_id);
269
};
270
parent
271
.spawn((
272
Node {
273
padding: UiRect::all(px(10)),
274
..default()
275
},
276
BackgroundColor(Color::WHITE),
277
))
278
.with_children(|parent| {
279
parent
280
.spawn((
281
Node {
282
width: px(500),
283
height: px(500),
284
flex_direction: FlexDirection::Column,
285
align_items: AlignItems::FlexEnd,
286
justify_content: JustifyContent::SpaceBetween,
287
padding: UiRect {
288
left: px(5),
289
top: px(5),
290
..default()
291
},
292
..default()
293
},
294
BackgroundColor(palette[0]),
295
Outline {
296
width: px(4),
297
color: DARK_CYAN.into(),
298
offset: px(10),
299
},
300
))
301
.with_children(|parent| {
302
spawn_buttons(parent, target_ids.pop().unwrap());
303
304
parent
305
.spawn((
306
Node {
307
width: px(400),
308
height: px(400),
309
flex_direction: FlexDirection::Column,
310
align_items: AlignItems::FlexEnd,
311
justify_content: JustifyContent::SpaceBetween,
312
padding: UiRect {
313
left: px(5),
314
top: px(5),
315
..default()
316
},
317
..default()
318
},
319
BackgroundColor(palette[1]),
320
))
321
.with_children(|parent| {
322
spawn_buttons(parent, target_ids.pop().unwrap());
323
324
parent
325
.spawn((
326
Node {
327
width: px(300),
328
height: px(300),
329
flex_direction: FlexDirection::Column,
330
align_items: AlignItems::FlexEnd,
331
justify_content: JustifyContent::SpaceBetween,
332
padding: UiRect {
333
left: px(5),
334
top: px(5),
335
..default()
336
},
337
..default()
338
},
339
BackgroundColor(palette[2]),
340
))
341
.with_children(|parent| {
342
spawn_buttons(parent, target_ids.pop().unwrap());
343
344
parent
345
.spawn((
346
Node {
347
width: px(200),
348
height: px(200),
349
align_items: AlignItems::FlexStart,
350
justify_content: JustifyContent::SpaceBetween,
351
flex_direction: FlexDirection::Column,
352
padding: UiRect {
353
left: px(5),
354
top: px(5),
355
..default()
356
},
357
..default()
358
},
359
BackgroundColor(palette[3]),
360
))
361
.with_children(|parent| {
362
spawn_buttons(parent, target_ids.pop().unwrap());
363
364
parent.spawn(Node {
365
width: px(100),
366
height: px(100),
367
..default()
368
});
369
});
370
});
371
});
372
});
373
});
374
}
375
376
fn spawn_button<T>(parent: &mut ChildSpawnerCommands, text_font: TextFont, target: Entity)
377
where
378
T: Default + std::fmt::Debug + Send + Sync + 'static,
379
Target<T>: TargetUpdate,
380
{
381
parent
382
.spawn((
383
Button,
384
Node {
385
align_self: AlignSelf::FlexStart,
386
padding: UiRect::axes(px(5), px(1)),
387
..default()
388
},
389
BackgroundColor(Color::BLACK.with_alpha(0.5)),
390
Target::<T>::new(target),
391
))
392
.with_children(|builder| {
393
builder.spawn((
394
Text(format!("{}::{:?}", Target::<T>::NAME, T::default())),
395
text_font,
396
TextLayout::new_with_justify(Justify::Center),
397
));
398
});
399
}
400
401
fn buttons_handler<T>(
402
mut left_panel_query: Query<&mut <Target<T> as TargetUpdate>::TargetComponent>,
403
mut visibility_button_query: Query<(&Target<T>, &Interaction, &Children), Changed<Interaction>>,
404
mut text_query: Query<(&mut Text, &mut TextColor)>,
405
) where
406
T: Send + Sync,
407
Target<T>: TargetUpdate + Component,
408
{
409
for (target, interaction, children) in visibility_button_query.iter_mut() {
410
if matches!(interaction, Interaction::Pressed) {
411
let mut target_value = left_panel_query.get_mut(target.id).unwrap();
412
for &child in children {
413
if let Ok((mut text, mut text_color)) = text_query.get_mut(child) {
414
**text = target.update_target(target_value.as_mut());
415
text_color.0 = if text.contains("None") || text.contains("Hidden") {
416
Color::srgb(1.0, 0.7, 0.7)
417
} else {
418
Color::WHITE
419
};
420
}
421
}
422
}
423
}
424
}
425
426
fn text_hover(
427
mut button_query: Query<(&Interaction, &mut BackgroundColor, &Children), Changed<Interaction>>,
428
mut text_query: Query<(&Text, &mut TextColor)>,
429
) {
430
for (interaction, mut color, children) in button_query.iter_mut() {
431
match interaction {
432
Interaction::Hovered => {
433
*color = Color::BLACK.with_alpha(0.6).into();
434
for &child in children {
435
if let Ok((_, mut text_color)) = text_query.get_mut(child) {
436
// Bypass change detection to avoid recomputation of the text when only changing the color
437
text_color.bypass_change_detection().0 = YELLOW.into();
438
}
439
}
440
}
441
_ => {
442
*color = Color::BLACK.with_alpha(0.5).into();
443
for &child in children {
444
if let Ok((text, mut text_color)) = text_query.get_mut(child) {
445
text_color.bypass_change_detection().0 =
446
if text.contains("None") || text.contains("Hidden") {
447
HIDDEN_COLOR
448
} else {
449
Color::WHITE
450
};
451
}
452
}
453
}
454
}
455
}
456
}
457
458