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
41fn main() {
42 let mut app = App::new();
43
44 app.add_plugins((
45 DefaultPlugins.set(WindowPlugin {
46 primary_window: Some(Window {
47 present_mode: PresentMode::AutoNoVsync,
48 resolution: WindowResolution::new(1920, 1080).with_scale_factor_override(1.0),
49 ..default()
50 }),
51 ..default()
52 }),
53 FrameTimeDiagnosticsPlugin::default(),
54 LogDiagnosticsPlugin::default(),
55 ))
56 .insert_resource(WinitSettings::continuous())
57 .add_systems(Startup, (setup_camera, setup_text));
58
59 #[cfg(not(target_arch = "wasm32"))]
61 let args: Args = argh::from_env();
62 #[cfg(target_arch = "wasm32")]
63 let args = Args::from_args(&[], &[]).unwrap();
64
65 if args.set_font_changed {
66 app.add_systems(Update, set_changed::<TextFont>);
67 }
68
69 if args.respawn {
70 app.add_systems(Update, (despawn_layout, setup_text).chain());
71 }
72
73 if args.clear_font_atlases {
74 app.add_systems(Update, clear_all_font_atlases);
75 }
76
77 app.add_systems(Update, update_lorem_text);
78
79 app.add_systems(Update, update_number_text);
80
81 app.run();
82}
83
84#[derive(Component)]
85struct ManyTextRoot;
86
87fn setup_camera(mut commands: Commands) {
88 warn!(include_str!("warning_string.txt"));
89 commands.spawn(Camera2d);
90}
91
92fn setup_text(mut commands: Commands, asset_server: Res<AssetServer>) {
93 commands
94 .spawn((
95 Node {
96 display: Display::Grid,
97 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
98 width: percent(100),
99 height: percent(100),
100 column_gap: px(2.),
101 ..default()
102 },
103 ManyTextRoot,
104 ))
105 .with_children(|parent| {
106 for font_path in [
107 "fonts/EBGaramond12-Regular.otf",
108 "fonts/FiraMono-Medium.ttf",
109 "fonts/FiraSans-Bold.ttf",
110 "fonts/MonaSans-VariableFont.ttf",
111 ] {
112 let font = asset_server.load(font_path);
113 let text_font = TextFont {
114 font: font.into(),
115 font_size: px(10).into(),
116 ..default()
117 };
118 parent
119 .spawn((
120 Node {
121 flex_direction: FlexDirection::Column,
122 border: px(1.).all(),
123 padding: px(1.).all(),
124 row_gap: px(2.),
125 ..default()
126 },
127 BorderColor::all(Color::WHITE),
128 ))
129 .with_children(|parent| {
130 parent.spawn((Text(font_path.to_string()), text_font.clone()));
131 for justify in [
132 Justify::Left,
133 Justify::Center,
134 Justify::Right,
135 Justify::Justified,
136 ] {
137 for linebreak in [LineBreak::AnyCharacter, LineBreak::WordBoundary] {
138 parent.spawn((
139 Text(format!("Justify::{justify:?}, LineBreak::{linebreak:?}")),
140 text_font.clone(),
141 TextColor::from(bevy::color::palettes::css::YELLOW),
142 ));
143 let layout = TextLayout { justify, linebreak };
144 parent.spawn((
145 Text::new(LOREM_TEXT_1),
146 Lorem(false),
147 layout,
148 text_font.clone().with_font_size(10.),
149 TextColor::from(bevy::color::palettes::css::NAVY),
150 ));
151 parent.spawn((
152 Text::new(LOREM_TEXT_2),
153 Lorem(true),
154 layout,
155 text_font.clone().with_font_size(11.),
156 TextColor::from(bevy::color::palettes::css::PALE_GREEN),
157 ));
158 }
159 }
160
161 parent
162 .spawn((
163 Text::new(LOREM_TEXT_1),
164 Lorem(false),
165 text_font.clone().with_font_size(12.),
166 TextColor::from(bevy::color::palettes::css::MISTY_ROSE),
167 ))
168 .with_child((TextSpan::new(" "), text_font.clone().with_font_size(13.)))
169 .with_child((
170 TextSpan::new(LOREM_TEXT_2),
171 Lorem(true),
172 text_font.clone().with_font_size(13.),
173 TextColor::from(bevy::color::palettes::css::MAROON),
174 ));
175
176 parent
177 .spawn((
178 Text::default(),
179 TextLayout::linebreak(LineBreak::AnyCharacter),
180 ))
181 .with_children(|parent| {
182 for i in (0..10).cycle().take(100) {
183 parent.spawn((
184 TextSpan(i.to_string()),
185 text_font.clone().with_font_size((6 + i) as f32),
186 NumberSpan,
187 ));
188 }
189 });
190 });
191 }
192 });
193}
194
195fn set_changed<C: Component<Mutability = Mutable>>(mut component_query: Query<&mut C>) {
196 for mut component in &mut component_query {
197 component.set_changed();
198 }
199}
200
201fn despawn_layout(mut commands: Commands, root_node: Single<Entity, With<ManyTextRoot>>) {
202 commands.entity(*root_node).despawn();
203}
204
205fn clear_all_font_atlases(mut font_atlases: ResMut<FontAtlasSet>) {
206 font_atlases.clear();
207}
208
209fn update_lorem_text(mut lorem_text_query: Query<(&mut Text, &mut Lorem)>) {
210 for (mut text, mut lorem) in &mut lorem_text_query {
211 if lorem.0 {
212 text.0.clear();
213 text.0.push_str(LOREM_TEXT_1);
214 } else {
215 text.0.clear();
216 text.0.push_str(LOREM_TEXT_2);
217 }
218
219 lorem.0 = !lorem.0;
220 }
221}
222
223fn update_number_text(mut n: Local<u32>, mut number_spans: Query<&mut TextSpan, With<NumberSpan>>) {
224 for mut text in &mut number_spans {
225 text.0 = format!("{}", (text.0.parse::<u32>().unwrap() + *n) % 10);
226 }
227
228 *n = (*n + 1) % 10;
229}