flex_layout/
flex_layout.rs

1//! Demonstrates how the `AlignItems` and `JustifyContent` properties can be composed to layout text.
2use bevy::prelude::*;
3
4const ALIGN_ITEMS_COLOR: Color = Color::srgb(1., 0.066, 0.349);
5const JUSTIFY_CONTENT_COLOR: Color = Color::srgb(0.102, 0.522, 1.);
6const MARGIN: Val = Val::Px(12.);
7
8fn main() {
9    App::new()
10        .add_plugins(DefaultPlugins.set(WindowPlugin {
11            primary_window: Some(Window {
12                title: "Bevy Flex Layout Example".to_string(),
13                ..Default::default()
14            }),
15            ..Default::default()
16        }))
17        .add_systems(Startup, spawn_layout)
18        .run();
19}
20
21fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
22    let font = asset_server.load("fonts/FiraSans-Bold.ttf");
23    commands.spawn(Camera2d);
24    commands
25        .spawn((
26            Node {
27                // fill the entire window
28                width: percent(100),
29                height: percent(100),
30                flex_direction: FlexDirection::Column,
31                align_items: AlignItems::Center,
32                padding: UiRect::all(MARGIN),
33                row_gap: MARGIN,
34                ..Default::default()
35            },
36            BackgroundColor(Color::BLACK),
37        ))
38        .with_children(|builder| {
39            // spawn the key
40            builder
41                .spawn(Node {
42                    flex_direction: FlexDirection::Row,
43                    ..default()
44                })
45                .with_children(|builder| {
46                    spawn_nested_text_bundle(
47                        builder,
48                        font.clone(),
49                        ALIGN_ITEMS_COLOR,
50                        UiRect::right(MARGIN),
51                        "AlignItems",
52                    );
53                    spawn_nested_text_bundle(
54                        builder,
55                        font.clone(),
56                        JUSTIFY_CONTENT_COLOR,
57                        UiRect::default(),
58                        "JustifyContent",
59                    );
60                });
61
62            builder
63                .spawn(Node {
64                    width: percent(100),
65                    height: percent(100),
66                    flex_direction: FlexDirection::Column,
67                    row_gap: MARGIN,
68                    ..default()
69                })
70                .with_children(|builder| {
71                    // spawn one child node for each combination of `AlignItems` and `JustifyContent`
72                    let justifications = [
73                        JustifyContent::FlexStart,
74                        JustifyContent::Center,
75                        JustifyContent::FlexEnd,
76                        JustifyContent::SpaceEvenly,
77                        JustifyContent::SpaceAround,
78                        JustifyContent::SpaceBetween,
79                    ];
80                    let alignments = [
81                        AlignItems::Baseline,
82                        AlignItems::FlexStart,
83                        AlignItems::Center,
84                        AlignItems::FlexEnd,
85                        AlignItems::Stretch,
86                    ];
87                    for align_items in alignments {
88                        builder
89                            .spawn(Node {
90                                width: percent(100),
91                                height: percent(100),
92                                flex_direction: FlexDirection::Row,
93                                column_gap: MARGIN,
94                                ..Default::default()
95                            })
96                            .with_children(|builder| {
97                                for justify_content in justifications {
98                                    spawn_child_node(
99                                        builder,
100                                        font.clone(),
101                                        align_items,
102                                        justify_content,
103                                    );
104                                }
105                            });
106                    }
107                });
108        });
109}
110
111fn spawn_child_node(
112    builder: &mut ChildSpawnerCommands,
113    font: Handle<Font>,
114    align_items: AlignItems,
115    justify_content: JustifyContent,
116) {
117    builder
118        .spawn((
119            Node {
120                flex_direction: FlexDirection::Column,
121                align_items,
122                justify_content,
123                width: percent(100),
124                height: percent(100),
125                ..default()
126            },
127            BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
128        ))
129        .with_children(|builder| {
130            let labels = [
131                (format!("{align_items:?}"), ALIGN_ITEMS_COLOR, 0.),
132                (format!("{justify_content:?}"), JUSTIFY_CONTENT_COLOR, 3.),
133            ];
134            for (text, color, top_margin) in labels {
135                // We nest the text within a parent node because margins and padding can't be directly applied to text nodes currently.
136                spawn_nested_text_bundle(
137                    builder,
138                    font.clone(),
139                    color,
140                    UiRect::top(px(top_margin)),
141                    &text,
142                );
143            }
144        });
145}
146
147fn spawn_nested_text_bundle(
148    builder: &mut ChildSpawnerCommands,
149    font: Handle<Font>,
150    background_color: Color,
151    margin: UiRect,
152    text: &str,
153) {
154    builder
155        .spawn((
156            Node {
157                margin,
158                padding: UiRect::axes(px(5), px(1)),
159                ..default()
160            },
161            BackgroundColor(background_color),
162        ))
163        .with_children(|builder| {
164            builder.spawn((
165                Text::new(text),
166                TextFont { font, ..default() },
167                TextColor::BLACK,
168            ));
169        });
170}