1use argh::FromArgs;
4use bevy::{
5 diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
6 ecs::component::Mutable,
7 prelude::*,
8 text::FontAtlasSet,
9 window::{PresentMode, WindowResolution},
10 winit::WinitSettings,
11};
12
13const LOREM_TEXT_1: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do \
14eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \
15nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
16const LOREM_TEXT_2: &str = "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
17Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
18
19#[derive(Component)]
20struct Lorem(bool);
21
22#[derive(Component)]
23struct NumberSpan;
24
25#[derive(FromArgs, Resource)]
26struct Args {
28 #[argh(switch)]
30 set_font_changed: bool,
31
32 #[argh(switch)]
34 respawn: bool,
35
36 #[argh(switch)]
38 clear_font_atlases: bool,
39
40 #[argh(switch)]
42 animate: bool,
43}
44
45fn main() {
46 let mut app = App::new();
47
48 app.add_plugins((
49 DefaultPlugins.set(WindowPlugin {
50 primary_window: Some(Window {
51 present_mode: PresentMode::AutoNoVsync,
52 resolution: WindowResolution::new(1920, 1080).with_scale_factor_override(1.0),
53 ..default()
54 }),
55 ..default()
56 }),
57 FrameTimeDiagnosticsPlugin::default(),
58 LogDiagnosticsPlugin::default(),
59 ))
60 .insert_resource(WinitSettings::continuous())
61 .add_systems(Startup, (setup_camera, setup_text));
62
63 #[cfg(not(target_arch = "wasm32"))]
65 let args: Args = argh::from_env();
66 #[cfg(target_arch = "wasm32")]
67 let args = Args::from_args(&[], &[]).unwrap();
68
69 if args.set_font_changed {
70 app.add_systems(Update, set_changed::<TextFont>);
71 }
72
73 if args.respawn {
74 app.add_systems(Update, (despawn_layout, setup_text).chain());
75 }
76
77 if args.clear_font_atlases {
78 app.add_systems(Update, clear_all_font_atlases);
79 }
80
81 if args.animate {
82 app.add_systems(Update, update_lorem_text);
83 app.add_systems(Update, update_number_text);
84 }
85
86 app.run();
87}
88
89#[derive(Component)]
90struct ManyTextRoot;
91
92fn setup_camera(mut commands: Commands) {
93 warn!(include_str!("warning_string.txt"));
94 commands.spawn(Camera2d);
95}
96
97fn setup_text(mut commands: Commands, asset_server: Res<AssetServer>) {
98 commands
99 .spawn((
100 Node {
101 display: Display::Grid,
102 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
103 width: percent(100),
104 height: percent(100),
105 column_gap: px(2.),
106 ..default()
107 },
108 ManyTextRoot,
109 ))
110 .with_children(|parent| {
111 for font_path in [
112 "fonts/EBGaramond12-Regular.otf",
113 "fonts/FiraMono-Medium.ttf",
114 "fonts/FiraSans-Bold.ttf",
115 "fonts/MonaSans-VariableFont.ttf",
116 ] {
117 let font = asset_server.load(font_path);
118 let text_font = TextFont {
119 font: font.into(),
120 font_size: px(10).into(),
121 ..default()
122 };
123 parent
124 .spawn((
125 Node {
126 flex_direction: FlexDirection::Column,
127 border: px(1.).all(),
128 padding: px(1.).all(),
129 row_gap: px(2.),
130 ..default()
131 },
132 BorderColor::all(Color::WHITE),
133 ))
134 .with_children(|parent| {
135 parent.spawn((Text(font_path.to_string()), text_font.clone()));
136 for justify in [
137 Justify::Left,
138 Justify::Center,
139 Justify::Right,
140 Justify::Justified,
141 ] {
142 for linebreak in [LineBreak::AnyCharacter, LineBreak::WordBoundary] {
143 parent.spawn((
144 Text(format!("Justify::{justify:?}, LineBreak::{linebreak:?}")),
145 text_font.clone(),
146 TextColor::from(bevy::color::palettes::css::YELLOW),
147 ));
148 let layout = TextLayout { justify, linebreak };
149 parent.spawn((
150 Text::new(LOREM_TEXT_1),
151 Lorem(false),
152 layout,
153 text_font.clone().with_font_size(10.),
154 TextColor::from(bevy::color::palettes::css::NAVY),
155 ));
156 parent.spawn((
157 Text::new(LOREM_TEXT_2),
158 Lorem(true),
159 layout,
160 text_font.clone().with_font_size(11.),
161 TextColor::from(bevy::color::palettes::css::PALE_GREEN),
162 ));
163 }
164 }
165
166 parent
167 .spawn((
168 Text::new(LOREM_TEXT_1),
169 Lorem(false),
170 text_font.clone().with_font_size(12.),
171 TextColor::from(bevy::color::palettes::css::MISTY_ROSE),
172 ))
173 .with_child((TextSpan::new(" "), text_font.clone().with_font_size(13.)))
174 .with_child((
175 TextSpan::new(LOREM_TEXT_2),
176 Lorem(true),
177 text_font.clone().with_font_size(13.),
178 TextColor::from(bevy::color::palettes::css::MAROON),
179 ));
180
181 parent
182 .spawn((
183 Text::default(),
184 TextLayout::linebreak(LineBreak::AnyCharacter),
185 ))
186 .with_children(|parent| {
187 for i in (0..10).cycle().take(100) {
188 parent.spawn((
189 TextSpan(i.to_string()),
190 text_font.clone().with_font_size((6 + i) as f32),
191 NumberSpan,
192 ));
193 }
194 });
195 });
196 }
197 });
198}
199
200fn set_changed<C: Component<Mutability = Mutable>>(mut component_query: Query<&mut C>) {
201 for mut component in &mut component_query {
202 component.set_changed();
203 }
204}
205
206fn despawn_layout(mut commands: Commands, root_node: Single<Entity, With<ManyTextRoot>>) {
207 commands.entity(*root_node).despawn();
208}
209
210fn clear_all_font_atlases(mut font_atlases: ResMut<FontAtlasSet>) {
211 font_atlases.clear();
212}
213
214fn update_lorem_text(mut lorem_text_query: Query<(&mut Text, &mut Lorem)>) {
215 for (mut text, mut lorem) in &mut lorem_text_query {
216 if lorem.0 {
217 text.0.clear();
218 text.0.push_str(LOREM_TEXT_1);
219 } else {
220 text.0.clear();
221 text.0.push_str(LOREM_TEXT_2);
222 }
223
224 lorem.0 = !lorem.0;
225 }
226}
227
228fn update_number_text(mut n: Local<u32>, mut number_spans: Query<&mut TextSpan, With<NumberSpan>>) {
229 for mut text in &mut number_spans {
230 text.0 = format!("{}", (text.0.parse::<u32>().unwrap() + *n) % 10);
231 }
232
233 *n = (*n + 1) % 10;
234}