use bevy::prelude::*;
use bevy::text::{LetterSpacing, RemSize};
#[derive(Component)]
struct LetterSpacingLabel;
#[derive(Component)]
struct AnimatedLetterSpacing;
#[derive(Resource, Default)]
enum SpacingMode {
#[default]
Px,
Rem,
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<SpacingMode>()
.add_systems(Startup, setup)
.add_systems(Update, (update_letter_spacing, toggle_mode))
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
commands
.spawn(Node {
width: percent(100),
height: percent(100),
..default()
})
.with_children(|parent| {
parent
.spawn(Node {
width: percent(100),
height: percent(100),
align_items: AlignItems::Center,
padding: UiRect::axes(vw(5), vh(10)),
row_gap: vh(6),
flex_direction: FlexDirection::Column,
..default()
})
.with_children(|parent| {
parent.spawn((
Text::new("HELLO"),
Underline,
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(6.0),
..default()
},
Node {
padding: vh(2).bottom(),
..default()
},
));
parent
.spawn(Node {
flex_direction: FlexDirection::Column,
width: percent(100.0),
..default()
})
.with_children(|parent| {
parent.spawn((
Text::new("Justify::Left"),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(2.0),
..default()
},
));
parent.spawn((
Text::new("letter spacing"),
AnimatedLetterSpacing,
TextLayout::justify(Justify::Left),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(6.0),
..default()
},
Node {
width: percent(100.0),
..default()
},
LetterSpacing::Px(0.0),
));
});
parent
.spawn(Node {
flex_direction: FlexDirection::Column,
width: percent(100.0),
..default()
})
.with_children(|parent| {
parent.spawn((
Text::new("Justify::Center"),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(2.0),
..default()
},
));
parent.spawn((
Text::new("letter spacing"),
AnimatedLetterSpacing,
TextLayout::justify(Justify::Center),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(6.0),
..default()
},
Node {
width: percent(100.0),
..default()
},
LetterSpacing::Px(0.0),
));
});
parent
.spawn(Node {
flex_direction: FlexDirection::Column,
width: percent(100.0),
..default()
})
.with_children(|parent| {
parent.spawn((
Text::new("Justify::Right"),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(2.0),
..default()
},
));
parent.spawn((
Text::new("letter spacing"),
AnimatedLetterSpacing,
TextLayout::justify(Justify::Right),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(6.0),
..default()
},
Node {
width: percent(100.0),
..default()
},
LetterSpacing::Px(0.0),
));
});
});
parent.spawn((
Text::new("LetterSpacing::Px(0.0)"),
LetterSpacingLabel,
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(3.0),
..default()
},
Node {
position_type: PositionType::Absolute,
bottom: vh(2.0),
left: vw(2.0),
..default()
},
));
parent.spawn((
Text::new("← → to adjust Space to toggle Px / Rem"),
TextFont {
font: font.clone().into(),
font_size: FontSize::Vh(2.5),
..default()
},
Node {
position_type: PositionType::Absolute,
bottom: vh(2.0),
right: vw(2.0),
..default()
},
));
});
}
fn toggle_mode(
keyboard: Res<ButtonInput<KeyCode>>,
mut mode: ResMut<SpacingMode>,
rem_size: Res<RemSize>,
mut query: Query<&mut LetterSpacing, With<AnimatedLetterSpacing>>,
mut label_query: Query<&mut Text, With<LetterSpacingLabel>>,
) {
if !keyboard.just_pressed(KeyCode::Space) {
return;
}
for mut spacing in &mut query {
let new_spacing = match *spacing {
LetterSpacing::Px(v) => {
*mode = SpacingMode::Rem;
LetterSpacing::Rem(v / rem_size.0)
}
LetterSpacing::Rem(v) => {
*mode = SpacingMode::Px;
LetterSpacing::Px(v * rem_size.0)
}
};
*spacing = new_spacing;
}
for mut text in &mut label_query {
match *mode {
SpacingMode::Px => {
if let Some(LetterSpacing::Px(v)) = query.iter().next().copied() {
text.0 = format!("LetterSpacing::Px({:.1})", v);
}
}
SpacingMode::Rem => {
if let Some(LetterSpacing::Rem(v)) = query.iter().next().copied() {
text.0 = format!("LetterSpacing::Rem({:.2})", v);
}
}
}
}
}
fn update_letter_spacing(
keyboard: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut LetterSpacing, With<AnimatedLetterSpacing>>,
mut label_query: Query<&mut Text, With<LetterSpacingLabel>>,
) {
let delta = if keyboard.pressed(KeyCode::ArrowRight) {
0.5
} else if keyboard.pressed(KeyCode::ArrowLeft) {
-0.5
} else {
return;
};
for mut spacing in &mut query {
match *spacing {
LetterSpacing::Px(current) => {
let new_value = (current + delta).clamp(-100.0, 100.0);
*spacing = LetterSpacing::Px(new_value);
for mut text in &mut label_query {
text.0 = format!("LetterSpacing::Px({:.1})", new_value);
}
}
LetterSpacing::Rem(current) => {
let new_value = (current + delta * 0.1).clamp(-10.0, 10.0);
*spacing = LetterSpacing::Rem(new_value);
for mut text in &mut label_query {
text.0 = format!("LetterSpacing::Rem({:.2})", new_value);
}
}
}
}
}