Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ui/size_constraints.rs
6595 views
1
//! Demonstrates how the to use the size constraints to control the size of a UI node.
2
3
use bevy::{color::palettes::css::*, prelude::*};
4
5
fn main() {
6
App::new()
7
.add_plugins(DefaultPlugins)
8
.add_event::<ButtonActivatedEvent>()
9
.add_systems(Startup, setup)
10
.add_systems(Update, (update_buttons, update_radio_buttons_colors))
11
.run();
12
}
13
14
const ACTIVE_BORDER_COLOR: Color = Color::Srgba(ANTIQUE_WHITE);
15
const INACTIVE_BORDER_COLOR: Color = Color::BLACK;
16
17
const ACTIVE_INNER_COLOR: Color = Color::WHITE;
18
const INACTIVE_INNER_COLOR: Color = Color::Srgba(NAVY);
19
20
const ACTIVE_TEXT_COLOR: Color = Color::BLACK;
21
const HOVERED_TEXT_COLOR: Color = Color::WHITE;
22
const UNHOVERED_TEXT_COLOR: Color = Color::srgb(0.5, 0.5, 0.5);
23
24
#[derive(Component)]
25
struct Bar;
26
27
#[derive(Copy, Clone, Debug, Component, PartialEq)]
28
enum Constraint {
29
FlexBasis,
30
Width,
31
MinWidth,
32
MaxWidth,
33
}
34
35
#[derive(Copy, Clone, Component)]
36
struct ButtonValue(Val);
37
38
#[derive(BufferedEvent)]
39
struct ButtonActivatedEvent(Entity);
40
41
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
42
// ui camera
43
commands.spawn(Camera2d);
44
45
let text_font = (
46
TextFont {
47
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
48
font_size: 33.0,
49
..Default::default()
50
},
51
TextColor(Color::srgb(0.9, 0.9, 0.9)),
52
);
53
54
commands
55
.spawn((
56
Node {
57
width: percent(100),
58
height: percent(100),
59
justify_content: JustifyContent::Center,
60
align_items: AlignItems::Center,
61
..default()
62
},
63
BackgroundColor(Color::BLACK),
64
))
65
.with_children(|parent| {
66
parent
67
.spawn(Node {
68
flex_direction: FlexDirection::Column,
69
align_items: AlignItems::Center,
70
justify_content: JustifyContent::Center,
71
..default()
72
})
73
.with_children(|parent| {
74
parent.spawn((
75
Text::new("Size Constraints Example"),
76
text_font.clone(),
77
Node {
78
margin: UiRect::bottom(px(25)),
79
..Default::default()
80
},
81
));
82
83
spawn_bar(parent);
84
85
parent
86
.spawn((
87
Node {
88
flex_direction: FlexDirection::Column,
89
align_items: AlignItems::Stretch,
90
padding: UiRect::all(px(10)),
91
margin: UiRect::top(px(50)),
92
..default()
93
},
94
BackgroundColor(YELLOW.into()),
95
))
96
.with_children(|parent| {
97
for constraint in [
98
Constraint::MinWidth,
99
Constraint::FlexBasis,
100
Constraint::Width,
101
Constraint::MaxWidth,
102
] {
103
spawn_button_row(parent, constraint, text_font.clone());
104
}
105
});
106
});
107
});
108
}
109
110
fn spawn_bar(parent: &mut ChildSpawnerCommands) {
111
parent
112
.spawn((
113
Node {
114
flex_basis: percent(100),
115
align_self: AlignSelf::Stretch,
116
padding: UiRect::all(px(10)),
117
..default()
118
},
119
BackgroundColor(YELLOW.into()),
120
))
121
.with_children(|parent| {
122
parent
123
.spawn((
124
Node {
125
align_items: AlignItems::Stretch,
126
width: percent(100),
127
height: px(100),
128
padding: UiRect::all(px(4)),
129
..default()
130
},
131
BackgroundColor(Color::BLACK),
132
))
133
.with_children(|parent| {
134
parent.spawn((Node::default(), BackgroundColor(Color::WHITE), Bar));
135
});
136
});
137
}
138
139
fn spawn_button_row(
140
parent: &mut ChildSpawnerCommands,
141
constraint: Constraint,
142
text_style: (TextFont, TextColor),
143
) {
144
let label = match constraint {
145
Constraint::FlexBasis => "flex_basis",
146
Constraint::Width => "size",
147
Constraint::MinWidth => "min_size",
148
Constraint::MaxWidth => "max_size",
149
};
150
151
parent
152
.spawn((
153
Node {
154
flex_direction: FlexDirection::Column,
155
padding: UiRect::all(px(2)),
156
align_items: AlignItems::Stretch,
157
..default()
158
},
159
BackgroundColor(Color::BLACK),
160
))
161
.with_children(|parent| {
162
parent
163
.spawn(Node {
164
flex_direction: FlexDirection::Row,
165
justify_content: JustifyContent::End,
166
padding: UiRect::all(px(2)),
167
..default()
168
})
169
.with_children(|parent| {
170
// spawn row label
171
parent
172
.spawn((Node {
173
min_width: px(200),
174
max_width: px(200),
175
justify_content: JustifyContent::Center,
176
align_items: AlignItems::Center,
177
..default()
178
},))
179
.with_child((Text::new(label), text_style.clone()));
180
181
// spawn row buttons
182
parent.spawn(Node::default()).with_children(|parent| {
183
spawn_button(
184
parent,
185
constraint,
186
ButtonValue(auto()),
187
"Auto".to_string(),
188
text_style.clone(),
189
true,
190
);
191
for percent_value in [0, 25, 50, 75, 100, 125] {
192
spawn_button(
193
parent,
194
constraint,
195
ButtonValue(percent(percent_value)),
196
format!("{percent_value}%"),
197
text_style.clone(),
198
false,
199
);
200
}
201
});
202
});
203
});
204
}
205
206
fn spawn_button(
207
parent: &mut ChildSpawnerCommands,
208
constraint: Constraint,
209
action: ButtonValue,
210
label: String,
211
text_style: (TextFont, TextColor),
212
active: bool,
213
) {
214
parent
215
.spawn((
216
Button,
217
Node {
218
align_items: AlignItems::Center,
219
justify_content: JustifyContent::Center,
220
border: UiRect::all(px(2)),
221
margin: UiRect::horizontal(px(2)),
222
..Default::default()
223
},
224
BorderColor::all(if active {
225
ACTIVE_BORDER_COLOR
226
} else {
227
INACTIVE_BORDER_COLOR
228
}),
229
constraint,
230
action,
231
))
232
.with_children(|parent| {
233
parent
234
.spawn((
235
Node {
236
width: px(100),
237
justify_content: JustifyContent::Center,
238
..default()
239
},
240
BackgroundColor(if active {
241
ACTIVE_INNER_COLOR
242
} else {
243
INACTIVE_INNER_COLOR
244
}),
245
))
246
.with_child((
247
Text::new(label),
248
text_style.0,
249
TextColor(if active {
250
ACTIVE_TEXT_COLOR
251
} else {
252
UNHOVERED_TEXT_COLOR
253
}),
254
TextLayout::new_with_justify(Justify::Center),
255
));
256
});
257
}
258
259
fn update_buttons(
260
mut button_query: Query<
261
(Entity, &Interaction, &Constraint, &ButtonValue),
262
Changed<Interaction>,
263
>,
264
mut bar_node: Single<&mut Node, With<Bar>>,
265
mut text_query: Query<&mut TextColor>,
266
children_query: Query<&Children>,
267
mut button_activated_event: EventWriter<ButtonActivatedEvent>,
268
) {
269
for (button_id, interaction, constraint, value) in button_query.iter_mut() {
270
match interaction {
271
Interaction::Pressed => {
272
button_activated_event.write(ButtonActivatedEvent(button_id));
273
match constraint {
274
Constraint::FlexBasis => {
275
bar_node.flex_basis = value.0;
276
}
277
Constraint::Width => {
278
bar_node.width = value.0;
279
}
280
Constraint::MinWidth => {
281
bar_node.min_width = value.0;
282
}
283
Constraint::MaxWidth => {
284
bar_node.max_width = value.0;
285
}
286
}
287
}
288
Interaction::Hovered => {
289
if let Ok(children) = children_query.get(button_id) {
290
for &child in children {
291
if let Ok(grand_children) = children_query.get(child) {
292
for &grandchild in grand_children {
293
if let Ok(mut text_color) = text_query.get_mut(grandchild)
294
&& text_color.0 != ACTIVE_TEXT_COLOR
295
{
296
text_color.0 = HOVERED_TEXT_COLOR;
297
}
298
}
299
}
300
}
301
}
302
}
303
Interaction::None => {
304
if let Ok(children) = children_query.get(button_id) {
305
for &child in children {
306
if let Ok(grand_children) = children_query.get(child) {
307
for &grandchild in grand_children {
308
if let Ok(mut text_color) = text_query.get_mut(grandchild)
309
&& text_color.0 != ACTIVE_TEXT_COLOR
310
{
311
text_color.0 = UNHOVERED_TEXT_COLOR;
312
}
313
}
314
}
315
}
316
}
317
}
318
}
319
}
320
}
321
322
fn update_radio_buttons_colors(
323
mut event_reader: EventReader<ButtonActivatedEvent>,
324
button_query: Query<(Entity, &Constraint, &Interaction)>,
325
mut border_query: Query<&mut BorderColor>,
326
mut color_query: Query<&mut BackgroundColor>,
327
mut text_query: Query<&mut TextColor>,
328
children_query: Query<&Children>,
329
) {
330
for &ButtonActivatedEvent(button_id) in event_reader.read() {
331
let (_, target_constraint, _) = button_query.get(button_id).unwrap();
332
for (id, constraint, interaction) in button_query.iter() {
333
if target_constraint == constraint {
334
let (border_color, inner_color, label_color) = if id == button_id {
335
(ACTIVE_BORDER_COLOR, ACTIVE_INNER_COLOR, ACTIVE_TEXT_COLOR)
336
} else {
337
(
338
INACTIVE_BORDER_COLOR,
339
INACTIVE_INNER_COLOR,
340
if matches!(interaction, Interaction::Hovered) {
341
HOVERED_TEXT_COLOR
342
} else {
343
UNHOVERED_TEXT_COLOR
344
},
345
)
346
};
347
348
*border_query.get_mut(id).unwrap() = BorderColor::all(border_color);
349
for &child in children_query.get(id).into_iter().flatten() {
350
color_query.get_mut(child).unwrap().0 = inner_color;
351
for &grandchild in children_query.get(child).into_iter().flatten() {
352
if let Ok(mut text_color) = text_query.get_mut(grandchild) {
353
text_color.0 = label_color;
354
}
355
}
356
}
357
}
358
}
359
}
360
}
361
362