1mod helpers;
6
7use argh::FromArgs;
8use bevy::prelude::*;
9
10use helpers::Next;
11
12#[derive(FromArgs)]
13pub struct Args {
15 #[argh(positional)]
16 scene: Option<Scene>,
17}
18
19fn 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 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::ImageMeasure), image_measure::setup)
38 .add_systems(OnEnter(Scene::Text), text::setup)
39 .add_systems(OnEnter(Scene::Grid), grid::setup)
40 .add_systems(OnEnter(Scene::Borders), borders::setup)
41 .add_systems(OnEnter(Scene::BoxShadow), box_shadow::setup)
42 .add_systems(OnEnter(Scene::TextWrap), text_wrap::setup)
43 .add_systems(OnEnter(Scene::Overflow), overflow::setup)
44 .add_systems(OnEnter(Scene::Slice), slice::setup)
45 .add_systems(OnEnter(Scene::LayoutRounding), layout_rounding::setup)
46 .add_systems(OnEnter(Scene::LinearGradient), linear_gradient::setup)
47 .add_systems(OnEnter(Scene::RadialGradient), radial_gradient::setup)
48 .add_systems(OnEnter(Scene::Transformations), transformations::setup)
49 .add_systems(OnEnter(Scene::ViewportCoords), viewport_coords::setup)
50 .add_systems(OnEnter(Scene::OuterColor), outer_color::setup)
51 .add_systems(OnEnter(Scene::BoxedContent), boxed_content::setup)
52 .add_systems(OnEnter(Scene::EditableText), editable_text::setup)
53 .add_systems(Update, switch_scene);
54
55 match args.scene {
56 None => app.init_state::<Scene>(),
57 Some(scene) => app.insert_state(scene),
58 };
59
60 #[cfg(feature = "bevy_ui_debug")]
61 {
62 app.add_systems(OnEnter(Scene::DebugOutlines), debug_outlines::setup);
63 app.add_systems(OnExit(Scene::DebugOutlines), debug_outlines::teardown);
64 }
65
66 #[cfg(feature = "bevy_ci_testing")]
67 app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
68
69 app.run();
70}
71
72#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
73#[states(scoped_entities)]
74enum Scene {
75 #[default]
76 Image,
77 ImageMeasure,
78 Text,
79 Grid,
80 Borders,
81 BoxShadow,
82 TextWrap,
83 Overflow,
84 Slice,
85 LayoutRounding,
86 LinearGradient,
87 RadialGradient,
88 Transformations,
89 #[cfg(feature = "bevy_ui_debug")]
90 DebugOutlines,
91 ViewportCoords,
92 OuterColor,
93 BoxedContent,
94 EditableText,
95}
96
97impl std::str::FromStr for Scene {
98 type Err = String;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 let mut isit = Self::default();
102 while s.to_lowercase() != format!("{isit:?}").to_lowercase() {
103 isit = isit.next();
104 if isit == Self::default() {
105 return Err(format!("Invalid Scene name: {s}"));
106 }
107 }
108 Ok(isit)
109 }
110}
111
112impl Next for Scene {
113 fn next(&self) -> Self {
114 match self {
115 Scene::Image => Scene::ImageMeasure,
116 Scene::ImageMeasure => Scene::Text,
117 Scene::Text => Scene::Grid,
118 Scene::Grid => Scene::Borders,
119 Scene::Borders => Scene::BoxShadow,
120 Scene::BoxShadow => Scene::TextWrap,
121 Scene::TextWrap => Scene::Overflow,
122 Scene::Overflow => Scene::Slice,
123 Scene::Slice => Scene::LayoutRounding,
124 Scene::LayoutRounding => Scene::LinearGradient,
125 Scene::LinearGradient => Scene::RadialGradient,
126 #[cfg(feature = "bevy_ui_debug")]
127 Scene::RadialGradient => Scene::DebugOutlines,
128 #[cfg(feature = "bevy_ui_debug")]
129 Scene::DebugOutlines => Scene::Transformations,
130 #[cfg(not(feature = "bevy_ui_debug"))]
131 Scene::RadialGradient => Scene::Transformations,
132 Scene::Transformations => Scene::ViewportCoords,
133 Scene::ViewportCoords => Scene::OuterColor,
134 Scene::OuterColor => Scene::BoxedContent,
135 Scene::BoxedContent => Scene::EditableText,
136 Scene::EditableText => Scene::Image,
137 }
138 }
139}
140
141fn switch_scene(
142 keyboard: Res<ButtonInput<KeyCode>>,
143 scene: Res<State<Scene>>,
144 mut next_scene: ResMut<NextState<Scene>>,
145) {
146 if keyboard.just_pressed(KeyCode::Space) {
147 info!("Switching scene");
148 next_scene.set(scene.get().next());
149 }
150}
151
152mod image {
153 use bevy::color::palettes::css::DARK_GREY;
154 use bevy::prelude::*;
155
156 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
157 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Image)));
158 commands
159 .spawn(Node {
160 width: percent(100.),
161 height: percent(100.),
162 flex_direction: FlexDirection::Column,
163 justify_content: JustifyContent::SpaceAround,
164 align_items: AlignItems::Stretch,
165 ..default()
166 })
167 .with_children(|parent| {
168 for [b, p] in [[0, 0], [10, 0], [0, 10], [10, 10]] {
169 for image_path in ["branding/icon.png", "branding/bevy_logo_dark.png"] {
170 parent
171 .spawn(Node {
172 justify_content: JustifyContent::SpaceAround,
173 align_items: AlignItems::Center,
174 ..default()
175 })
176 .with_children(|parent| {
177 for visual_box in [
178 VisualBox::BorderBox,
179 VisualBox::PaddingBox,
180 VisualBox::ContentBox,
181 ] {
182 parent.spawn((
183 ImageNode {
184 image: asset_server.load(image_path),
185 visual_box,
186 ..default()
187 },
188 Node {
189 border: px(b).all(),
190 padding: px(p).all(),
191 width: px(100.),
192 ..default()
193 },
194 DespawnOnExit(super::Scene::Image),
195 Outline {
196 color: DARK_GREY.into(),
197 width: px(2.),
198 ..default()
199 },
200 ));
201 }
202 });
203 }
204 }
205 });
206 }
207}
208
209mod image_measure {
210 use bevy::{
211 color::palettes::css::{GREEN, RED},
212 prelude::*,
213 };
214
215 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
216 commands.spawn((Camera2d, DespawnOnExit(super::Scene::ImageMeasure)));
217 commands.spawn((
218 Node {
219 margin: auto().all(),
220 column_gap: px(5.),
221 ..Default::default()
222 },
223 DespawnOnExit(super::Scene::ImageMeasure),
224 children![
225 (
226 Node {
227 width: vmin(20.),
228 ..default()
229 },
230 children![(
231 Node {
232 position_type: PositionType::Absolute,
233 width: vmin(20.),
234 ..default()
235 },
236 BackgroundColor(GREEN.into()),
237 ImageNode::new(asset_server.load("branding/icon.png")),
238 )],
239 ),
240 (
241 Node {
242 width: vmin(20.),
243 ..default()
244 },
245 children![(
246 Node {
247 position_type: PositionType::Absolute,
248 width: vmin(20.),
249 border: px(8.).all(),
250 ..default()
251 },
252 BorderColor::all(RED),
253 BackgroundColor(GREEN.into()),
254 ImageNode::new(asset_server.load("branding/icon.png")),
255 )],
256 ),
257 (
258 Node {
259 width: vmin(20.),
260 ..default()
261 },
262 children![(
263 Node {
264 position_type: PositionType::Absolute,
265 width: vmin(20.),
266 border: px(8.).all(),
267 padding: px(4.).all(),
268 ..default()
269 },
270 BorderColor::all(RED),
271 BackgroundColor(GREEN.into()),
272 ImageNode::new(asset_server.load("branding/icon.png")),
273 )],
274 ),
275 (
276 Node {
277 width: vmin(20.),
278 ..default()
279 },
280 children![(
281 Node {
282 position_type: PositionType::Absolute,
283 width: vmin(20.),
284 border: UiRect::px(4.0, 12.0, 8.0, 16.0),
285 ..default()
286 },
287 BorderColor::all(RED),
288 BackgroundColor(GREEN.into()),
289 ImageNode::new(asset_server.load("branding/icon.png")),
290 )],
291 ),
292 (
293 Node {
294 width: vmin(20.),
295 ..default()
296 },
297 children![(
298 Node {
299 position_type: PositionType::Absolute,
300 width: vmin(20.),
301 border: UiRect::px(4.0, 12.0, 8.0, 16.0),
302 padding: UiRect::axes(px(10.), px(0.)),
303 ..default()
304 },
305 BorderColor::all(RED),
306 BackgroundColor(GREEN.into()),
307 ImageNode::new(asset_server.load("branding/icon.png")),
308 )],
309 ),
310 (
311 Node {
312 width: vmin(20.),
313 ..default()
314 },
315 children![(
316 Node {
317 position_type: PositionType::Absolute,
318 width: vmin(20.),
319 border: UiRect::px(4.0, 12.0, 8.0, 16.0),
320 padding: UiRect::axes(px(0.), px(10.)),
321 ..default()
322 },
323 BorderColor::all(RED),
324 BackgroundColor(GREEN.into()),
325 ImageNode::new(asset_server.load("branding/icon.png")),
326 )],
327 ),
328 ],
329 ));
330 }
331}
332
333mod text {
334 use bevy::{color::palettes::css::*, prelude::*, text::FontSmoothing};
335
336 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
337 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Text)));
338
339 let mut container = commands.spawn((
340 Node {
341 flex_direction: FlexDirection::Column,
342 ..default()
343 },
344 DespawnOnExit(super::Scene::Text),
345 ));
346
347 container.with_child((
348 Text::new("Hello World."),
349 TextFont {
350 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
351 font_size: FontSize::Px(200.),
352 ..default()
353 },
354 ));
355
356 container.with_children(|builder| {
357 let mut grid = builder.spawn(Node {
358 display: Display::Grid,
359 grid_template_columns: vec![GridTrack::flex(1.0), GridTrack::flex(1.0)],
360 padding: UiRect::horizontal(px(5.)),
361 ..default()
362 });
363
364 grid.with_children(|grid| {
365 for hinting in [FontHinting::Enabled, FontHinting::Disabled] {
366 let mut content = grid.spawn(Node {
367 flex_direction: FlexDirection::Column,
368 row_gap: px(5.),
369 ..default()
370 });
371
372 content.with_child((
373 Text::new(format!("FontHinting::{:?}", hinting)),
374 TextFont {
375 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
376 ..default()
377 },
378 hinting,
379 ));
380
381 content.with_child((
382 Text::new("white "),
383 TextFont {
384 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
385 ..default()
386 },
387 hinting,
388 children![
389 (TextSpan::new("red "), TextColor(RED.into()),),
390 (TextSpan::new("green "), TextColor(GREEN.into()),),
391 (TextSpan::new("blue "), TextColor(BLUE.into()),),
392 (
393 TextSpan::new("black"),
394 TextColor(Color::BLACK),
395 TextFont {
396 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
397 ..default()
398 },
399 TextBackgroundColor(Color::WHITE)
400 ),
401 ],
402 ));
403
404 content.with_child((
405 Text::new(""),
406 TextFont {
407 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
408 ..default()
409 },
410 hinting,
411 children![
412 (
413 TextSpan::new("white "),
414 TextFont {
415 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
416 ..default()
417 }
418 ),
419 (TextSpan::new("red "), TextColor(RED.into()),),
420 (TextSpan::new("green "), TextColor(GREEN.into()),),
421 (TextSpan::new("blue "), TextColor(BLUE.into()),),
422 (
423 TextSpan::new("black"),
424 TextColor(Color::BLACK),
425 TextFont {
426 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
427 ..default()
428 },
429 TextBackgroundColor(Color::WHITE)
430 ),
431 ],
432 ));
433
434 content.with_child((
435 Text::new(""),
436 TextFont {
437 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
438 ..default()
439 },
440 hinting,
441 children![
442 (TextSpan::new(""), TextColor(YELLOW.into()),),
443 TextSpan::new(""),
444 (
445 TextSpan::new("white "),
446 TextFont {
447 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
448 ..default()
449 }
450 ),
451 TextSpan::new(""),
452 (TextSpan::new("red "), TextColor(RED.into()),),
453 TextSpan::new(""),
454 TextSpan::new(""),
455 (TextSpan::new("green "), TextColor(GREEN.into()),),
456 (TextSpan::new(""), TextColor(YELLOW.into()),),
457 (TextSpan::new("blue "), TextColor(BLUE.into()),),
458 TextSpan::new(""),
459 (TextSpan::new(""), TextColor(YELLOW.into()),),
460 (
461 TextSpan::new("black"),
462 TextColor(Color::BLACK),
463 TextFont {
464 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
465 ..default()
466 },
467 TextBackgroundColor(Color::WHITE)
468 ),
469 TextSpan::new(""),
470 ],
471 ));
472
473 content.with_child((
474 hinting,
475 Text::new("FiraSans_"),
476 TextFont {
477 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
478 font_size: FontSize::Px(25.),
479 ..default()
480 },
481 children![
482 (
483 TextSpan::new("MonaSans_"),
484 TextFont {
485 font: asset_server
486 .load("fonts/MonaSans-VariableFont.ttf")
487 .into(),
488 font_size: FontSize::Px(25.),
489 ..default()
490 }
491 ),
492 (
493 TextSpan::new("EBGaramond_"),
494 TextFont {
495 font: asset_server
496 .load("fonts/EBGaramond12-Regular.otf")
497 .into(),
498 font_size: FontSize::Px(25.),
499 ..default()
500 },
501 ),
502 (
503 TextSpan::new("FiraMono"),
504 TextFont {
505 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
506 font_size: FontSize::Px(25.),
507 ..default()
508 },
509 ),
510 ],
511 ));
512
513 content.with_child((
514 hinting,
515 Text::new("FiraSans "),
516 TextFont {
517 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
518 font_size: FontSize::Px(25.),
519 ..default()
520 },
521 children![
522 (
523 TextSpan::new("MonaSans "),
524 TextFont {
525 font: asset_server
526 .load("fonts/MonaSans-VariableFont.ttf")
527 .into(),
528 font_size: FontSize::Px(25.),
529 ..default()
530 }
531 ),
532 (
533 TextSpan::new("EBGaramond "),
534 TextFont {
535 font: asset_server
536 .load("fonts/EBGaramond12-Regular.otf")
537 .into(),
538 font_size: FontSize::Px(25.),
539 ..default()
540 },
541 ),
542 (
543 TextSpan::new("FiraMono"),
544 TextFont {
545 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
546 font_size: FontSize::Px(25.),
547 ..default()
548 },
549 ),
550 ],
551 ));
552
553 content.with_child((
554 hinting,
555 Text::new("FiraSans "),
556 TextFont {
557 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
558 font_size: FontSize::Px(25.),
559 ..default()
560 },
561 children![
562 (
563 TextSpan::new("MonaSans_"),
564 TextFont {
565 font: asset_server
566 .load("fonts/MonaSans-VariableFont.ttf")
567 .into(),
568 font_size: FontSize::Px(25.),
569 ..default()
570 }
571 ),
572 (
573 TextSpan::new("EBGaramond "),
574 TextFont {
575 font: asset_server
576 .load("fonts/EBGaramond12-Regular.otf")
577 .into(),
578 font_size: FontSize::Px(25.),
579 ..default()
580 },
581 ),
582 (
583 TextSpan::new("FiraMono"),
584 TextFont {
585 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
586 font_size: FontSize::Px(25.),
587 ..default()
588 },
589 ),
590 ],
591 ));
592
593 content.with_child((
594 hinting,
595 Text::new("FiraSans"),
596 TextFont {
597 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
598 font_size: FontSize::Px(25.),
599 ..default()
600 },
601 children![
602 TextSpan::new(" "),
603 (
604 TextSpan::new("MonaSans"),
605 TextFont {
606 font: asset_server
607 .load("fonts/MonaSans-VariableFont.ttf")
608 .into(),
609 font_size: FontSize::Px(25.),
610 ..default()
611 }
612 ),
613 TextSpan::new(" "),
614 (
615 TextSpan::new("EBGaramond"),
616 TextFont {
617 font: asset_server
618 .load("fonts/EBGaramond12-Regular.otf")
619 .into(),
620 font_size: FontSize::Px(25.),
621 ..default()
622 },
623 ),
624 TextSpan::new(" "),
625 (
626 TextSpan::new("FiraMono"),
627 TextFont {
628 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
629 font_size: FontSize::Px(25.),
630 ..default()
631 },
632 ),
633 ],
634 ));
635
636 content.with_child((
637 hinting,
638 Text::new("Fira Sans_"),
639 TextFont {
640 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
641 font_size: FontSize::Px(25.),
642 ..default()
643 },
644 children![
645 (
646 TextSpan::new("Mona Sans_"),
647 TextFont {
648 font: asset_server
649 .load("fonts/MonaSans-VariableFont.ttf")
650 .into(),
651 font_size: FontSize::Px(25.),
652 ..default()
653 }
654 ),
655 (
656 TextSpan::new("EB Garamond_"),
657 TextFont {
658 font: asset_server
659 .load("fonts/EBGaramond12-Regular.otf")
660 .into(),
661 font_size: FontSize::Px(25.),
662 ..default()
663 },
664 ),
665 (
666 TextSpan::new("Fira Mono"),
667 TextFont {
668 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
669 font_size: FontSize::Px(25.),
670 ..default()
671 },
672 ),
673 ],
674 ));
675
676 content.with_child((
677 hinting,
678 Text::new("FontWeight(100)_"),
679 TextFont {
680 font: "Mona Sans".into(),
681 font_size: FontSize::Px(25.),
682 weight: FontWeight(100),
683 ..default()
684 },
685 children![
686 (
687 TextSpan::new("FontWeight(500)_"),
688 TextFont {
689 font: "Mona Sans".into(),
690 font_size: FontSize::Px(25.),
691 weight: FontWeight(500),
692 ..default()
693 }
694 ),
695 (
696 TextSpan::new("FontWeight(900)"),
697 TextFont {
698 font: "Mona Sans".into(),
699 font_size: FontSize::Px(25.),
700 weight: FontWeight(900),
701 ..default()
702 },
703 ),
704 ],
705 ));
706
707 content.with_child((
708 hinting,
709 Text::new("FiraSans_"),
710 TextFont {
711 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
712 font_size: FontSize::Px(25.),
713 weight: FontWeight(900),
714 ..default()
715 },
716 children![
717 (
718 TextSpan::new("MonaSans_"),
719 TextFont {
720 font: asset_server
721 .load("fonts/MonaSans-VariableFont.ttf")
722 .into(),
723 font_size: FontSize::Px(25.),
724 weight: FontWeight(700),
725 ..default()
726 }
727 ),
728 (
729 TextSpan::new("EBGaramond_"),
730 TextFont {
731 font: asset_server
732 .load("fonts/EBGaramond12-Regular.otf")
733 .into(),
734 font_size: FontSize::Px(25.),
735 weight: FontWeight(500),
736 ..default()
737 },
738 ),
739 (
740 TextSpan::new("FiraMono"),
741 TextFont {
742 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
743 font_size: FontSize::Px(25.),
744 weight: FontWeight(300),
745 ..default()
746 },
747 ),
748 ],
749 ));
750
751 content.with_child((
752 hinting,
753 Text::new("FiraSans\t"),
754 TextFont {
755 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
756 font_size: FontSize::Px(25.),
757 ..default()
758 },
759 children![
760 (
761 TextSpan::new("MonaSans\t"),
762 TextFont {
763 font: asset_server
764 .load("fonts/MonaSans-VariableFont.ttf")
765 .into(),
766 font_size: FontSize::Px(25.),
767 ..default()
768 }
769 ),
770 (
771 TextSpan::new("EBGaramond\t"),
772 TextFont {
773 font: asset_server
774 .load("fonts/EBGaramond12-Regular.otf")
775 .into(),
776 font_size: FontSize::Px(25.),
777 ..default()
778 },
779 ),
780 (
781 TextSpan::new("FiraMono"),
782 TextFont {
783 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
784 font_size: FontSize::Px(25.),
785 ..default()
786 },
787 ),
788 ],
789 ));
790
791 for font_smoothing in [FontSmoothing::AntiAliased, FontSmoothing::None] {
792 content.with_child((
793 Text::new(format!("FontSmoothing::{:?}", font_smoothing)),
794 TextFont {
795 font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
796 font_size: FontSize::Px(25.),
797 font_smoothing,
798 ..default()
799 },
800 ));
801 }
802 }
803 });
804 });
805 }
806}
807
808mod grid {
809 use bevy::{color::palettes::css::*, prelude::*};
810
811 pub fn setup(mut commands: Commands) {
812 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Grid)));
813 commands.spawn((
815 Node {
816 display: Display::Grid,
817 width: percent(100),
818 height: percent(100),
819 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
820 grid_template_rows: vec![
821 GridTrack::auto(),
822 GridTrack::flex(1.0),
823 GridTrack::px(40.),
824 ],
825 ..default()
826 },
827 BackgroundColor(Color::WHITE),
828 DespawnOnExit(super::Scene::Grid),
829 children![
830 (
832 Node {
833 display: Display::Grid,
834 grid_column: GridPlacement::span(2),
835 padding: UiRect::all(px(40)),
836 ..default()
837 },
838 BackgroundColor(RED.into()),
839 ),
840 (
842 Node {
843 height: percent(100),
844 aspect_ratio: Some(1.0),
845 display: Display::Grid,
846 grid_template_columns: RepeatedGridTrack::flex(3, 1.0),
847 grid_template_rows: RepeatedGridTrack::flex(2, 1.0),
848 row_gap: px(12),
849 column_gap: px(12),
850 ..default()
851 },
852 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
853 children![
854 (Node::default(), BackgroundColor(ORANGE.into())),
855 (Node::default(), BackgroundColor(BISQUE.into())),
856 (Node::default(), BackgroundColor(BLUE.into())),
857 (Node::default(), BackgroundColor(CRIMSON.into())),
858 (Node::default(), BackgroundColor(AQUA.into())),
859 ]
860 ),
861 (Node::DEFAULT, BackgroundColor(BLACK.into())),
863 ],
864 ));
865 }
866}
867
868mod borders {
869 use bevy::{color::palettes::css::*, prelude::*};
870
871 pub fn setup(mut commands: Commands) {
872 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Borders)));
873 let root = commands
874 .spawn((
875 Node {
876 flex_wrap: FlexWrap::Wrap,
877 ..default()
878 },
879 DespawnOnExit(super::Scene::Borders),
880 ))
881 .id();
882
883 let borders = [
885 UiRect::default(),
886 UiRect::all(px(20)),
887 UiRect::left(px(20)),
888 UiRect::vertical(px(20)),
889 UiRect {
890 left: px(40),
891 top: px(20),
892 ..Default::default()
893 },
894 UiRect {
895 right: px(20),
896 bottom: px(30),
897 ..Default::default()
898 },
899 UiRect {
900 right: px(20),
901 top: px(40),
902 bottom: px(20),
903 ..Default::default()
904 },
905 UiRect {
906 left: px(20),
907 top: px(20),
908 bottom: px(20),
909 ..Default::default()
910 },
911 UiRect {
912 left: px(20),
913 right: px(20),
914 bottom: px(40),
915 ..Default::default()
916 },
917 ];
918
919 let non_zero = |x, y| x != px(0) && y != px(0);
920 let border_size = |x, y| if non_zero(x, y) { f32::MAX } else { 0. };
921
922 for border in borders {
923 for rounded in [true, false] {
924 let border_node = commands
925 .spawn((
926 Node {
927 width: px(100),
928 height: px(100),
929 border,
930 margin: UiRect::all(px(30)),
931 align_items: AlignItems::Center,
932 justify_content: JustifyContent::Center,
933 border_radius: if rounded {
934 BorderRadius::px(
935 border_size(border.left, border.top),
936 border_size(border.right, border.top),
937 border_size(border.right, border.bottom),
938 border_size(border.left, border.bottom),
939 )
940 } else {
941 BorderRadius::ZERO
942 },
943 ..default()
944 },
945 BackgroundColor(MAROON.into()),
946 BorderColor::all(RED),
947 Outline {
948 width: px(10),
949 offset: px(10),
950 color: Color::WHITE,
951 },
952 ))
953 .id();
954
955 commands.entity(root).add_child(border_node);
956 }
957 }
958 }
959}
960
961mod box_shadow {
962 use bevy::{color::palettes::css::*, prelude::*};
963
964 pub fn setup(mut commands: Commands) {
965 commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxShadow)));
966
967 commands
968 .spawn((
969 Node {
970 width: percent(100),
971 height: percent(100),
972 padding: UiRect::all(px(30)),
973 column_gap: px(200),
974 flex_wrap: FlexWrap::Wrap,
975 ..default()
976 },
977 BackgroundColor(GREEN.into()),
978 DespawnOnExit(super::Scene::BoxShadow),
979 ))
980 .with_children(|commands| {
981 let example_nodes = [
982 (
983 Vec2::splat(100.),
984 Vec2::ZERO,
985 10.,
986 0.,
987 BorderRadius::bottom_right(px(10)),
988 ),
989 (Vec2::new(200., 50.), Vec2::ZERO, 10., 0., BorderRadius::MAX),
990 (
991 Vec2::new(100., 50.),
992 Vec2::ZERO,
993 10.,
994 10.,
995 BorderRadius::ZERO,
996 ),
997 (
998 Vec2::splat(100.),
999 Vec2::splat(20.),
1000 10.,
1001 10.,
1002 BorderRadius::bottom_right(px(10)),
1003 ),
1004 (
1005 Vec2::splat(100.),
1006 Vec2::splat(50.),
1007 0.,
1008 10.,
1009 BorderRadius::ZERO,
1010 ),
1011 (
1012 Vec2::new(50., 100.),
1013 Vec2::splat(10.),
1014 0.,
1015 10.,
1016 BorderRadius::MAX,
1017 ),
1018 ];
1019
1020 for (size, offset, spread, blur, border_radius) in example_nodes {
1021 commands.spawn((
1022 Node {
1023 width: px(size.x),
1024 height: px(size.y),
1025 border: UiRect::all(px(2)),
1026 border_radius,
1027 ..default()
1028 },
1029 BorderColor::all(WHITE),
1030 BackgroundColor(BLUE.into()),
1031 BoxShadow::new(
1032 Color::BLACK.with_alpha(0.9),
1033 percent(offset.x),
1034 percent(offset.y),
1035 percent(spread),
1036 px(blur),
1037 ),
1038 ));
1039 }
1040 });
1041 }
1042}
1043
1044mod text_wrap {
1045 use bevy::prelude::*;
1046
1047 pub fn setup(mut commands: Commands) {
1048 commands.spawn((Camera2d, DespawnOnExit(super::Scene::TextWrap)));
1049
1050 let root = commands
1051 .spawn((
1052 Node {
1053 flex_direction: FlexDirection::Column,
1054 width: px(200),
1055 height: percent(100),
1056 overflow: Overflow::clip_x(),
1057 ..default()
1058 },
1059 BackgroundColor(Color::BLACK),
1060 DespawnOnExit(super::Scene::TextWrap),
1061 ))
1062 .id();
1063
1064 for linebreak in [
1065 LineBreak::AnyCharacter,
1066 LineBreak::WordBoundary,
1067 LineBreak::WordOrCharacter,
1068 LineBreak::NoWrap,
1069 ] {
1070 let messages = [
1071 "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string(),
1072 "pneumonoultramicroscopicsilicovolcanoconiosis".to_string(),
1073 ];
1074
1075 for (j, message) in messages.into_iter().enumerate() {
1076 commands.entity(root).with_child((
1077 Text(message.clone()),
1078 TextLayout::new(Justify::Left, linebreak),
1079 BackgroundColor(Color::srgb(0.8 - j as f32 * 0.3, 0., 0.)),
1080 ));
1081 }
1082 }
1083
1084 commands.spawn((
1085 Node {
1086 position_type: PositionType::Absolute,
1087 padding: UiRect::px(10.0, 10.0, 8.0, 6.0),
1088 top: px(300.0),
1089 left: px(300.0),
1090 width: vmin(30.0),
1091 ..default()
1092 },
1093 BackgroundColor::from(bevy::color::palettes::css::GREEN),
1094 Text::new("initial text here"),
1095 TextColor(Color::WHITE),
1096 TextLayout {
1097 justify: Justify::Left,
1098 linebreak: LineBreak::WordBoundary,
1099 },
1100 DespawnOnExit(super::Scene::TextWrap),
1101 ));
1102 }
1103}
1104
1105mod overflow {
1106 use bevy::{color::palettes::css::*, prelude::*};
1107
1108 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1109 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Overflow)));
1110 let image = asset_server.load("branding/icon.png");
1111
1112 commands
1113 .spawn((
1114 Node {
1115 width: percent(100),
1116 height: percent(100),
1117 align_items: AlignItems::Center,
1118 justify_content: JustifyContent::SpaceAround,
1119 ..Default::default()
1120 },
1121 BackgroundColor(BLUE.into()),
1122 DespawnOnExit(super::Scene::Overflow),
1123 ))
1124 .with_children(|parent| {
1125 for overflow in [
1126 Overflow::visible(),
1127 Overflow::clip_x(),
1128 Overflow::clip_y(),
1129 Overflow::clip(),
1130 ] {
1131 parent
1132 .spawn((
1133 Node {
1134 width: px(100),
1135 height: px(100),
1136 padding: UiRect {
1137 left: px(25),
1138 top: px(25),
1139 ..Default::default()
1140 },
1141 border: UiRect::all(px(5)),
1142 overflow,
1143 ..default()
1144 },
1145 BorderColor::all(RED),
1146 BackgroundColor(Color::WHITE),
1147 ))
1148 .with_children(|parent| {
1149 parent.spawn((
1150 ImageNode::new(image.clone()),
1151 Node {
1152 min_width: px(100),
1153 min_height: px(100),
1154 ..default()
1155 },
1156 Interaction::default(),
1157 Outline {
1158 width: px(2),
1159 offset: px(2),
1160 color: Color::NONE,
1161 },
1162 ));
1163 });
1164 }
1165 });
1166 }
1167}
1168
1169mod slice {
1170 use bevy::prelude::*;
1171
1172 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1173 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Slice)));
1174 let image = asset_server.load("textures/fantasy_ui_borders/numbered_slices.png");
1175
1176 let slicer = TextureSlicer {
1177 border: BorderRect::all(16.0),
1178 center_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
1179 sides_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
1180 ..default()
1181 };
1182 commands
1183 .spawn((
1184 Node {
1185 width: percent(100),
1186 height: percent(100),
1187 flex_direction: FlexDirection::Column,
1188 justify_content: JustifyContent::SpaceAround,
1189 align_content: AlignContent::Center,
1190 ..default()
1191 },
1192 DespawnOnExit(super::Scene::Slice),
1193 ))
1194 .with_children(|parent| {
1195 for visual_box in [
1196 VisualBox::BorderBox,
1197 VisualBox::PaddingBox,
1198 VisualBox::ContentBox,
1199 ] {
1200 parent
1201 .spawn(Node {
1202 justify_content: JustifyContent::SpaceAround,
1203 ..default()
1204 })
1205 .with_children(|parent| {
1206 for [w, h] in [[200.0, 200.0], [300.0, 200.0], [150., 200.0]] {
1207 parent.spawn((
1208 Button,
1209 ImageNode {
1210 image: image.clone(),
1211 image_mode: NodeImageMode::Sliced(slicer.clone()),
1212 visual_box,
1213 ..default()
1214 },
1215 Node {
1216 width: px(w),
1217 height: px(h),
1218 border: px(20.).all(),
1219 padding: px(20.).all(),
1220 ..default()
1221 },
1222 Outline {
1223 width: px(2.),
1224 ..default()
1225 },
1226 ));
1227 }
1228
1229 parent.spawn((
1230 ImageNode {
1231 image: asset_server
1232 .load("textures/fantasy_ui_borders/panel-border-010.png"),
1233 image_mode: NodeImageMode::Sliced(TextureSlicer {
1234 border: BorderRect::all(22.0),
1235 center_scale_mode: SliceScaleMode::Stretch,
1236 sides_scale_mode: SliceScaleMode::Stretch,
1237 max_corner_scale: 1.0,
1238 }),
1239 visual_box,
1240 ..Default::default()
1241 },
1242 Node {
1243 width: px(200),
1244 height: px(200),
1245 border: px(20.).all(),
1246 padding: px(20.).all(),
1247 ..default()
1248 },
1249 Outline {
1250 color: bevy::color::palettes::css::DARK_CYAN.into(),
1251 width: px(2.),
1252 ..default()
1253 },
1254 BackgroundColor(bevy::color::palettes::css::NAVY.into()),
1255 ));
1256 });
1257 }
1258 });
1259 }
1260}
1261
1262mod layout_rounding {
1263 use bevy::{color::palettes::css::*, prelude::*};
1264
1265 pub fn setup(mut commands: Commands) {
1266 commands.spawn((Camera2d, DespawnOnExit(super::Scene::LayoutRounding)));
1267
1268 commands
1269 .spawn((
1270 Node {
1271 display: Display::Grid,
1272 width: percent(100),
1273 height: percent(100),
1274 grid_template_rows: vec![RepeatedGridTrack::fr(10, 1.)],
1275 ..Default::default()
1276 },
1277 BackgroundColor(Color::WHITE),
1278 DespawnOnExit(super::Scene::LayoutRounding),
1279 ))
1280 .with_children(|commands| {
1281 for i in 2..12 {
1282 commands
1283 .spawn(Node {
1284 display: Display::Grid,
1285 grid_template_columns: vec![RepeatedGridTrack::fr(i, 1.)],
1286 ..Default::default()
1287 })
1288 .with_children(|commands| {
1289 for _ in 0..i {
1290 commands.spawn((
1291 Node {
1292 border: UiRect::all(px(5)),
1293 ..Default::default()
1294 },
1295 BackgroundColor(MAROON.into()),
1296 BorderColor::all(DARK_BLUE),
1297 ));
1298 }
1299 });
1300 }
1301 });
1302 }
1303}
1304
1305mod linear_gradient {
1306 use bevy::camera::Camera2d;
1307 use bevy::color::palettes::css::BLUE;
1308 use bevy::color::palettes::css::LIME;
1309 use bevy::color::palettes::css::RED;
1310 use bevy::color::palettes::css::YELLOW;
1311 use bevy::color::Color;
1312 use bevy::ecs::prelude::*;
1313 use bevy::state::state_scoped::DespawnOnExit;
1314 use bevy::text::TextFont;
1315 use bevy::ui::AlignItems;
1316 use bevy::ui::BackgroundGradient;
1317 use bevy::ui::ColorStop;
1318 use bevy::ui::GridPlacement;
1319 use bevy::ui::InterpolationColorSpace;
1320 use bevy::ui::JustifyContent;
1321 use bevy::ui::LinearGradient;
1322 use bevy::ui::Node;
1323 use bevy::ui::PositionType;
1324 use bevy::utils::default;
1325
1326 pub fn setup(mut commands: Commands) {
1327 commands.spawn((Camera2d, DespawnOnExit(super::Scene::LinearGradient)));
1328 commands
1329 .spawn((
1330 Node {
1331 flex_direction: bevy::ui::FlexDirection::Column,
1332 width: bevy::ui::percent(100),
1333 height: bevy::ui::percent(100),
1334 justify_content: JustifyContent::Center,
1335 align_items: AlignItems::Center,
1336 row_gap: bevy::ui::px(5),
1337 ..default()
1338 },
1339 DespawnOnExit(super::Scene::LinearGradient),
1340 ))
1341 .with_children(|commands| {
1342 let mut i = 0;
1343 commands
1344 .spawn(Node {
1345 display: bevy::ui::Display::Grid,
1346 row_gap: bevy::ui::px(4),
1347 column_gap: bevy::ui::px(4),
1348 ..Default::default()
1349 })
1350 .with_children(|commands| {
1351 for stops in [
1352 vec![ColorStop::auto(RED), ColorStop::auto(YELLOW)],
1353 vec![
1354 ColorStop::auto(Color::BLACK),
1355 ColorStop::auto(RED),
1356 ColorStop::auto(Color::WHITE),
1357 ],
1358 vec![
1359 Color::hsl(180.71191, 0.0, 0.3137255).into(),
1360 Color::hsl(180.71191, 0.5, 0.3137255).into(),
1361 Color::hsl(180.71191, 1.0, 0.3137255).into(),
1362 ],
1363 vec![
1364 Color::hsl(180.71191, 0.825, 0.0).into(),
1365 Color::hsl(180.71191, 0.825, 0.5).into(),
1366 Color::hsl(180.71191, 0.825, 1.0).into(),
1367 ],
1368 vec![
1369 Color::hsl(0.0 + 0.0001, 1.0, 0.5).into(),
1370 Color::hsl(180.0, 1.0, 0.5).into(),
1371 Color::hsl(360.0 - 0.0001, 1.0, 0.5).into(),
1372 ],
1373 vec![
1374 Color::WHITE.into(),
1375 RED.into(),
1376 LIME.into(),
1377 BLUE.into(),
1378 Color::BLACK.into(),
1379 ],
1380 ] {
1381 for color_space in [
1382 InterpolationColorSpace::LinearRgba,
1383 InterpolationColorSpace::Srgba,
1384 InterpolationColorSpace::Oklaba,
1385 InterpolationColorSpace::Oklcha,
1386 InterpolationColorSpace::OklchaLong,
1387 InterpolationColorSpace::Hsla,
1388 InterpolationColorSpace::HslaLong,
1389 InterpolationColorSpace::Hsva,
1390 InterpolationColorSpace::HsvaLong,
1391 ] {
1392 let row = i % 18 + 1;
1393 let column = i / 18 + 1;
1394 i += 1;
1395
1396 commands.spawn((
1397 Node {
1398 grid_row: GridPlacement::start(row as i16 + 1),
1399 grid_column: GridPlacement::start(column as i16 + 1),
1400 justify_content: JustifyContent::SpaceEvenly,
1401 ..Default::default()
1402 },
1403 children![(
1404 Node {
1405 height: bevy::ui::px(30),
1406 width: bevy::ui::px(300),
1407 justify_content: JustifyContent::Center,
1408 ..Default::default()
1409 },
1410 BackgroundGradient::from(LinearGradient {
1411 color_space,
1412 angle: LinearGradient::TO_RIGHT,
1413 stops: stops.clone(),
1414 }),
1415 children![
1416 Node {
1417 position_type: PositionType::Absolute,
1418 ..default()
1419 },
1420 TextFont::from_font_size(10.),
1421 bevy::ui::widget::Text(format!("{color_space:?}")),
1422 ]
1423 )],
1424 ));
1425 }
1426 }
1427 });
1428 });
1429 }
1430}
1431
1432mod radial_gradient {
1433 use bevy::color::palettes::css::RED;
1434 use bevy::color::palettes::tailwind::GRAY_700;
1435 use bevy::prelude::*;
1436 use bevy::ui::ColorStop;
1437
1438 const CELL_SIZE: f32 = 80.;
1439 const GAP: f32 = 10.;
1440
1441 pub fn setup(mut commands: Commands) {
1442 let color_stops = vec![
1443 ColorStop::new(Color::BLACK, px(5)),
1444 ColorStop::new(Color::WHITE, px(5)),
1445 ColorStop::new(Color::WHITE, percent(100)),
1446 ColorStop::auto(RED),
1447 ];
1448
1449 commands.spawn((Camera2d, DespawnOnExit(super::Scene::RadialGradient)));
1450 commands
1451 .spawn((
1452 Node {
1453 width: percent(100),
1454 height: percent(100),
1455 display: Display::Grid,
1456 align_items: AlignItems::Start,
1457 grid_template_columns: vec![RepeatedGridTrack::px(
1458 GridTrackRepetition::AutoFill,
1459 CELL_SIZE,
1460 )],
1461 grid_auto_flow: GridAutoFlow::Row,
1462 row_gap: px(GAP),
1463 column_gap: px(GAP),
1464 padding: UiRect::all(px(GAP)),
1465 ..default()
1466 },
1467 DespawnOnExit(super::Scene::RadialGradient),
1468 ))
1469 .with_children(|commands| {
1470 for (shape, shape_label) in [
1471 (RadialGradientShape::ClosestSide, "ClosestSide"),
1472 (RadialGradientShape::FarthestSide, "FarthestSide"),
1473 (RadialGradientShape::Circle(percent(55)), "Circle(55%)"),
1474 (RadialGradientShape::FarthestCorner, "FarthestCorner"),
1475 ] {
1476 for (position, position_label) in [
1477 (UiPosition::TOP_LEFT, "TOP_LEFT"),
1478 (UiPosition::LEFT, "LEFT"),
1479 (UiPosition::BOTTOM_LEFT, "BOTTOM_LEFT"),
1480 (UiPosition::TOP, "TOP"),
1481 (UiPosition::CENTER, "CENTER"),
1482 (UiPosition::BOTTOM, "BOTTOM"),
1483 (UiPosition::TOP_RIGHT, "TOP_RIGHT"),
1484 (UiPosition::RIGHT, "RIGHT"),
1485 (UiPosition::BOTTOM_RIGHT, "BOTTOM_RIGHT"),
1486 ] {
1487 for (w, h) in [(CELL_SIZE, CELL_SIZE), (CELL_SIZE, CELL_SIZE / 2.)] {
1488 commands
1489 .spawn((
1490 BackgroundColor(GRAY_700.into()),
1491 Node {
1492 display: Display::Grid,
1493 width: px(CELL_SIZE),
1494 ..Default::default()
1495 },
1496 ))
1497 .with_children(|commands| {
1498 commands.spawn((
1499 Node {
1500 margin: UiRect::all(px(2)),
1501 ..default()
1502 },
1503 Text(format!("{shape_label}\n{position_label}")),
1504 TextFont::from_font_size(9.),
1505 ));
1506 commands.spawn((
1507 Node {
1508 width: px(w),
1509 height: px(h),
1510 ..default()
1511 },
1512 BackgroundGradient::from(RadialGradient {
1513 stops: color_stops.clone(),
1514 position,
1515 shape,
1516 ..default()
1517 }),
1518 ));
1519 });
1520 }
1521 }
1522 }
1523 });
1524 }
1525}
1526
1527mod transformations {
1528 use bevy::{color::palettes::css::*, prelude::*};
1529
1530 pub fn setup(mut commands: Commands) {
1531 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Transformations)));
1532 commands
1533 .spawn((
1534 Node {
1535 width: percent(100),
1536 height: percent(100),
1537 display: Display::Block,
1538 ..default()
1539 },
1540 DespawnOnExit(super::Scene::Transformations),
1541 ))
1542 .with_children(|parent| {
1543 for (transformation, label, background) in [
1544 (
1545 UiTransform::from_rotation(Rot2::degrees(45.)),
1546 "Rotate 45 degrees",
1547 RED,
1548 ),
1549 (
1550 UiTransform::from_scale(Vec2::new(2., 0.5)),
1551 "Scale 2.x 0.5y",
1552 GREEN,
1553 ),
1554 (
1555 UiTransform::from_translation(Val2::px(-50., 50.)),
1556 "Translate -50px x +50px y",
1557 BLUE,
1558 ),
1559 (
1560 UiTransform {
1561 translation: Val2::px(50., 0.),
1562 scale: Vec2::new(-1., 1.),
1563 rotation: Rot2::degrees(30.),
1564 },
1565 "T 50px x\nS -1.x (refl)\nR 30deg",
1566 DARK_CYAN,
1567 ),
1568 ] {
1569 parent
1570 .spawn((Node {
1571 width: percent(100),
1572 margin: UiRect {
1573 top: px(50),
1574 bottom: px(50),
1575 ..default()
1576 },
1577 align_items: AlignItems::Center,
1578 justify_content: JustifyContent::SpaceAround,
1579 ..default()
1580 },))
1581 .with_children(|row| {
1582 row.spawn((
1583 Text::new("Before Tf"),
1584 Node {
1585 width: px(100),
1586 height: px(100),
1587 border_radius: BorderRadius::bottom_right(px(25.)),
1588 ..default()
1589 },
1590 BackgroundColor(background.into()),
1591 TextFont::default(),
1592 ));
1593 row.spawn((
1594 Text::new(label),
1595 Node {
1596 width: px(100),
1597 height: px(100),
1598 border_radius: BorderRadius::bottom_right(px(25.)),
1599 ..default()
1600 },
1601 BackgroundColor(background.into()),
1602 transformation,
1603 TextFont::default(),
1604 ));
1605 });
1606 }
1607 });
1608 }
1609}
1610
1611#[cfg(feature = "bevy_ui_debug")]
1612mod debug_outlines {
1613 use bevy::{
1614 color::palettes::css::{BLUE, GRAY, RED},
1615 prelude::*,
1616 ui_render::UiDebugOptions,
1617 };
1618
1619 pub fn setup(mut commands: Commands, mut debug_options: ResMut<GlobalUiDebugOptions>) {
1620 debug_options.enabled = true;
1621 debug_options.line_width = 5.;
1622 debug_options.line_color_override = Some(LinearRgba::GREEN);
1623 debug_options.show_hidden = true;
1624 debug_options.show_clipped = true;
1625
1626 let debug_options: UiDebugOptions = (*debug_options.as_ref()).into();
1627
1628 commands.spawn((Camera2d, DespawnOnExit(super::Scene::DebugOutlines)));
1629 commands
1630 .spawn((
1631 Node {
1632 width: percent(100),
1633 height: percent(50),
1634 align_items: AlignItems::Center,
1635 justify_content: JustifyContent::SpaceAround,
1636 ..default()
1637 },
1638 DespawnOnExit(super::Scene::DebugOutlines),
1639 ))
1640 .with_children(|parent| {
1641 parent.spawn((
1642 Node {
1643 width: px(100),
1644 height: px(100),
1645 ..default()
1646 },
1647 BackgroundColor(GRAY.into()),
1648 UiTransform::from_rotation(Rot2::degrees(45.)),
1649 ));
1650
1651 parent.spawn((Text::new("Regular Text"), TextFont::default()));
1652
1653 parent.spawn((
1654 Node {
1655 width: px(100),
1656 height: px(100),
1657 ..default()
1658 },
1659 Text::new("Invisible"),
1660 BackgroundColor(GRAY.into()),
1661 TextFont::default(),
1662 Visibility::Hidden,
1663 ));
1664
1665 parent
1666 .spawn((
1667 Node {
1668 width: px(100),
1669 height: px(100),
1670 padding: UiRect {
1671 left: px(25),
1672 top: px(25),
1673 ..Default::default()
1674 },
1675 overflow: Overflow::clip(),
1676 ..default()
1677 },
1678 BackgroundColor(RED.into()),
1679 ))
1680 .with_children(|child| {
1681 child.spawn((
1682 Node {
1683 min_width: px(100),
1684 min_height: px(100),
1685 ..default()
1686 },
1687 BackgroundColor(BLUE.into()),
1688 ));
1689 });
1690 });
1691
1692 commands
1693 .spawn((
1694 Node {
1695 width: percent(100),
1696 height: percent(50),
1697 top: percent(50),
1698 align_items: AlignItems::Center,
1699 justify_content: JustifyContent::SpaceAround,
1700 ..default()
1701 },
1702 DespawnOnExit(super::Scene::DebugOutlines),
1703 ))
1704 .with_children(|parent| {
1705 parent.spawn((
1706 Node {
1707 width: px(200),
1708 height: px(200),
1709 border: UiRect {
1710 top: px(10),
1711 bottom: px(20),
1712 left: px(30),
1713 right: px(40),
1714 },
1715 border_radius: BorderRadius::bottom_right(px(10)),
1716 padding: UiRect {
1717 top: px(40),
1718 bottom: px(30),
1719 left: px(20),
1720 right: px(10),
1721 },
1722 ..default()
1723 },
1724 children![(
1725 Text::new("border padding content outlines"),
1726 TextFont::default(),
1727 UiDebugOptions {
1728 enabled: false,
1729 ..default()
1730 }
1731 )],
1732 UiDebugOptions {
1733 outline_border_box: true,
1734 outline_padding_box: true,
1735 outline_content_box: true,
1736 ignore_border_radius: false,
1737 ..debug_options
1738 },
1739 ));
1740
1741 parent.spawn((
1743 Node {
1744 flex_direction: FlexDirection::Column,
1745 width: px(90),
1746 height: px(230),
1747 overflow: Overflow::scroll_y(),
1748 scrollbar_width: 20.,
1749 ..default()
1750 },
1751 ScrollPosition(Vec2::new(180., 180.)),
1752 UiDebugOptions {
1753 line_width: 3.,
1754 outline_scrollbars: true,
1755 show_hidden: false,
1756 show_clipped: false,
1757 ..debug_options
1758 },
1759 Children::spawn(SpawnIter((0..20).map(move |i| {
1760 (
1761 Node::default(),
1762 children![(
1763 Text(format!("Item {i}")),
1764 UiDebugOptions {
1765 enabled: false,
1766 ..default()
1767 }
1768 )],
1769 UiDebugOptions {
1770 enabled: false,
1771 ..default()
1772 },
1773 )
1774 }))),
1775 ));
1776
1777 parent.spawn((
1779 Node {
1780 flex_direction: FlexDirection::Row,
1781 width: px(156),
1782 height: px(70),
1783 overflow: Overflow::scroll_x(),
1784 scrollbar_width: 10.,
1785 ..default()
1786 },
1787 UiDebugOptions {
1788 line_width: 3.,
1789 outline_scrollbars: true,
1790 show_hidden: false,
1791 show_clipped: false,
1792 ..debug_options
1793 },
1794 Children::spawn(SpawnIter((0..20).map(move |i| {
1795 (
1796 Node::default(),
1797 children![(
1798 Text(format!("Item {i}")),
1799 UiDebugOptions {
1800 enabled: false,
1801 ..default()
1802 }
1803 )],
1804 UiDebugOptions {
1805 enabled: false,
1806 ..default()
1807 },
1808 )
1809 }))),
1810 ));
1811
1812 parent.spawn((
1814 Node {
1815 flex_direction: FlexDirection::Column,
1816 width: px(230),
1817 height: px(125),
1818 overflow: Overflow::scroll(),
1819 scrollbar_width: 20.,
1820 ..default()
1821 },
1822 ScrollPosition(Vec2::new(300., 0.)),
1823 UiDebugOptions {
1824 line_width: 3.,
1825 outline_scrollbars: true,
1826 show_hidden: false,
1827 show_clipped: false,
1828 ..debug_options
1829 },
1830 Children::spawn(SpawnIter((0..6).map(move |i| {
1831 (
1832 Node {
1833 flex_direction: FlexDirection::Row,
1834 ..default()
1835 },
1836 Children::spawn(SpawnIter((0..6).map({
1837 move |j| {
1838 (
1839 Text(format!("Item {}", (i * 5) + j)),
1840 UiDebugOptions {
1841 enabled: false,
1842 ..default()
1843 },
1844 )
1845 }
1846 }))),
1847 UiDebugOptions {
1848 enabled: false,
1849 ..default()
1850 },
1851 )
1852 }))),
1853 ));
1854 });
1855 }
1856
1857 pub fn teardown(mut debug_options: ResMut<GlobalUiDebugOptions>) {
1858 *debug_options = GlobalUiDebugOptions::default();
1859 }
1860}
1861
1862mod viewport_coords {
1863 use bevy::{color::palettes::css::*, prelude::*};
1864
1865 const PALETTE: [Srgba; 9] = [RED, WHITE, BEIGE, AQUA, CRIMSON, NAVY, AZURE, LIME, BLACK];
1866
1867 pub fn setup(mut commands: Commands) {
1868 commands.spawn((Camera2d, DespawnOnExit(super::Scene::ViewportCoords)));
1869 commands
1870 .spawn((
1871 Node {
1872 width: vw(100),
1873 height: vh(100),
1874 border: UiRect::axes(vw(5), vh(5)),
1875 flex_wrap: FlexWrap::Wrap,
1876 ..default()
1877 },
1878 BorderColor::all(PALETTE[0]),
1879 DespawnOnExit(super::Scene::ViewportCoords),
1880 ))
1881 .with_children(|builder| {
1882 builder.spawn((
1883 Node {
1884 width: vw(30),
1885 height: vh(30),
1886 border: UiRect::all(vmin(5)),
1887 ..default()
1888 },
1889 BackgroundColor(PALETTE[1].into()),
1890 BorderColor::all(PALETTE[8]),
1891 ));
1892
1893 builder.spawn((
1894 Node {
1895 width: vw(60),
1896 height: vh(30),
1897 ..default()
1898 },
1899 BackgroundColor(PALETTE[2].into()),
1900 ));
1901
1902 builder.spawn((
1903 Node {
1904 width: vw(45),
1905 height: vh(30),
1906 border: UiRect::left(vmax(45. / 2.)),
1907 ..default()
1908 },
1909 BackgroundColor(PALETTE[3].into()),
1910 BorderColor::all(PALETTE[7]),
1911 ));
1912
1913 builder.spawn((
1914 Node {
1915 width: vw(45),
1916 height: vh(30),
1917 border: UiRect::right(vmax(45. / 2.)),
1918 ..default()
1919 },
1920 BackgroundColor(PALETTE[4].into()),
1921 BorderColor::all(PALETTE[7]),
1922 ));
1923
1924 builder.spawn((
1925 Node {
1926 width: vw(60),
1927 height: vh(30),
1928 ..default()
1929 },
1930 BackgroundColor(PALETTE[5].into()),
1931 ));
1932
1933 builder.spawn((
1934 Node {
1935 width: vw(30),
1936 height: vh(30),
1937 border: UiRect::all(vmin(5)),
1938 ..default()
1939 },
1940 BackgroundColor(PALETTE[6].into()),
1941 BorderColor::all(PALETTE[8]),
1942 ));
1943 });
1944 }
1945}
1946
1947mod outer_color {
1948 use bevy::prelude::*;
1949
1950 pub fn setup(mut commands: Commands) {
1951 let radius = percent(33.);
1952 let width = px(10.);
1953
1954 commands.spawn((Camera2d, DespawnOnExit(super::Scene::OuterColor)));
1955 commands
1956 .spawn((
1957 Node {
1958 display: Display::Grid,
1959 grid_template_columns: RepeatedGridTrack::px(3, 200.),
1960 grid_template_rows: RepeatedGridTrack::px(3, 200.),
1961 margin: UiRect::AUTO,
1962 ..default()
1963 },
1964 DespawnOnExit(super::Scene::OuterColor),
1965 ))
1966 .with_children(|builder| {
1967 for (border, border_radius, invert) in [
1968 (UiRect::ZERO, BorderRadius::bottom_right(radius), true),
1969 (UiRect::top(width), BorderRadius::top(radius), false),
1970 (UiRect::ZERO, BorderRadius::bottom_left(radius), true),
1971 (UiRect::left(width), BorderRadius::left(radius), false),
1972 (UiRect::all(width), BorderRadius::all(radius), true),
1973 (UiRect::right(width), BorderRadius::right(radius), false),
1974 (UiRect::ZERO, BorderRadius::top_right(radius), true),
1975 (UiRect::bottom(width), BorderRadius::bottom(radius), false),
1976 (UiRect::ZERO, BorderRadius::top_left(radius), true),
1977 ] {
1978 builder
1979 .spawn((
1980 Node {
1981 width: px(200.),
1982 height: px(200.),
1983 border_radius,
1984 border,
1985 ..default()
1986 },
1987 BorderColor::all(bevy::color::palettes::css::RED),
1988 ))
1989 .insert_if(BackgroundColor(Color::WHITE), || !invert)
1990 .insert_if(OuterColor(Color::WHITE), || invert);
1991 }
1992 });
1993 }
1994}
1995
1996mod boxed_content {
1997 use bevy::color::palettes::css::RED;
1998 use bevy::prelude::*;
1999
2000 pub fn setup(mut commands: Commands) {
2001 commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxedContent)));
2002 commands
2003 .spawn((
2004 Node {
2005 margin: auto().all(),
2006 column_gap: px(30),
2007 ..default()
2008 },
2009 DespawnOnExit(super::Scene::BoxedContent),
2010 ))
2011 .with_children(|builder| {
2012 for (heading, text_justify) in [
2013 ("Left", Justify::Left),
2014 ("Center", Justify::Center),
2015 ("Right", Justify::Right),
2016 ] {
2017 builder
2018 .spawn(Node {
2019 flex_direction: FlexDirection::Column,
2020 align_items: AlignItems::Center,
2021 justify_content: JustifyContent::Start,
2022 row_gap: px(20),
2023 ..default()
2024 })
2025 .with_children(|builder| {
2026 builder.spawn((
2027 Node::default(),
2028 Text::new(format!("{heading} justify")),
2029 TextFont::from_font_size(FontSize::Px(14.)),
2030 TextLayout::justify(Justify::Center),
2031 ));
2032
2033 builder.spawn((
2034 Node::default(),
2035 Text::new("This text has\nno border or padding."),
2036 TextFont::from_font_size(FontSize::Px(10.)),
2037 TextLayout::justify(text_justify),
2038 Outline {
2039 width: px(2),
2040 color: Color::WHITE,
2041 ..Default::default()
2042 },
2043 ));
2044
2045 builder.spawn((
2046 Node {
2047 border: px(10).all(),
2048 ..default()
2049 },
2050 Text::new("This text has\na border but no padding."),
2051 TextFont::from_font_size(FontSize::Px(10.)),
2052 TextLayout::justify(text_justify),
2053 BorderColor::all(RED),
2054 Outline {
2055 width: px(2),
2056 color: Color::WHITE,
2057 ..Default::default()
2058 },
2059 ));
2060
2061 builder.spawn((
2062 Node {
2063 padding: px(20).all(),
2064 ..default()
2065 },
2066 Text::new("This text has\npadding but no border."),
2067 TextFont::from_font_size(FontSize::Px(10.)),
2068 TextLayout::justify(text_justify),
2069 Outline {
2070 width: px(2),
2071 color: Color::WHITE,
2072 ..Default::default()
2073 },
2074 ));
2075
2076 builder.spawn((
2077 Node {
2078 border: px(10).all(),
2079 padding: px(20).all(),
2080 ..default()
2081 },
2082 Text::new("This text has\nborder and padding."),
2083 TextFont::from_font_size(FontSize::Px(10.)),
2084 TextLayout::justify(text_justify),
2085 BorderColor::all(RED),
2086 Outline {
2087 width: px(2),
2088 color: Color::WHITE,
2089 ..Default::default()
2090 },
2091 ));
2092
2093 builder.spawn((
2094 Node {
2095 border: px(10).left(),
2096 ..default()
2097 },
2098 Text::new("This text has\na left border and no padding."),
2099 TextFont::from_font_size(FontSize::Px(10.)),
2100 TextLayout::justify(text_justify),
2101 BorderColor::all(RED),
2102 Outline {
2103 width: px(2),
2104 color: Color::WHITE,
2105 ..Default::default()
2106 },
2107 ));
2108
2109 builder.spawn((
2110 Node {
2111 border: px(10).right(),
2112 ..default()
2113 },
2114 Text::new("This text has\na right border and no padding."),
2115 TextFont::from_font_size(FontSize::Px(10.)),
2116 TextLayout::justify(text_justify),
2117 BorderColor::all(RED),
2118 Outline {
2119 width: px(2),
2120 color: Color::WHITE,
2121 ..Default::default()
2122 },
2123 ));
2124
2125 builder.spawn((
2126 Node {
2127 padding: px(20).top().with_right(px(20)),
2128 ..default()
2129 },
2130 Text::new("This text has\npadding on its top and right."),
2131 TextFont::from_font_size(FontSize::Px(10.)),
2132 TextLayout::justify(text_justify),
2133 BorderColor::all(RED),
2134 Outline {
2135 width: px(2),
2136 color: Color::WHITE,
2137 ..Default::default()
2138 },
2139 ));
2140
2141 builder.spawn((
2142 Node {
2143 padding: px(20).bottom().with_left(px(20)),
2144 ..default()
2145 },
2146 Text::new("This text has\npadding on its bottom and left."),
2147 TextFont::from_font_size(FontSize::Px(10.)),
2148 TextLayout::justify(text_justify),
2149 BorderColor::all(RED),
2150 Outline {
2151 width: px(2),
2152 color: Color::WHITE,
2153 ..Default::default()
2154 },
2155 ));
2156
2157 builder.spawn((
2158 Node {
2159 padding: px(20).top().with_left(px(20)),
2160 border: px(10).bottom().with_right(px(10)),
2161 ..default()
2162 },
2163 Text::new(
2164 "This text has\npadding on its top and left\nand a border on its bottom and right.",
2165 ),
2166 TextFont::from_font_size(FontSize::Px(10.)),
2167 TextLayout::justify(text_justify),
2168 BorderColor::all(RED),
2169 Outline {
2170 width: px(2),
2171 color: Color::WHITE,
2172 ..Default::default()
2173 },
2174 ));
2175 });
2176 }
2177 });
2178 }
2179}
2180
2181mod editable_text {
2182 use bevy::color::palettes::css::YELLOW;
2183 use bevy::prelude::*;
2184 use bevy::text::EditableText;
2185 use bevy::text::TextEdit;
2186 use bevy::ui::widget::TextScroll;
2187
2188 pub fn setup(mut commands: Commands) {
2189 commands.spawn((Camera2d, DespawnOnExit(super::Scene::EditableText)));
2190 commands.spawn((
2191 Node {
2192 flex_direction: FlexDirection::Column,
2193 align_items: AlignItems::Center,
2194 justify_content: JustifyContent::Center,
2195 width: vw(100),
2196 height: vh(100),
2197 row_gap: px(25.),
2198 ..default()
2199 },
2200 DespawnOnExit(super::Scene::EditableText),
2201 children![
2202 (
2203 EditableText {
2204 pending_edits: vec![TextEdit::Insert("Single line EditableText".into())],
2205 ..default()
2206 },
2207 Node {
2208 width: px(200.),
2209 border: px(2).all(),
2210 ..default()
2211 },
2212 BorderColor::all(YELLOW),
2213 ),
2214 (
2215 EditableText {
2216 pending_edits: vec![
2217 TextEdit::Insert(
2218 "1. Multiline EditableText\n2.\n3.\n4.\n5.\n6.\n7.\n8.\n9.\n10."
2219 .into()
2220 ),
2221 TextEdit::TextStart(false),
2222 ],
2223 visible_lines: Some(8.),
2224 ..default()
2225 },
2226 TextScroll::default(),
2227 Node {
2228 width: px(350.),
2229 border: px(2).all(),
2230 ..default()
2231 },
2232 BorderColor::all(YELLOW),
2233 ),
2234 (
2235 EditableText {
2236 pending_edits: vec![
2237 TextEdit::Insert(
2238 "1. Multiline EditableText\n2.\n3.\n4.\n5.\n6.\n7.\n8.\n9.\n10."
2239 .into()
2240 ),
2241 TextEdit::TextEnd(true),
2242 ],
2243 visible_lines: Some(8.),
2244 ..default()
2245 },
2246 TextScroll::default(),
2247 Node {
2248 width: px(350.),
2249 border: px(2).all(),
2250 ..default()
2251 },
2252 BorderColor::all(YELLOW),
2253 ),
2254 ],
2255 ));
2256 }
2257}