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