Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/testbed/ui.rs
9315 views
1
//! UI testbed
2
//!
3
//! You can switch scene by pressing the spacebar
4
5
mod helpers;
6
7
use argh::FromArgs;
8
use bevy::prelude::*;
9
10
use helpers::Next;
11
12
#[derive(FromArgs)]
13
/// ui testbed
14
pub struct Args {
15
#[argh(positional)]
16
scene: Option<Scene>,
17
}
18
19
fn main() {
20
#[cfg(not(target_arch = "wasm32"))]
21
let args: Args = argh::from_env();
22
#[cfg(target_arch = "wasm32")]
23
let args: Args = Args::from_args(&[], &[]).unwrap();
24
25
let mut app = App::new();
26
app.add_plugins(DefaultPlugins.set(WindowPlugin {
27
primary_window: Some(Window {
28
// The ViewportCoords scene relies on these specific viewport dimensions,
29
// so let's explicitly define them and set resizable to false
30
resolution: (1280, 720).into(),
31
resizable: false,
32
..Default::default()
33
}),
34
..Default::default()
35
}))
36
.add_systems(OnEnter(Scene::Image), image::setup)
37
.add_systems(OnEnter(Scene::Text), text::setup)
38
.add_systems(OnEnter(Scene::Grid), grid::setup)
39
.add_systems(OnEnter(Scene::Borders), borders::setup)
40
.add_systems(OnEnter(Scene::BoxShadow), box_shadow::setup)
41
.add_systems(OnEnter(Scene::TextWrap), text_wrap::setup)
42
.add_systems(OnEnter(Scene::Overflow), overflow::setup)
43
.add_systems(OnEnter(Scene::Slice), slice::setup)
44
.add_systems(OnEnter(Scene::LayoutRounding), layout_rounding::setup)
45
.add_systems(OnEnter(Scene::LinearGradient), linear_gradient::setup)
46
.add_systems(OnEnter(Scene::RadialGradient), radial_gradient::setup)
47
.add_systems(OnEnter(Scene::Transformations), transformations::setup)
48
.add_systems(OnEnter(Scene::ViewportCoords), viewport_coords::setup)
49
.add_systems(Update, switch_scene);
50
51
match args.scene {
52
None => app.init_state::<Scene>(),
53
Some(scene) => app.insert_state(scene),
54
};
55
56
#[cfg(feature = "bevy_ui_debug")]
57
{
58
app.add_systems(OnEnter(Scene::DebugOutlines), debug_outlines::setup);
59
app.add_systems(OnExit(Scene::DebugOutlines), debug_outlines::teardown);
60
}
61
62
#[cfg(feature = "bevy_ci_testing")]
63
app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
64
65
app.run();
66
}
67
68
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
69
#[states(scoped_entities)]
70
enum Scene {
71
#[default]
72
Image,
73
Text,
74
Grid,
75
Borders,
76
BoxShadow,
77
TextWrap,
78
Overflow,
79
Slice,
80
LayoutRounding,
81
LinearGradient,
82
RadialGradient,
83
Transformations,
84
#[cfg(feature = "bevy_ui_debug")]
85
DebugOutlines,
86
ViewportCoords,
87
}
88
89
impl std::str::FromStr for Scene {
90
type Err = String;
91
92
fn from_str(s: &str) -> Result<Self, Self::Err> {
93
let mut isit = Self::default();
94
while s.to_lowercase() != format!("{isit:?}").to_lowercase() {
95
isit = isit.next();
96
if isit == Self::default() {
97
return Err(format!("Invalid Scene name: {s}"));
98
}
99
}
100
Ok(isit)
101
}
102
}
103
104
impl Next for Scene {
105
fn next(&self) -> Self {
106
match self {
107
Scene::Image => Scene::Text,
108
Scene::Text => Scene::Grid,
109
Scene::Grid => Scene::Borders,
110
Scene::Borders => Scene::BoxShadow,
111
Scene::BoxShadow => Scene::TextWrap,
112
Scene::TextWrap => Scene::Overflow,
113
Scene::Overflow => Scene::Slice,
114
Scene::Slice => Scene::LayoutRounding,
115
Scene::LayoutRounding => Scene::LinearGradient,
116
Scene::LinearGradient => Scene::RadialGradient,
117
#[cfg(feature = "bevy_ui_debug")]
118
Scene::RadialGradient => Scene::DebugOutlines,
119
#[cfg(feature = "bevy_ui_debug")]
120
Scene::DebugOutlines => Scene::Transformations,
121
#[cfg(not(feature = "bevy_ui_debug"))]
122
Scene::RadialGradient => Scene::Transformations,
123
Scene::Transformations => Scene::ViewportCoords,
124
Scene::ViewportCoords => Scene::Image,
125
}
126
}
127
}
128
129
fn switch_scene(
130
keyboard: Res<ButtonInput<KeyCode>>,
131
scene: Res<State<Scene>>,
132
mut next_scene: ResMut<NextState<Scene>>,
133
) {
134
if keyboard.just_pressed(KeyCode::Space) {
135
info!("Switching scene");
136
next_scene.set(scene.get().next());
137
}
138
}
139
140
mod image {
141
use bevy::prelude::*;
142
143
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
144
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Image)));
145
commands.spawn((
146
ImageNode::new(asset_server.load("branding/bevy_logo_dark.png")),
147
DespawnOnExit(super::Scene::Image),
148
));
149
}
150
}
151
152
mod text {
153
use bevy::{color::palettes::css::*, prelude::*, text::FontSmoothing};
154
155
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
156
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Text)));
157
commands.spawn((
158
Text::new("Hello World."),
159
TextFont {
160
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
161
font_size: FontSize::Px(200.),
162
..default()
163
},
164
DespawnOnExit(super::Scene::Text),
165
));
166
167
commands.spawn((
168
Node {
169
left: px(100.),
170
top: px(230.),
171
..Default::default()
172
},
173
Text::new("white "),
174
TextFont {
175
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
176
..default()
177
},
178
DespawnOnExit(super::Scene::Text),
179
children![
180
(TextSpan::new("red "), TextColor(RED.into()),),
181
(TextSpan::new("green "), TextColor(GREEN.into()),),
182
(TextSpan::new("blue "), TextColor(BLUE.into()),),
183
(
184
TextSpan::new("black"),
185
TextColor(Color::BLACK),
186
TextFont {
187
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
188
..default()
189
},
190
TextBackgroundColor(Color::WHITE)
191
),
192
],
193
));
194
195
commands.spawn((
196
Node {
197
left: px(100.),
198
top: px(260.),
199
..Default::default()
200
},
201
Text::new(""),
202
TextFont {
203
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
204
..default()
205
},
206
DespawnOnExit(super::Scene::Text),
207
children![
208
(
209
TextSpan::new("white "),
210
TextFont {
211
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
212
..default()
213
}
214
),
215
(TextSpan::new("red "), TextColor(RED.into()),),
216
(TextSpan::new("green "), TextColor(GREEN.into()),),
217
(TextSpan::new("blue "), TextColor(BLUE.into()),),
218
(
219
TextSpan::new("black"),
220
TextColor(Color::BLACK),
221
TextFont {
222
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
223
..default()
224
},
225
TextBackgroundColor(Color::WHITE)
226
),
227
],
228
));
229
230
let mut top = 300.;
231
commands.spawn((
232
Node {
233
left: px(100.),
234
top: px(top),
235
..Default::default()
236
},
237
Text::new(""),
238
TextFont {
239
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
240
..default()
241
},
242
DespawnOnExit(super::Scene::Text),
243
children![
244
(TextSpan::new(""), TextColor(YELLOW.into()),),
245
TextSpan::new(""),
246
(
247
TextSpan::new("white "),
248
TextFont {
249
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
250
..default()
251
}
252
),
253
TextSpan::new(""),
254
(TextSpan::new("red "), TextColor(RED.into()),),
255
TextSpan::new(""),
256
TextSpan::new(""),
257
(TextSpan::new("green "), TextColor(GREEN.into()),),
258
(TextSpan::new(""), TextColor(YELLOW.into()),),
259
(TextSpan::new("blue "), TextColor(BLUE.into()),),
260
TextSpan::new(""),
261
(TextSpan::new(""), TextColor(YELLOW.into()),),
262
(
263
TextSpan::new("black"),
264
TextColor(Color::BLACK),
265
TextFont {
266
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
267
..default()
268
},
269
TextBackgroundColor(Color::WHITE)
270
),
271
TextSpan::new(""),
272
],
273
));
274
275
top += 35.;
276
commands.spawn((
277
Node {
278
left: px(100.),
279
top: px(top),
280
..Default::default()
281
},
282
Text::new("FiraSans_"),
283
TextFont {
284
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
285
font_size: FontSize::Px(25.),
286
..default()
287
},
288
DespawnOnExit(super::Scene::Text),
289
children![
290
(
291
TextSpan::new("MonaSans_"),
292
TextFont {
293
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
294
font_size: FontSize::Px(25.),
295
..default()
296
}
297
),
298
(
299
TextSpan::new("EBGaramond_"),
300
TextFont {
301
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
302
font_size: FontSize::Px(25.),
303
..default()
304
},
305
),
306
(
307
TextSpan::new("FiraMono"),
308
TextFont {
309
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
310
font_size: FontSize::Px(25.),
311
..default()
312
},
313
),
314
],
315
));
316
top += 35.;
317
commands.spawn((
318
Node {
319
left: px(100.),
320
top: px(top),
321
..Default::default()
322
},
323
Text::new("FiraSans "),
324
TextFont {
325
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
326
font_size: FontSize::Px(25.),
327
..default()
328
},
329
DespawnOnExit(super::Scene::Text),
330
children![
331
(
332
TextSpan::new("MonaSans "),
333
TextFont {
334
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
335
font_size: FontSize::Px(25.),
336
..default()
337
}
338
),
339
(
340
TextSpan::new("EBGaramond "),
341
TextFont {
342
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
343
font_size: FontSize::Px(25.),
344
..default()
345
},
346
),
347
(
348
TextSpan::new("FiraMono"),
349
TextFont {
350
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
351
font_size: FontSize::Px(25.),
352
..default()
353
},
354
),
355
],
356
));
357
358
top += 35.;
359
commands.spawn((
360
Node {
361
left: px(100.),
362
top: px(top),
363
..Default::default()
364
},
365
Text::new("FiraSans "),
366
TextFont {
367
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
368
font_size: FontSize::Px(25.),
369
..default()
370
},
371
DespawnOnExit(super::Scene::Text),
372
children![
373
(
374
TextSpan::new("MonaSans_"),
375
TextFont {
376
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
377
font_size: FontSize::Px(25.),
378
..default()
379
}
380
),
381
(
382
TextSpan::new("EBGaramond "),
383
TextFont {
384
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
385
font_size: FontSize::Px(25.),
386
..default()
387
},
388
),
389
(
390
TextSpan::new("FiraMono"),
391
TextFont {
392
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
393
font_size: FontSize::Px(25.),
394
..default()
395
},
396
),
397
],
398
));
399
400
top += 35.;
401
commands.spawn((
402
Node {
403
left: px(100.),
404
top: px(top),
405
..Default::default()
406
},
407
Text::new("FiraSans"),
408
TextFont {
409
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
410
font_size: FontSize::Px(25.),
411
..default()
412
},
413
DespawnOnExit(super::Scene::Text),
414
children![
415
TextSpan::new(" "),
416
(
417
TextSpan::new("MonaSans"),
418
TextFont {
419
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
420
font_size: FontSize::Px(25.),
421
..default()
422
}
423
),
424
TextSpan::new(" "),
425
(
426
TextSpan::new("EBGaramond"),
427
TextFont {
428
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
429
font_size: FontSize::Px(25.),
430
..default()
431
},
432
),
433
TextSpan::new(" "),
434
(
435
TextSpan::new("FiraMono"),
436
TextFont {
437
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
438
font_size: FontSize::Px(25.),
439
..default()
440
},
441
),
442
],
443
));
444
445
top += 35.;
446
commands.spawn((
447
Node {
448
left: px(100.),
449
top: px(top),
450
..Default::default()
451
},
452
Text::new("Fira Sans_"),
453
TextFont {
454
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
455
font_size: FontSize::Px(25.),
456
..default()
457
},
458
DespawnOnExit(super::Scene::Text),
459
children![
460
(
461
TextSpan::new("Mona Sans_"),
462
TextFont {
463
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
464
font_size: FontSize::Px(25.),
465
..default()
466
}
467
),
468
(
469
TextSpan::new("EB Garamond_"),
470
TextFont {
471
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
472
font_size: FontSize::Px(25.),
473
..default()
474
},
475
),
476
(
477
TextSpan::new("Fira Mono"),
478
TextFont {
479
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
480
font_size: FontSize::Px(25.),
481
..default()
482
},
483
),
484
],
485
));
486
487
top += 35.;
488
commands.spawn((
489
Node {
490
left: px(100.),
491
top: px(top),
492
..Default::default()
493
},
494
Text::new("FontWeight(100)_"),
495
TextFont {
496
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
497
font_size: FontSize::Px(25.),
498
weight: FontWeight(100),
499
..default()
500
},
501
DespawnOnExit(super::Scene::Text),
502
children![
503
(
504
TextSpan::new("FontWeight(500)_"),
505
TextFont {
506
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
507
font_size: FontSize::Px(25.),
508
weight: FontWeight(500),
509
..default()
510
}
511
),
512
(
513
TextSpan::new("FontWeight(900)"),
514
TextFont {
515
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
516
font_size: FontSize::Px(25.),
517
weight: FontWeight(900),
518
..default()
519
},
520
),
521
],
522
));
523
524
top += 35.;
525
commands.spawn((
526
Node {
527
left: px(100.),
528
top: px(top),
529
..Default::default()
530
},
531
Text::new("FiraSans_"),
532
TextFont {
533
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
534
font_size: FontSize::Px(25.),
535
weight: FontWeight(900),
536
..default()
537
},
538
DespawnOnExit(super::Scene::Text),
539
children![
540
(
541
TextSpan::new("MonaSans_"),
542
TextFont {
543
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
544
font_size: FontSize::Px(25.),
545
weight: FontWeight(700),
546
..default()
547
}
548
),
549
(
550
TextSpan::new("EBGaramond_"),
551
TextFont {
552
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
553
font_size: FontSize::Px(25.),
554
weight: FontWeight(500),
555
..default()
556
},
557
),
558
(
559
TextSpan::new("FiraMono"),
560
TextFont {
561
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
562
font_size: FontSize::Px(25.),
563
weight: FontWeight(300),
564
..default()
565
},
566
),
567
],
568
));
569
570
top += 35.;
571
commands.spawn((
572
Node {
573
left: px(100.),
574
top: px(top),
575
..Default::default()
576
},
577
Text::new("FiraSans\t"),
578
TextFont {
579
font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
580
font_size: FontSize::Px(25.),
581
..default()
582
},
583
DespawnOnExit(super::Scene::Text),
584
children![
585
(
586
TextSpan::new("MonaSans\t"),
587
TextFont {
588
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
589
font_size: FontSize::Px(25.),
590
..default()
591
}
592
),
593
(
594
TextSpan::new("EBGaramond\t"),
595
TextFont {
596
font: asset_server.load("fonts/EBGaramond12-Regular.otf").into(),
597
font_size: FontSize::Px(25.),
598
..default()
599
},
600
),
601
(
602
TextSpan::new("FiraMono"),
603
TextFont {
604
font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
605
font_size: FontSize::Px(25.),
606
..default()
607
},
608
),
609
],
610
));
611
612
for font_smoothing in [FontSmoothing::AntiAliased, FontSmoothing::None] {
613
top += 35.;
614
commands.spawn((
615
Node {
616
left: px(100.),
617
top: px(top),
618
..Default::default()
619
},
620
Text::new(format!("FontSmoothing::{:?}", font_smoothing)),
621
TextFont {
622
font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
623
font_size: FontSize::Px(25.),
624
font_smoothing,
625
..default()
626
},
627
DespawnOnExit(super::Scene::Text),
628
));
629
}
630
}
631
}
632
633
mod grid {
634
use bevy::{color::palettes::css::*, prelude::*};
635
636
pub fn setup(mut commands: Commands) {
637
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Grid)));
638
// Top-level grid (app frame)
639
commands.spawn((
640
Node {
641
display: Display::Grid,
642
width: percent(100),
643
height: percent(100),
644
grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
645
grid_template_rows: vec![
646
GridTrack::auto(),
647
GridTrack::flex(1.0),
648
GridTrack::px(40.),
649
],
650
..default()
651
},
652
BackgroundColor(Color::WHITE),
653
DespawnOnExit(super::Scene::Grid),
654
children![
655
// Header
656
(
657
Node {
658
display: Display::Grid,
659
grid_column: GridPlacement::span(2),
660
padding: UiRect::all(px(40)),
661
..default()
662
},
663
BackgroundColor(RED.into()),
664
),
665
// Main content grid (auto placed in row 2, column 1)
666
(
667
Node {
668
height: percent(100),
669
aspect_ratio: Some(1.0),
670
display: Display::Grid,
671
grid_template_columns: RepeatedGridTrack::flex(3, 1.0),
672
grid_template_rows: RepeatedGridTrack::flex(2, 1.0),
673
row_gap: px(12),
674
column_gap: px(12),
675
..default()
676
},
677
BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
678
children![
679
(Node::default(), BackgroundColor(ORANGE.into())),
680
(Node::default(), BackgroundColor(BISQUE.into())),
681
(Node::default(), BackgroundColor(BLUE.into())),
682
(Node::default(), BackgroundColor(CRIMSON.into())),
683
(Node::default(), BackgroundColor(AQUA.into())),
684
]
685
),
686
// Right side bar (auto placed in row 2, column 2)
687
(Node::DEFAULT, BackgroundColor(BLACK.into())),
688
],
689
));
690
}
691
}
692
693
mod borders {
694
use bevy::{color::palettes::css::*, prelude::*};
695
696
pub fn setup(mut commands: Commands) {
697
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Borders)));
698
let root = commands
699
.spawn((
700
Node {
701
flex_wrap: FlexWrap::Wrap,
702
..default()
703
},
704
DespawnOnExit(super::Scene::Borders),
705
))
706
.id();
707
708
// all the different combinations of border edges
709
let borders = [
710
UiRect::default(),
711
UiRect::all(px(20)),
712
UiRect::left(px(20)),
713
UiRect::vertical(px(20)),
714
UiRect {
715
left: px(40),
716
top: px(20),
717
..Default::default()
718
},
719
UiRect {
720
right: px(20),
721
bottom: px(30),
722
..Default::default()
723
},
724
UiRect {
725
right: px(20),
726
top: px(40),
727
bottom: px(20),
728
..Default::default()
729
},
730
UiRect {
731
left: px(20),
732
top: px(20),
733
bottom: px(20),
734
..Default::default()
735
},
736
UiRect {
737
left: px(20),
738
right: px(20),
739
bottom: px(40),
740
..Default::default()
741
},
742
];
743
744
let non_zero = |x, y| x != px(0) && y != px(0);
745
let border_size = |x, y| if non_zero(x, y) { f32::MAX } else { 0. };
746
747
for border in borders {
748
for rounded in [true, false] {
749
let border_node = commands
750
.spawn((
751
Node {
752
width: px(100),
753
height: px(100),
754
border,
755
margin: UiRect::all(px(30)),
756
align_items: AlignItems::Center,
757
justify_content: JustifyContent::Center,
758
border_radius: if rounded {
759
BorderRadius::px(
760
border_size(border.left, border.top),
761
border_size(border.right, border.top),
762
border_size(border.right, border.bottom),
763
border_size(border.left, border.bottom),
764
)
765
} else {
766
BorderRadius::ZERO
767
},
768
..default()
769
},
770
BackgroundColor(MAROON.into()),
771
BorderColor::all(RED),
772
Outline {
773
width: px(10),
774
offset: px(10),
775
color: Color::WHITE,
776
},
777
))
778
.id();
779
780
commands.entity(root).add_child(border_node);
781
}
782
}
783
}
784
}
785
786
mod box_shadow {
787
use bevy::{color::palettes::css::*, prelude::*};
788
789
pub fn setup(mut commands: Commands) {
790
commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxShadow)));
791
792
commands
793
.spawn((
794
Node {
795
width: percent(100),
796
height: percent(100),
797
padding: UiRect::all(px(30)),
798
column_gap: px(200),
799
flex_wrap: FlexWrap::Wrap,
800
..default()
801
},
802
BackgroundColor(GREEN.into()),
803
DespawnOnExit(super::Scene::BoxShadow),
804
))
805
.with_children(|commands| {
806
let example_nodes = [
807
(
808
Vec2::splat(100.),
809
Vec2::ZERO,
810
10.,
811
0.,
812
BorderRadius::bottom_right(px(10)),
813
),
814
(Vec2::new(200., 50.), Vec2::ZERO, 10., 0., BorderRadius::MAX),
815
(
816
Vec2::new(100., 50.),
817
Vec2::ZERO,
818
10.,
819
10.,
820
BorderRadius::ZERO,
821
),
822
(
823
Vec2::splat(100.),
824
Vec2::splat(20.),
825
10.,
826
10.,
827
BorderRadius::bottom_right(px(10)),
828
),
829
(
830
Vec2::splat(100.),
831
Vec2::splat(50.),
832
0.,
833
10.,
834
BorderRadius::ZERO,
835
),
836
(
837
Vec2::new(50., 100.),
838
Vec2::splat(10.),
839
0.,
840
10.,
841
BorderRadius::MAX,
842
),
843
];
844
845
for (size, offset, spread, blur, border_radius) in example_nodes {
846
commands.spawn((
847
Node {
848
width: px(size.x),
849
height: px(size.y),
850
border: UiRect::all(px(2)),
851
border_radius,
852
..default()
853
},
854
BorderColor::all(WHITE),
855
BackgroundColor(BLUE.into()),
856
BoxShadow::new(
857
Color::BLACK.with_alpha(0.9),
858
percent(offset.x),
859
percent(offset.y),
860
percent(spread),
861
px(blur),
862
),
863
));
864
}
865
});
866
}
867
}
868
869
mod text_wrap {
870
use bevy::prelude::*;
871
872
pub fn setup(mut commands: Commands) {
873
commands.spawn((Camera2d, DespawnOnExit(super::Scene::TextWrap)));
874
875
let root = commands
876
.spawn((
877
Node {
878
flex_direction: FlexDirection::Column,
879
width: px(200),
880
height: percent(100),
881
overflow: Overflow::clip_x(),
882
..default()
883
},
884
BackgroundColor(Color::BLACK),
885
DespawnOnExit(super::Scene::TextWrap),
886
))
887
.id();
888
889
for linebreak in [
890
LineBreak::AnyCharacter,
891
LineBreak::WordBoundary,
892
LineBreak::WordOrCharacter,
893
LineBreak::NoWrap,
894
] {
895
let messages = [
896
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string(),
897
"pneumonoultramicroscopicsilicovolcanoconiosis".to_string(),
898
];
899
900
for (j, message) in messages.into_iter().enumerate() {
901
commands.entity(root).with_child((
902
Text(message.clone()),
903
TextLayout::new(Justify::Left, linebreak),
904
BackgroundColor(Color::srgb(0.8 - j as f32 * 0.3, 0., 0.)),
905
));
906
}
907
}
908
}
909
}
910
911
mod overflow {
912
use bevy::{color::palettes::css::*, prelude::*};
913
914
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
915
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Overflow)));
916
let image = asset_server.load("branding/icon.png");
917
918
commands
919
.spawn((
920
Node {
921
width: percent(100),
922
height: percent(100),
923
align_items: AlignItems::Center,
924
justify_content: JustifyContent::SpaceAround,
925
..Default::default()
926
},
927
BackgroundColor(BLUE.into()),
928
DespawnOnExit(super::Scene::Overflow),
929
))
930
.with_children(|parent| {
931
for overflow in [
932
Overflow::visible(),
933
Overflow::clip_x(),
934
Overflow::clip_y(),
935
Overflow::clip(),
936
] {
937
parent
938
.spawn((
939
Node {
940
width: px(100),
941
height: px(100),
942
padding: UiRect {
943
left: px(25),
944
top: px(25),
945
..Default::default()
946
},
947
border: UiRect::all(px(5)),
948
overflow,
949
..default()
950
},
951
BorderColor::all(RED),
952
BackgroundColor(Color::WHITE),
953
))
954
.with_children(|parent| {
955
parent.spawn((
956
ImageNode::new(image.clone()),
957
Node {
958
min_width: px(100),
959
min_height: px(100),
960
..default()
961
},
962
Interaction::default(),
963
Outline {
964
width: px(2),
965
offset: px(2),
966
color: Color::NONE,
967
},
968
));
969
});
970
}
971
});
972
}
973
}
974
975
mod slice {
976
use bevy::prelude::*;
977
978
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
979
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Slice)));
980
let image = asset_server.load("textures/fantasy_ui_borders/numbered_slices.png");
981
982
let slicer = TextureSlicer {
983
border: BorderRect::all(16.0),
984
center_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
985
sides_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
986
..default()
987
};
988
commands
989
.spawn((
990
Node {
991
width: percent(100),
992
height: percent(100),
993
align_items: AlignItems::Center,
994
justify_content: JustifyContent::SpaceAround,
995
..default()
996
},
997
DespawnOnExit(super::Scene::Slice),
998
))
999
.with_children(|parent| {
1000
for [w, h] in [[150.0, 150.0], [300.0, 150.0], [150.0, 300.0]] {
1001
parent.spawn((
1002
Button,
1003
ImageNode {
1004
image: image.clone(),
1005
image_mode: NodeImageMode::Sliced(slicer.clone()),
1006
..default()
1007
},
1008
Node {
1009
width: px(w),
1010
height: px(h),
1011
..default()
1012
},
1013
));
1014
}
1015
1016
parent.spawn((
1017
ImageNode {
1018
image: asset_server
1019
.load("textures/fantasy_ui_borders/panel-border-010.png"),
1020
image_mode: NodeImageMode::Sliced(TextureSlicer {
1021
border: BorderRect::all(22.0),
1022
center_scale_mode: SliceScaleMode::Stretch,
1023
sides_scale_mode: SliceScaleMode::Stretch,
1024
max_corner_scale: 1.0,
1025
}),
1026
..Default::default()
1027
},
1028
Node {
1029
width: px(100),
1030
height: px(100),
1031
..default()
1032
},
1033
BackgroundColor(bevy::color::palettes::css::NAVY.into()),
1034
));
1035
});
1036
}
1037
}
1038
1039
mod layout_rounding {
1040
use bevy::{color::palettes::css::*, prelude::*};
1041
1042
pub fn setup(mut commands: Commands) {
1043
commands.spawn((Camera2d, DespawnOnExit(super::Scene::LayoutRounding)));
1044
1045
commands
1046
.spawn((
1047
Node {
1048
display: Display::Grid,
1049
width: percent(100),
1050
height: percent(100),
1051
grid_template_rows: vec![RepeatedGridTrack::fr(10, 1.)],
1052
..Default::default()
1053
},
1054
BackgroundColor(Color::WHITE),
1055
DespawnOnExit(super::Scene::LayoutRounding),
1056
))
1057
.with_children(|commands| {
1058
for i in 2..12 {
1059
commands
1060
.spawn(Node {
1061
display: Display::Grid,
1062
grid_template_columns: vec![RepeatedGridTrack::fr(i, 1.)],
1063
..Default::default()
1064
})
1065
.with_children(|commands| {
1066
for _ in 0..i {
1067
commands.spawn((
1068
Node {
1069
border: UiRect::all(px(5)),
1070
..Default::default()
1071
},
1072
BackgroundColor(MAROON.into()),
1073
BorderColor::all(DARK_BLUE),
1074
));
1075
}
1076
});
1077
}
1078
});
1079
}
1080
}
1081
1082
mod linear_gradient {
1083
use bevy::camera::Camera2d;
1084
use bevy::color::palettes::css::BLUE;
1085
use bevy::color::palettes::css::LIME;
1086
use bevy::color::palettes::css::RED;
1087
use bevy::color::palettes::css::YELLOW;
1088
use bevy::color::Color;
1089
use bevy::ecs::prelude::*;
1090
use bevy::state::state_scoped::DespawnOnExit;
1091
use bevy::text::TextFont;
1092
use bevy::ui::AlignItems;
1093
use bevy::ui::BackgroundGradient;
1094
use bevy::ui::ColorStop;
1095
use bevy::ui::GridPlacement;
1096
use bevy::ui::InterpolationColorSpace;
1097
use bevy::ui::JustifyContent;
1098
use bevy::ui::LinearGradient;
1099
use bevy::ui::Node;
1100
use bevy::ui::PositionType;
1101
use bevy::utils::default;
1102
1103
pub fn setup(mut commands: Commands) {
1104
commands.spawn((Camera2d, DespawnOnExit(super::Scene::LinearGradient)));
1105
commands
1106
.spawn((
1107
Node {
1108
flex_direction: bevy::ui::FlexDirection::Column,
1109
width: bevy::ui::percent(100),
1110
height: bevy::ui::percent(100),
1111
justify_content: JustifyContent::Center,
1112
align_items: AlignItems::Center,
1113
row_gap: bevy::ui::px(5),
1114
..default()
1115
},
1116
DespawnOnExit(super::Scene::LinearGradient),
1117
))
1118
.with_children(|commands| {
1119
let mut i = 0;
1120
commands
1121
.spawn(Node {
1122
display: bevy::ui::Display::Grid,
1123
row_gap: bevy::ui::px(4),
1124
column_gap: bevy::ui::px(4),
1125
..Default::default()
1126
})
1127
.with_children(|commands| {
1128
for stops in [
1129
vec![ColorStop::auto(RED), ColorStop::auto(YELLOW)],
1130
vec![
1131
ColorStop::auto(Color::BLACK),
1132
ColorStop::auto(RED),
1133
ColorStop::auto(Color::WHITE),
1134
],
1135
vec![
1136
Color::hsl(180.71191, 0.0, 0.3137255).into(),
1137
Color::hsl(180.71191, 0.5, 0.3137255).into(),
1138
Color::hsl(180.71191, 1.0, 0.3137255).into(),
1139
],
1140
vec![
1141
Color::hsl(180.71191, 0.825, 0.0).into(),
1142
Color::hsl(180.71191, 0.825, 0.5).into(),
1143
Color::hsl(180.71191, 0.825, 1.0).into(),
1144
],
1145
vec![
1146
Color::hsl(0.0 + 0.0001, 1.0, 0.5).into(),
1147
Color::hsl(180.0, 1.0, 0.5).into(),
1148
Color::hsl(360.0 - 0.0001, 1.0, 0.5).into(),
1149
],
1150
vec![
1151
Color::WHITE.into(),
1152
RED.into(),
1153
LIME.into(),
1154
BLUE.into(),
1155
Color::BLACK.into(),
1156
],
1157
] {
1158
for color_space in [
1159
InterpolationColorSpace::LinearRgba,
1160
InterpolationColorSpace::Srgba,
1161
InterpolationColorSpace::Oklaba,
1162
InterpolationColorSpace::Oklcha,
1163
InterpolationColorSpace::OklchaLong,
1164
InterpolationColorSpace::Hsla,
1165
InterpolationColorSpace::HslaLong,
1166
InterpolationColorSpace::Hsva,
1167
InterpolationColorSpace::HsvaLong,
1168
] {
1169
let row = i % 18 + 1;
1170
let column = i / 18 + 1;
1171
i += 1;
1172
1173
commands.spawn((
1174
Node {
1175
grid_row: GridPlacement::start(row as i16 + 1),
1176
grid_column: GridPlacement::start(column as i16 + 1),
1177
justify_content: JustifyContent::SpaceEvenly,
1178
..Default::default()
1179
},
1180
children![(
1181
Node {
1182
height: bevy::ui::px(30),
1183
width: bevy::ui::px(300),
1184
justify_content: JustifyContent::Center,
1185
..Default::default()
1186
},
1187
BackgroundGradient::from(LinearGradient {
1188
color_space,
1189
angle: LinearGradient::TO_RIGHT,
1190
stops: stops.clone(),
1191
}),
1192
children![
1193
Node {
1194
position_type: PositionType::Absolute,
1195
..default()
1196
},
1197
TextFont::from_font_size(10.),
1198
bevy::ui::widget::Text(format!("{color_space:?}")),
1199
]
1200
)],
1201
));
1202
}
1203
}
1204
});
1205
});
1206
}
1207
}
1208
1209
mod radial_gradient {
1210
use bevy::color::palettes::css::RED;
1211
use bevy::color::palettes::tailwind::GRAY_700;
1212
use bevy::prelude::*;
1213
use bevy::ui::ColorStop;
1214
1215
const CELL_SIZE: f32 = 80.;
1216
const GAP: f32 = 10.;
1217
1218
pub fn setup(mut commands: Commands) {
1219
let color_stops = vec![
1220
ColorStop::new(Color::BLACK, px(5)),
1221
ColorStop::new(Color::WHITE, px(5)),
1222
ColorStop::new(Color::WHITE, percent(100)),
1223
ColorStop::auto(RED),
1224
];
1225
1226
commands.spawn((Camera2d, DespawnOnExit(super::Scene::RadialGradient)));
1227
commands
1228
.spawn((
1229
Node {
1230
width: percent(100),
1231
height: percent(100),
1232
display: Display::Grid,
1233
align_items: AlignItems::Start,
1234
grid_template_columns: vec![RepeatedGridTrack::px(
1235
GridTrackRepetition::AutoFill,
1236
CELL_SIZE,
1237
)],
1238
grid_auto_flow: GridAutoFlow::Row,
1239
row_gap: px(GAP),
1240
column_gap: px(GAP),
1241
padding: UiRect::all(px(GAP)),
1242
..default()
1243
},
1244
DespawnOnExit(super::Scene::RadialGradient),
1245
))
1246
.with_children(|commands| {
1247
for (shape, shape_label) in [
1248
(RadialGradientShape::ClosestSide, "ClosestSide"),
1249
(RadialGradientShape::FarthestSide, "FarthestSide"),
1250
(RadialGradientShape::Circle(percent(55)), "Circle(55%)"),
1251
(RadialGradientShape::FarthestCorner, "FarthestCorner"),
1252
] {
1253
for (position, position_label) in [
1254
(UiPosition::TOP_LEFT, "TOP_LEFT"),
1255
(UiPosition::LEFT, "LEFT"),
1256
(UiPosition::BOTTOM_LEFT, "BOTTOM_LEFT"),
1257
(UiPosition::TOP, "TOP"),
1258
(UiPosition::CENTER, "CENTER"),
1259
(UiPosition::BOTTOM, "BOTTOM"),
1260
(UiPosition::TOP_RIGHT, "TOP_RIGHT"),
1261
(UiPosition::RIGHT, "RIGHT"),
1262
(UiPosition::BOTTOM_RIGHT, "BOTTOM_RIGHT"),
1263
] {
1264
for (w, h) in [(CELL_SIZE, CELL_SIZE), (CELL_SIZE, CELL_SIZE / 2.)] {
1265
commands
1266
.spawn((
1267
BackgroundColor(GRAY_700.into()),
1268
Node {
1269
display: Display::Grid,
1270
width: px(CELL_SIZE),
1271
..Default::default()
1272
},
1273
))
1274
.with_children(|commands| {
1275
commands.spawn((
1276
Node {
1277
margin: UiRect::all(px(2)),
1278
..default()
1279
},
1280
Text(format!("{shape_label}\n{position_label}")),
1281
TextFont::from_font_size(9.),
1282
));
1283
commands.spawn((
1284
Node {
1285
width: px(w),
1286
height: px(h),
1287
..default()
1288
},
1289
BackgroundGradient::from(RadialGradient {
1290
stops: color_stops.clone(),
1291
position,
1292
shape,
1293
..default()
1294
}),
1295
));
1296
});
1297
}
1298
}
1299
}
1300
});
1301
}
1302
}
1303
1304
mod transformations {
1305
use bevy::{color::palettes::css::*, prelude::*};
1306
1307
pub fn setup(mut commands: Commands) {
1308
commands.spawn((Camera2d, DespawnOnExit(super::Scene::Transformations)));
1309
commands
1310
.spawn((
1311
Node {
1312
width: percent(100),
1313
height: percent(100),
1314
display: Display::Block,
1315
..default()
1316
},
1317
DespawnOnExit(super::Scene::Transformations),
1318
))
1319
.with_children(|parent| {
1320
for (transformation, label, background) in [
1321
(
1322
UiTransform::from_rotation(Rot2::degrees(45.)),
1323
"Rotate 45 degrees",
1324
RED,
1325
),
1326
(
1327
UiTransform::from_scale(Vec2::new(2., 0.5)),
1328
"Scale 2.x 0.5y",
1329
GREEN,
1330
),
1331
(
1332
UiTransform::from_translation(Val2::px(-50., 50.)),
1333
"Translate -50px x +50px y",
1334
BLUE,
1335
),
1336
(
1337
UiTransform {
1338
translation: Val2::px(50., 0.),
1339
scale: Vec2::new(-1., 1.),
1340
rotation: Rot2::degrees(30.),
1341
},
1342
"T 50px x\nS -1.x (refl)\nR 30deg",
1343
DARK_CYAN,
1344
),
1345
] {
1346
parent
1347
.spawn((Node {
1348
width: percent(100),
1349
margin: UiRect {
1350
top: px(50),
1351
bottom: px(50),
1352
..default()
1353
},
1354
align_items: AlignItems::Center,
1355
justify_content: JustifyContent::SpaceAround,
1356
..default()
1357
},))
1358
.with_children(|row| {
1359
row.spawn((
1360
Text::new("Before Tf"),
1361
Node {
1362
width: px(100),
1363
height: px(100),
1364
border_radius: BorderRadius::bottom_right(px(25.)),
1365
..default()
1366
},
1367
BackgroundColor(background.into()),
1368
TextFont::default(),
1369
));
1370
row.spawn((
1371
Text::new(label),
1372
Node {
1373
width: px(100),
1374
height: px(100),
1375
border_radius: BorderRadius::bottom_right(px(25.)),
1376
..default()
1377
},
1378
BackgroundColor(background.into()),
1379
transformation,
1380
TextFont::default(),
1381
));
1382
});
1383
}
1384
});
1385
}
1386
}
1387
1388
#[cfg(feature = "bevy_ui_debug")]
1389
mod debug_outlines {
1390
use bevy::{
1391
color::palettes::css::{BLUE, GRAY, RED},
1392
prelude::*,
1393
ui_render::UiDebugOptions,
1394
};
1395
1396
pub fn setup(mut commands: Commands, mut debug_options: ResMut<UiDebugOptions>) {
1397
debug_options.enabled = true;
1398
debug_options.line_width = 5.;
1399
debug_options.line_color_override = Some(LinearRgba::GREEN);
1400
debug_options.show_hidden = true;
1401
debug_options.show_clipped = true;
1402
commands.spawn((Camera2d, DespawnOnExit(super::Scene::DebugOutlines)));
1403
commands
1404
.spawn((
1405
Node {
1406
width: percent(100),
1407
height: percent(50),
1408
align_items: AlignItems::Center,
1409
justify_content: JustifyContent::SpaceAround,
1410
..default()
1411
},
1412
DespawnOnExit(super::Scene::DebugOutlines),
1413
))
1414
.with_children(|parent| {
1415
parent.spawn((
1416
Node {
1417
width: px(100),
1418
height: px(100),
1419
..default()
1420
},
1421
BackgroundColor(GRAY.into()),
1422
UiTransform::from_rotation(Rot2::degrees(45.)),
1423
));
1424
1425
parent.spawn((Text::new("Regular Text"), TextFont::default()));
1426
1427
parent.spawn((
1428
Node {
1429
width: px(100),
1430
height: px(100),
1431
..default()
1432
},
1433
Text::new("Invisible"),
1434
BackgroundColor(GRAY.into()),
1435
TextFont::default(),
1436
Visibility::Hidden,
1437
));
1438
1439
parent
1440
.spawn((
1441
Node {
1442
width: px(100),
1443
height: px(100),
1444
padding: UiRect {
1445
left: px(25),
1446
top: px(25),
1447
..Default::default()
1448
},
1449
overflow: Overflow::clip(),
1450
..default()
1451
},
1452
BackgroundColor(RED.into()),
1453
))
1454
.with_children(|child| {
1455
child.spawn((
1456
Node {
1457
min_width: px(100),
1458
min_height: px(100),
1459
..default()
1460
},
1461
BackgroundColor(BLUE.into()),
1462
));
1463
});
1464
});
1465
1466
commands
1467
.spawn((
1468
Node {
1469
width: percent(100),
1470
height: percent(50),
1471
top: percent(50),
1472
align_items: AlignItems::Center,
1473
justify_content: JustifyContent::SpaceAround,
1474
..default()
1475
},
1476
DespawnOnExit(super::Scene::DebugOutlines),
1477
))
1478
.with_children(|parent| {
1479
parent.spawn((
1480
Node {
1481
width: px(200),
1482
height: px(200),
1483
border: UiRect {
1484
top: px(10),
1485
bottom: px(20),
1486
left: px(30),
1487
right: px(40),
1488
},
1489
border_radius: BorderRadius::bottom_right(px(10)),
1490
padding: UiRect {
1491
top: px(40),
1492
bottom: px(30),
1493
left: px(20),
1494
right: px(10),
1495
},
1496
..default()
1497
},
1498
children![(
1499
Text::new("border padding content outlines"),
1500
TextFont::default(),
1501
UiDebugOptions {
1502
enabled: false,
1503
..default()
1504
}
1505
)],
1506
UiDebugOptions {
1507
outline_border_box: true,
1508
outline_padding_box: true,
1509
outline_content_box: true,
1510
ignore_border_radius: false,
1511
..*debug_options
1512
},
1513
));
1514
1515
// Vertical scrollbar (non-functional)
1516
parent.spawn((
1517
Node {
1518
flex_direction: FlexDirection::Column,
1519
width: px(90),
1520
height: px(230),
1521
overflow: Overflow::scroll_y(),
1522
scrollbar_width: 20.,
1523
..default()
1524
},
1525
ScrollPosition(Vec2::new(180., 180.)),
1526
UiDebugOptions {
1527
line_width: 3.,
1528
outline_scrollbars: true,
1529
show_hidden: false,
1530
show_clipped: false,
1531
..*debug_options
1532
},
1533
Children::spawn(SpawnIter((0..20).map(move |i| {
1534
(
1535
Node::default(),
1536
children![(
1537
Text(format!("Item {i}")),
1538
UiDebugOptions {
1539
enabled: false,
1540
..default()
1541
}
1542
)],
1543
UiDebugOptions {
1544
enabled: false,
1545
..default()
1546
},
1547
)
1548
}))),
1549
));
1550
1551
// Horizontal scrollbar (non-functional)
1552
parent.spawn((
1553
Node {
1554
flex_direction: FlexDirection::Row,
1555
width: px(156),
1556
height: px(70),
1557
overflow: Overflow::scroll_x(),
1558
scrollbar_width: 10.,
1559
..default()
1560
},
1561
UiDebugOptions {
1562
line_width: 3.,
1563
outline_scrollbars: true,
1564
show_hidden: false,
1565
show_clipped: false,
1566
..*debug_options
1567
},
1568
Children::spawn(SpawnIter((0..20).map(move |i| {
1569
(
1570
Node::default(),
1571
children![(
1572
Text(format!("Item {i}")),
1573
UiDebugOptions {
1574
enabled: false,
1575
..default()
1576
}
1577
)],
1578
UiDebugOptions {
1579
enabled: false,
1580
..default()
1581
},
1582
)
1583
}))),
1584
));
1585
1586
// bi-directional scrollbar (non-functional)
1587
parent.spawn((
1588
Node {
1589
flex_direction: FlexDirection::Column,
1590
width: px(230),
1591
height: px(125),
1592
overflow: Overflow::scroll(),
1593
scrollbar_width: 20.,
1594
..default()
1595
},
1596
ScrollPosition(Vec2::new(300., 0.)),
1597
UiDebugOptions {
1598
line_width: 3.,
1599
outline_scrollbars: true,
1600
show_hidden: false,
1601
show_clipped: false,
1602
..*debug_options
1603
},
1604
Children::spawn(SpawnIter((0..6).map(move |i| {
1605
(
1606
Node {
1607
flex_direction: FlexDirection::Row,
1608
..default()
1609
},
1610
Children::spawn(SpawnIter((0..6).map({
1611
move |j| {
1612
(
1613
Text(format!("Item {}", (i * 5) + j)),
1614
UiDebugOptions {
1615
enabled: false,
1616
..default()
1617
},
1618
)
1619
}
1620
}))),
1621
UiDebugOptions {
1622
enabled: false,
1623
..default()
1624
},
1625
)
1626
}))),
1627
));
1628
});
1629
}
1630
1631
pub fn teardown(mut debug_options: ResMut<UiDebugOptions>) {
1632
*debug_options = UiDebugOptions::default();
1633
}
1634
}
1635
1636
mod viewport_coords {
1637
use bevy::{color::palettes::css::*, prelude::*};
1638
1639
const PALETTE: [Srgba; 9] = [RED, WHITE, BEIGE, AQUA, CRIMSON, NAVY, AZURE, LIME, BLACK];
1640
1641
pub fn setup(mut commands: Commands) {
1642
commands.spawn((Camera2d, DespawnOnExit(super::Scene::ViewportCoords)));
1643
commands
1644
.spawn((
1645
Node {
1646
width: vw(100),
1647
height: vh(100),
1648
border: UiRect::axes(vw(5), vh(5)),
1649
flex_wrap: FlexWrap::Wrap,
1650
..default()
1651
},
1652
BorderColor::all(PALETTE[0]),
1653
DespawnOnExit(super::Scene::ViewportCoords),
1654
))
1655
.with_children(|builder| {
1656
builder.spawn((
1657
Node {
1658
width: vw(30),
1659
height: vh(30),
1660
border: UiRect::all(vmin(5)),
1661
..default()
1662
},
1663
BackgroundColor(PALETTE[1].into()),
1664
BorderColor::all(PALETTE[8]),
1665
));
1666
1667
builder.spawn((
1668
Node {
1669
width: vw(60),
1670
height: vh(30),
1671
..default()
1672
},
1673
BackgroundColor(PALETTE[2].into()),
1674
));
1675
1676
builder.spawn((
1677
Node {
1678
width: vw(45),
1679
height: vh(30),
1680
border: UiRect::left(vmax(45. / 2.)),
1681
..default()
1682
},
1683
BackgroundColor(PALETTE[3].into()),
1684
BorderColor::all(PALETTE[7]),
1685
));
1686
1687
builder.spawn((
1688
Node {
1689
width: vw(45),
1690
height: vh(30),
1691
border: UiRect::right(vmax(45. / 2.)),
1692
..default()
1693
},
1694
BackgroundColor(PALETTE[4].into()),
1695
BorderColor::all(PALETTE[7]),
1696
));
1697
1698
builder.spawn((
1699
Node {
1700
width: vw(60),
1701
height: vh(30),
1702
..default()
1703
},
1704
BackgroundColor(PALETTE[5].into()),
1705
));
1706
1707
builder.spawn((
1708
Node {
1709
width: vw(30),
1710
height: vh(30),
1711
border: UiRect::all(vmin(5)),
1712
..default()
1713
},
1714
BackgroundColor(PALETTE[6].into()),
1715
BorderColor::all(PALETTE[8]),
1716
));
1717
});
1718
}
1719
}
1720
1721