Improve ergonomics and reduce boilerplate around creating text elements. (#5343)
# Objective
Creating UI elements is very boilerplate-y with lots of indentation.
This PR aims to reduce boilerplate around creating text elements.
## Changelog
* Renamed `Text::with_section` to `from_section`.
It no longer takes a `TextAlignment` as argument, as the vast majority of cases left it `Default::default()`.
* Added `Text::from_sections` which creates a `Text` from a list of `TextSections`.
Reduces line-count and reduces indentation by one level.
* Added `Text::with_alignment`.
A builder style method for setting the `TextAlignment` of a `Text`.
* Added `TextSection::new`.
Does not reduce line count, but reduces character count and made it easier to read. No more `.to_string()` calls!
* Added `TextSection::from_style` which creates an empty `TextSection` with a style.
No more empty strings! Reduces indentation.
* Added `TextAlignment::CENTER` and friends.
* Added methods to `TextBundle`. `from_section`, `from_sections`, `with_text_alignment` and `with_style`.
## Note for reviewers.
Because of the nature of these changes I recommend setting diff view to 'split'.
~~Look for the book icon~~ cog in the top-left of the Files changed tab.
Have fun reviewing ❤️
<sup> >:D </sup>
## Migration Guide
`Text::with_section` was renamed to `from_section` and no longer takes a `TextAlignment` as argument.
Use `with_alignment` to set the alignment instead.
Co-authored-by: devil-ira <justthecooldude@gmail.com>
This commit is contained in:
parent
35f99a6ccc
commit
9f906fdc8b
@ -522,7 +522,7 @@ mod tests {
|
||||
Err(error) => error,
|
||||
};
|
||||
assert_eq!(
|
||||
format!("{}", error),
|
||||
error.to_string(),
|
||||
"cannot convert VertexAttributeValues::Uint32x4 to alloc::vec::Vec<u32>"
|
||||
);
|
||||
assert_eq!(format!("{:?}", error),
|
||||
|
||||
@ -14,54 +14,83 @@ pub struct Text {
|
||||
}
|
||||
|
||||
impl Text {
|
||||
/// Constructs a [`Text`] with (initially) one section.
|
||||
/// Constructs a [`Text`] with a single section.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_asset::{AssetServer, Handle};
|
||||
/// # use bevy_asset::Handle;
|
||||
/// # use bevy_render::color::Color;
|
||||
/// # use bevy_text::{Font, Text, TextAlignment, TextStyle, HorizontalAlign, VerticalAlign};
|
||||
/// #
|
||||
/// # let font_handle: Handle<Font> = Default::default();
|
||||
/// #
|
||||
/// // basic usage
|
||||
/// let hello_world = Text::with_section(
|
||||
/// "hello world!".to_string(),
|
||||
/// // Basic usage.
|
||||
/// let hello_world = Text::from_section(
|
||||
/// // Accepts a String or any type that converts into a String, such as &str.
|
||||
/// "hello world!",
|
||||
/// TextStyle {
|
||||
/// font: font_handle.clone(),
|
||||
/// font_size: 60.0,
|
||||
/// color: Color::WHITE,
|
||||
/// },
|
||||
/// TextAlignment {
|
||||
/// vertical: VerticalAlign::Center,
|
||||
/// horizontal: HorizontalAlign::Center,
|
||||
/// },
|
||||
/// );
|
||||
///
|
||||
/// let hello_bevy = Text::with_section(
|
||||
/// // accepts a String or any type that converts into a String, such as &str
|
||||
/// let hello_bevy = Text::from_section(
|
||||
/// "hello bevy!",
|
||||
/// TextStyle {
|
||||
/// font: font_handle,
|
||||
/// font_size: 60.0,
|
||||
/// color: Color::WHITE,
|
||||
/// },
|
||||
/// // you can still use Default
|
||||
/// Default::default(),
|
||||
/// );
|
||||
/// ) // You can still add an alignment.
|
||||
/// .with_alignment(TextAlignment::CENTER);
|
||||
/// ```
|
||||
pub fn with_section<S: Into<String>>(
|
||||
value: S,
|
||||
style: TextStyle,
|
||||
alignment: TextAlignment,
|
||||
) -> Self {
|
||||
pub fn from_section(value: impl Into<String>, style: TextStyle) -> Self {
|
||||
Self {
|
||||
sections: vec![TextSection {
|
||||
value: value.into(),
|
||||
style,
|
||||
}],
|
||||
alignment,
|
||||
sections: vec![TextSection::new(value, style)],
|
||||
alignment: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a [`Text`] from a list of sections.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_asset::Handle;
|
||||
/// # use bevy_render::color::Color;
|
||||
/// # use bevy_text::{Font, Text, TextStyle, TextSection};
|
||||
/// #
|
||||
/// # let font_handle: Handle<Font> = Default::default();
|
||||
/// #
|
||||
/// let hello_world = Text::from_sections([
|
||||
/// TextSection::new(
|
||||
/// "Hello, ",
|
||||
/// TextStyle {
|
||||
/// font: font_handle.clone(),
|
||||
/// font_size: 60.0,
|
||||
/// color: Color::BLUE,
|
||||
/// },
|
||||
/// ),
|
||||
/// TextSection::new(
|
||||
/// "World!",
|
||||
/// TextStyle {
|
||||
/// font: font_handle,
|
||||
/// font_size: 60.0,
|
||||
/// color: Color::RED,
|
||||
/// },
|
||||
/// ),
|
||||
/// ]);
|
||||
/// ```
|
||||
pub fn from_sections(sections: impl IntoIterator<Item = TextSection>) -> Self {
|
||||
Self {
|
||||
sections: sections.into_iter().collect(),
|
||||
alignment: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this [`Text`] with a new [`TextAlignment`].
|
||||
pub const fn with_alignment(mut self, alignment: TextAlignment) -> Self {
|
||||
self.alignment = alignment;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, FromReflect, Reflect)]
|
||||
@ -70,18 +99,89 @@ pub struct TextSection {
|
||||
pub style: TextStyle,
|
||||
}
|
||||
|
||||
impl TextSection {
|
||||
/// Create a new [`TextSection`].
|
||||
pub fn new(value: impl Into<String>, style: TextStyle) -> Self {
|
||||
Self {
|
||||
value: value.into(),
|
||||
style,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an empty [`TextSection`] from a style. Useful when the value will be set dynamically.
|
||||
pub const fn from_style(style: TextStyle) -> Self {
|
||||
Self {
|
||||
value: String::new(),
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Reflect)]
|
||||
pub struct TextAlignment {
|
||||
pub vertical: VerticalAlign,
|
||||
pub horizontal: HorizontalAlign,
|
||||
}
|
||||
|
||||
impl TextAlignment {
|
||||
/// A [`TextAlignment`] set to the top-left.
|
||||
pub const TOP_LEFT: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Top,
|
||||
horizontal: HorizontalAlign::Left,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to the top-center.
|
||||
pub const TOP_CENTER: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Top,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to the the top-right.
|
||||
pub const TOP_RIGHT: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Top,
|
||||
horizontal: HorizontalAlign::Right,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to center the center-left.
|
||||
pub const CENTER_LEFT: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Left,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to center on both axes.
|
||||
pub const CENTER: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to the center-right.
|
||||
pub const CENTER_RIGHT: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Right,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to the bottom-left.
|
||||
pub const BOTTOM_LEFT: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Bottom,
|
||||
horizontal: HorizontalAlign::Left,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to the bottom-center.
|
||||
pub const BOTTOM_CENTER: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Bottom,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
};
|
||||
|
||||
/// A [`TextAlignment`] set to the bottom-right.
|
||||
pub const BOTTOM_RIGHT: Self = TextAlignment {
|
||||
vertical: VerticalAlign::Bottom,
|
||||
horizontal: HorizontalAlign::Right,
|
||||
};
|
||||
}
|
||||
|
||||
impl Default for TextAlignment {
|
||||
fn default() -> Self {
|
||||
TextAlignment {
|
||||
vertical: VerticalAlign::Top,
|
||||
horizontal: HorizontalAlign::Left,
|
||||
}
|
||||
TextAlignment::TOP_LEFT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ use bevy_render::{
|
||||
camera::Camera, extract_component::ExtractComponent, prelude::ComputedVisibility,
|
||||
view::Visibility,
|
||||
};
|
||||
use bevy_text::Text;
|
||||
use bevy_text::{Text, TextAlignment, TextSection, TextStyle};
|
||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
|
||||
/// The basic UI node
|
||||
@ -89,6 +89,40 @@ pub struct TextBundle {
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
impl TextBundle {
|
||||
/// Create a [`TextBundle`] from a single section.
|
||||
///
|
||||
/// See [`Text::from_section`] for usage.
|
||||
pub fn from_section(value: impl Into<String>, style: TextStyle) -> Self {
|
||||
Self {
|
||||
text: Text::from_section(value, style),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`TextBundle`] from a list of sections.
|
||||
///
|
||||
/// See [`Text::from_sections`] for usage.
|
||||
pub fn from_sections(sections: impl IntoIterator<Item = TextSection>) -> Self {
|
||||
Self {
|
||||
text: Text::from_sections(sections),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this [`TextBundle`] with a new [`TextAlignment`] on [`Text`].
|
||||
pub const fn with_text_alignment(mut self, alignment: TextAlignment) -> Self {
|
||||
self.text.alignment = alignment;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns this [`TextBundle`] with a new [`Style`].
|
||||
pub const fn with_style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextBundle {
|
||||
fn default() -> Self {
|
||||
TextBundle {
|
||||
|
||||
@ -31,30 +31,28 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
};
|
||||
let text_alignment = TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
};
|
||||
let text_alignment = TextAlignment::CENTER;
|
||||
// 2d camera
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
// Demonstrate changing translation
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section("translation", text_style.clone(), text_alignment),
|
||||
text: Text::from_section("translation", text_style.clone())
|
||||
.with_alignment(text_alignment),
|
||||
..default()
|
||||
})
|
||||
.insert(AnimateTranslation);
|
||||
// Demonstrate changing rotation
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section("rotation", text_style.clone(), text_alignment),
|
||||
text: Text::from_section("rotation", text_style.clone()).with_alignment(text_alignment),
|
||||
..default()
|
||||
})
|
||||
.insert(AnimateRotation);
|
||||
// Demonstrate changing scale
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section("scale", text_style.clone(), text_alignment),
|
||||
text: Text::from_section("scale", text_style.clone()).with_alignment(text_alignment),
|
||||
..default()
|
||||
})
|
||||
.insert(AnimateScale);
|
||||
@ -70,16 +68,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
transform: Transform::from_translation(box_position.extend(0.0)),
|
||||
..default()
|
||||
});
|
||||
let text_alignment_topleft = TextAlignment {
|
||||
vertical: VerticalAlign::Top,
|
||||
horizontal: HorizontalAlign::Left,
|
||||
};
|
||||
commands.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section(
|
||||
"this text wraps in the box",
|
||||
text_style,
|
||||
text_alignment_topleft,
|
||||
),
|
||||
text: Text::from_section("this text wraps in the box", text_style),
|
||||
text_2d_bounds: Text2dBounds {
|
||||
// Wrap text in the rectangle
|
||||
size: box_size,
|
||||
|
||||
@ -62,13 +62,11 @@ fn spawn_text(
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
};
|
||||
let text_alignment = TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
};
|
||||
|
||||
for (per_frame, event) in reader.iter().enumerate() {
|
||||
commands.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section(format!("{}", event.0), text_style.clone(), text_alignment),
|
||||
text: Text::from_section(event.0.to_string(), text_style.clone())
|
||||
.with_alignment(TextAlignment::CENTER),
|
||||
transform: Transform::from_xyz(
|
||||
per_frame as f32 * 100.0 + rand::thread_rng().gen_range(-40.0..40.0),
|
||||
300.0,
|
||||
|
||||
@ -55,18 +55,14 @@ fn setup_menu(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Play",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.9, 0.9, 0.9),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Play",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.9, 0.9, 0.9),
|
||||
},
|
||||
));
|
||||
})
|
||||
.id();
|
||||
commands.insert_resource(MenuData { button_entity });
|
||||
|
||||
@ -150,17 +150,16 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
|
||||
game.bonus.handle = asset_server.load("models/AlienCake/cakeBirthday.glb#Scene0");
|
||||
|
||||
// scoreboard
|
||||
commands.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
commands.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Score:",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.5, 0.5, 1.0),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
style: Style {
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
top: Val::Px(5.0),
|
||||
@ -168,9 +167,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// remove all entities that are not a camera
|
||||
@ -383,17 +381,13 @@ fn display_score(mut commands: Commands, asset_server: Res<AssetServer>, game: R
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
format!("Cake eaten: {}", game.cake_eaten),
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 80.0,
|
||||
color: Color::rgb(0.5, 0.5, 1.0),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
format!("Cake eaten: {}", game.cake_eaten),
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 80.0,
|
||||
color: Color::rgb(0.5, 0.5, 1.0),
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@ -216,29 +216,23 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
.insert(Velocity(INITIAL_BALL_DIRECTION.normalize() * BALL_SPEED));
|
||||
|
||||
// Scoreboard
|
||||
commands.spawn_bundle(TextBundle {
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: "Score: ".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: SCOREBOARD_FONT_SIZE,
|
||||
color: TEXT_COLOR,
|
||||
},
|
||||
commands.spawn_bundle(
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Score: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: SCOREBOARD_FONT_SIZE,
|
||||
color: TEXT_COLOR,
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: SCOREBOARD_FONT_SIZE,
|
||||
color: SCORE_COLOR,
|
||||
},
|
||||
},
|
||||
],
|
||||
..default()
|
||||
},
|
||||
style: Style {
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: SCOREBOARD_FONT_SIZE,
|
||||
color: SCORE_COLOR,
|
||||
}),
|
||||
])
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
top: SCOREBOARD_TEXT_PADDING,
|
||||
@ -246,9 +240,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// Walls
|
||||
commands.spawn_bundle(WallBundle::new(WallLocation::Left));
|
||||
@ -351,7 +344,7 @@ fn apply_velocity(mut query: Query<(&mut Transform, &Velocity)>) {
|
||||
|
||||
fn update_scoreboard(scoreboard: Res<Scoreboard>, mut query: Query<&mut Text>) {
|
||||
let mut text = query.single_mut();
|
||||
text.sections[1].value = format!("{}", scoreboard.score);
|
||||
text.sections[1].value = scoreboard.score.to_string();
|
||||
}
|
||||
|
||||
fn check_for_collisions(
|
||||
|
||||
@ -134,37 +134,27 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res<AssetSe
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
|
||||
commands
|
||||
.spawn()
|
||||
.insert(ContributorDisplay)
|
||||
.insert_bundle(TextBundle {
|
||||
style: Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
},
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: "Contributor showcase".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
},
|
||||
],
|
||||
..default()
|
||||
},
|
||||
commands.spawn().insert(ContributorDisplay).insert_bundle(
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Contributor showcase",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
}),
|
||||
])
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds the next contributor to display and selects the entity
|
||||
|
||||
@ -165,58 +165,52 @@ mod game {
|
||||
.insert(OnGameScreen)
|
||||
.with_children(|parent| {
|
||||
// Display two lines of text, the second one with the current settings
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
margin: UiRect::all(Val::Px(50.0)),
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Will be back to the menu shortly...",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 80.0,
|
||||
color: TEXT_COLOR,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
)
|
||||
.with_style(Style {
|
||||
margin: UiRect::all(Val::Px(50.0)),
|
||||
..default()
|
||||
},
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: format!("quality: {:?}", *display_quality),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 60.0,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
}),
|
||||
);
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
format!("quality: {:?}", *display_quality),
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 60.0,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
TextSection {
|
||||
value: " - ".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 60.0,
|
||||
color: TEXT_COLOR,
|
||||
},
|
||||
),
|
||||
TextSection::new(
|
||||
" - ",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 60.0,
|
||||
color: TEXT_COLOR,
|
||||
},
|
||||
TextSection {
|
||||
value: format!("volume: {:?}", *volume),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 60.0,
|
||||
color: Color::GREEN,
|
||||
},
|
||||
),
|
||||
TextSection::new(
|
||||
format!("volume: {:?}", *volume),
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 60.0,
|
||||
color: Color::GREEN,
|
||||
},
|
||||
],
|
||||
),
|
||||
])
|
||||
.with_style(Style {
|
||||
margin: UiRect::all(Val::Px(50.0)),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
// Spawn a 5 seconds timer to trigger going back to the menu
|
||||
commands.insert_resource(GameTimer(Timer::from_seconds(5.0, false)));
|
||||
@ -432,22 +426,20 @@ mod menu {
|
||||
.insert(OnMainMenuScreen)
|
||||
.with_children(|parent| {
|
||||
// Display the game name
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
margin: UiRect::all(Val::Px(50.0)),
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Bevy Game Menu UI",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 80.0,
|
||||
color: TEXT_COLOR,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
)
|
||||
.with_style(Style {
|
||||
margin: UiRect::all(Val::Px(50.0)),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
|
||||
// Display three buttons for each action available from the main menu:
|
||||
// - new game
|
||||
@ -467,14 +459,10 @@ mod menu {
|
||||
image: UiImage(icon),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"New Game",
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"New Game",
|
||||
button_text_style.clone(),
|
||||
));
|
||||
});
|
||||
parent
|
||||
.spawn_bundle(ButtonBundle {
|
||||
@ -490,14 +478,10 @@ mod menu {
|
||||
image: UiImage(icon),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Settings",
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Settings",
|
||||
button_text_style.clone(),
|
||||
));
|
||||
});
|
||||
parent
|
||||
.spawn_bundle(ButtonBundle {
|
||||
@ -513,10 +497,7 @@ mod menu {
|
||||
image: UiImage(icon),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section("Quit", button_text_style, Default::default()),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section("Quit", button_text_style));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -529,6 +510,7 @@ mod menu {
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
|
||||
let button_text_style = TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
@ -548,55 +530,25 @@ mod menu {
|
||||
})
|
||||
.insert(OnSettingsMenuScreen)
|
||||
.with_children(|parent| {
|
||||
// Display two buttons for the submenus
|
||||
parent
|
||||
.spawn_bundle(ButtonBundle {
|
||||
style: button_style.clone(),
|
||||
color: NORMAL_BUTTON.into(),
|
||||
..default()
|
||||
})
|
||||
.insert(MenuButtonAction::SettingsDisplay)
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Display",
|
||||
for (action, text) in [
|
||||
(MenuButtonAction::SettingsDisplay, "Display"),
|
||||
(MenuButtonAction::SettingsSound, "Sound"),
|
||||
(MenuButtonAction::BackToMainMenu, "Back"),
|
||||
] {
|
||||
parent
|
||||
.spawn_bundle(ButtonBundle {
|
||||
style: button_style.clone(),
|
||||
color: NORMAL_BUTTON.into(),
|
||||
..default()
|
||||
})
|
||||
.insert(action)
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
text,
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
));
|
||||
});
|
||||
});
|
||||
parent
|
||||
.spawn_bundle(ButtonBundle {
|
||||
style: button_style.clone(),
|
||||
color: NORMAL_BUTTON.into(),
|
||||
..default()
|
||||
})
|
||||
.insert(MenuButtonAction::SettingsSound)
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Sound",
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
});
|
||||
// Display the back button to return to the main menu screen
|
||||
parent
|
||||
.spawn_bundle(ButtonBundle {
|
||||
style: button_style,
|
||||
color: NORMAL_BUTTON.into(),
|
||||
..default()
|
||||
})
|
||||
.insert(MenuButtonAction::BackToMainMenu)
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section("Back", button_text_style, Default::default()),
|
||||
..default()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -644,14 +596,10 @@ mod menu {
|
||||
})
|
||||
.with_children(|parent| {
|
||||
// Display a label for the current setting
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Display Quality",
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Display Quality",
|
||||
button_text_style.clone(),
|
||||
));
|
||||
// Display a button for each possible value
|
||||
for quality_setting in [
|
||||
DisplayQuality::Low,
|
||||
@ -667,14 +615,10 @@ mod menu {
|
||||
..default()
|
||||
});
|
||||
entity.insert(quality_setting).with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
format!("{:?}", quality_setting),
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
format!("{quality_setting:?}"),
|
||||
button_text_style.clone(),
|
||||
));
|
||||
});
|
||||
if *display_quality == quality_setting {
|
||||
entity.insert(SelectedOption);
|
||||
@ -690,10 +634,7 @@ mod menu {
|
||||
})
|
||||
.insert(MenuButtonAction::BackToSettings)
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section("Back", button_text_style, Default::default()),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section("Back", button_text_style));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -739,14 +680,10 @@ mod menu {
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Volume",
|
||||
button_text_style.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Volume",
|
||||
button_text_style.clone(),
|
||||
));
|
||||
for volume_setting in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] {
|
||||
let mut entity = parent.spawn_bundle(ButtonBundle {
|
||||
style: Style {
|
||||
@ -770,10 +707,7 @@ mod menu {
|
||||
})
|
||||
.insert(MenuButtonAction::BackToSettings)
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section("Back", button_text_style, Default::default()),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section("Back", button_text_style));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -108,23 +108,17 @@ fn setup_scene(
|
||||
..default()
|
||||
})
|
||||
.with_children(|b| {
|
||||
b.spawn_bundle(TextBundle {
|
||||
text: Text {
|
||||
sections: vec![TextSection {
|
||||
value: "Test Button".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 30.0,
|
||||
color: Color::BLACK,
|
||||
},
|
||||
}],
|
||||
alignment: TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
b.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Test Button",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 30.0,
|
||||
color: Color::BLACK,
|
||||
},
|
||||
},
|
||||
..default()
|
||||
});
|
||||
)
|
||||
.with_text_alignment(TextAlignment::CENTER),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -105,20 +105,18 @@ fn save_scene_system(world: &mut World) {
|
||||
// text example.
|
||||
fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
commands.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
commands.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Nothing to see in this window! Check the console output!",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
)
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,45 +96,36 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
commands
|
||||
.spawn_bundle(TextBundle {
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: "Bird Count: ".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 0.0),
|
||||
},
|
||||
.spawn_bundle(
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Bird Count: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 0.0),
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 1.0),
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 1.0),
|
||||
}),
|
||||
TextSection::new(
|
||||
"\nAverage FPS: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 0.0),
|
||||
},
|
||||
TextSection {
|
||||
value: "\nAverage FPS: ".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 0.0),
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 1.0),
|
||||
},
|
||||
},
|
||||
],
|
||||
..default()
|
||||
},
|
||||
style: Style {
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.0, 1.0, 1.0),
|
||||
}),
|
||||
])
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
top: Val::Px(5.0),
|
||||
@ -142,9 +133,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})
|
||||
}),
|
||||
)
|
||||
.insert(StatsText);
|
||||
|
||||
commands.insert_resource(BirdTexture(texture));
|
||||
@ -265,12 +255,12 @@ fn counter_system(
|
||||
let mut text = query.single_mut();
|
||||
|
||||
if counter.is_changed() {
|
||||
text.sections[1].value = format!("{}", counter.count);
|
||||
text.sections[1].value = counter.count.to_string();
|
||||
}
|
||||
|
||||
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
|
||||
if let Some(average) = fps.average() {
|
||||
text.sections[3].value = format!("{:.2}", average);
|
||||
text.sections[3].value = format!("{average:.2}");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -101,25 +101,18 @@ fn setup(
|
||||
});
|
||||
|
||||
// Add text to explain inputs and what is happening.
|
||||
commands.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Press the arrow keys to move the cubes. Toggle movement for yellow (1), red (2) and green (3) cubes via number keys.
|
||||
commands.spawn_bundle(TextBundle::from_section(
|
||||
"Press the arrow keys to move the cubes. Toggle movement for yellow (1), red (2) and green (3) cubes via number keys.
|
||||
|
||||
Notice how the green cube will translate further in respect to the yellow in contrast to the red cube.
|
||||
This is due to the use of its LocalTransform that is relative to the yellow cubes transform instead of the GlobalTransform as in the case of the red cube.
|
||||
The red cube is moved through its GlobalTransform and thus is unaffected by the yellows transform.",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 22.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
TextAlignment {
|
||||
horizontal: HorizontalAlign::Left,
|
||||
..default()
|
||||
},
|
||||
),
|
||||
..default()
|
||||
});
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 22.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
// This system will move all cubes that are marked as ChangeGlobal according to their global transform.
|
||||
|
||||
@ -62,17 +62,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Button",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.9, 0.9, 0.9),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Button",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 40.0,
|
||||
color: Color::rgb(0.9, 0.9, 0.9),
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@ -67,8 +67,9 @@ fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut query: Quer
|
||||
if state.timer.tick(time.delta()).finished() {
|
||||
for mut text in &mut query {
|
||||
let c = rand::random::<u8>() as char;
|
||||
if !text.sections[0].value.contains(c) {
|
||||
text.sections[0].value.push(c);
|
||||
let string = &mut text.sections[0].value;
|
||||
if !string.contains(c) {
|
||||
string.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,16 +81,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut state: ResM
|
||||
let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf");
|
||||
state.handle = font_handle.clone();
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
commands.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"a",
|
||||
TextStyle {
|
||||
font: font_handle,
|
||||
font_size: 60.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
commands.spawn_bundle(TextBundle::from_section(
|
||||
"a",
|
||||
TextStyle {
|
||||
font: font_handle,
|
||||
font_size: 60.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
@ -31,8 +31,20 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
// Text with one section
|
||||
commands
|
||||
.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
.spawn_bundle(
|
||||
// Create a TextBundle that has a Text with a single section.
|
||||
TextBundle::from_section(
|
||||
// Accepts a `String` or any type that converts into a `String`, such as `&str`
|
||||
"hello\nbevy!",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 100.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
) // Set the alignment of the Text
|
||||
.with_text_alignment(TextAlignment::TOP_CENTER)
|
||||
// Set the style of the TextBundle itself.
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
@ -41,76 +53,41 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
// Use the `Text::with_section` constructor
|
||||
text: Text::with_section(
|
||||
// Accepts a `String` or any type that converts into a `String`, such as `&str`
|
||||
"hello\nbevy!",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 100.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
// Note: You can use `Default::default()` in place of the `TextAlignment`
|
||||
TextAlignment {
|
||||
horizontal: HorizontalAlign::Center,
|
||||
..default()
|
||||
},
|
||||
),
|
||||
..default()
|
||||
})
|
||||
}),
|
||||
)
|
||||
.insert(ColorText);
|
||||
// Rich text with multiple sections
|
||||
// Text with multiple sections
|
||||
commands
|
||||
.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
.spawn_bundle(
|
||||
// Create a TextBundle that has a Text with a list of sections.
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"FPS: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::GOLD,
|
||||
}),
|
||||
])
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
},
|
||||
// Use `Text` directly
|
||||
text: Text {
|
||||
// Construct a `Vec` of `TextSection`s
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: "FPS: ".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||
font_size: 60.0,
|
||||
color: Color::GOLD,
|
||||
},
|
||||
},
|
||||
],
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})
|
||||
}),
|
||||
)
|
||||
.insert(FpsText);
|
||||
}
|
||||
|
||||
fn text_update_system(diagnostics: Res<Diagnostics>, mut query: Query<&mut Text, With<FpsText>>) {
|
||||
for mut text in &mut query {
|
||||
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
|
||||
if let Some(average) = fps.average() {
|
||||
// Update the value of the second section
|
||||
text.sections[1].value = format!("{:.2}", average);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn text_color_system(time: Res<Time>, mut query: Query<&mut Text, With<ColorText>>) {
|
||||
for mut text in &mut query {
|
||||
let seconds = time.seconds_since_startup() as f32;
|
||||
// We used the `Text::with_section` helper method, but it is still just a `Text`,
|
||||
// so to update it, we are still updating the one and only section
|
||||
|
||||
// Update the color of the first and only section.
|
||||
text.sections[0].style.color = Color::Rgba {
|
||||
red: (1.25 * seconds).sin() / 2.0 + 0.5,
|
||||
green: (0.75 * seconds).sin() / 2.0 + 0.5,
|
||||
@ -119,3 +96,14 @@ fn text_color_system(time: Res<Time>, mut query: Query<&mut Text, With<ColorText
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn text_update_system(diagnostics: Res<Diagnostics>, mut query: Query<&mut Text, With<FpsText>>) {
|
||||
for mut text in &mut query {
|
||||
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
|
||||
if let Some(average) = fps.average() {
|
||||
// Update the value of the second section
|
||||
text.sections[1].value = format!("{average:.2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,16 @@ struct TextChanges;
|
||||
fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
commands.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
commands.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"This is\ntext with\nline breaks\nin the top left",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
@ -35,20 +43,18 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
"This is\ntext with\nline breaks\nin the top left",
|
||||
}),
|
||||
);
|
||||
commands.spawn_bundle(TextBundle::from_section(
|
||||
"This text is very long, has a limited width, is centred, is positioned in the top right and is also coloured pink.",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
color: Color::rgb(0.8, 0.2, 0.7),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
commands.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
)
|
||||
.with_text_alignment(TextAlignment::CENTER)
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
@ -61,24 +67,55 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
height: Val::Undefined,
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
"This text is very long, has a limited width, is centred, is positioned in the top right and is also coloured pink.",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 50.0,
|
||||
color: Color::rgb(0.8, 0.2, 0.7),
|
||||
},
|
||||
TextAlignment {
|
||||
horizontal: HorizontalAlign::Center,
|
||||
vertical: VerticalAlign::Center,
|
||||
},
|
||||
),
|
||||
..default()
|
||||
});
|
||||
})
|
||||
);
|
||||
commands
|
||||
.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
.spawn_bundle(
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"This text changes in the bottom right",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::new(
|
||||
"\nThis text changes in the bottom right - ",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::RED,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::ORANGE_RED,
|
||||
}),
|
||||
TextSection::new(
|
||||
" fps, ",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::GREEN,
|
||||
}),
|
||||
TextSection::new(
|
||||
" ms/frame",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
),
|
||||
])
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
@ -87,65 +124,19 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: "This text changes in the bottom right".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "\nThis text changes in the bottom right - ".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::RED,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::ORANGE_RED,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: " fps, ".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::GREEN,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: " ms/frame".to_string(),
|
||||
style: TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 30.0,
|
||||
color: Color::BLUE,
|
||||
},
|
||||
},
|
||||
],
|
||||
alignment: Default::default(),
|
||||
},
|
||||
..default()
|
||||
})
|
||||
}),
|
||||
)
|
||||
.insert(TextChanges);
|
||||
commands.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
commands.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"This\ntext has\nline breaks and also a set width in the bottom left",
|
||||
TextStyle {
|
||||
font,
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
@ -158,18 +149,8 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
"This\ntext has\nline breaks and also a set width in the bottom left".to_string(),
|
||||
TextStyle {
|
||||
font,
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn change_text_system(
|
||||
|
||||
@ -29,19 +29,15 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Button 1",
|
||||
TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: 40.0,
|
||||
// Alpha channel of the color controls transparency.
|
||||
color: Color::rgba(1.0, 1.0, 1.0, 0.2),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Button 1",
|
||||
TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: 40.0,
|
||||
// Alpha channel of the color controls transparency.
|
||||
color: Color::rgba(1.0, 1.0, 1.0, 0.2),
|
||||
},
|
||||
));
|
||||
});
|
||||
|
||||
// Button with a different color,
|
||||
@ -59,18 +55,14 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
text: Text::with_section(
|
||||
"Button 2",
|
||||
TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: 40.0,
|
||||
// Alpha channel of the color controls transparency.
|
||||
color: Color::rgba(1.0, 1.0, 1.0, 0.2),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
parent.spawn_bundle(TextBundle::from_section(
|
||||
"Button 2",
|
||||
TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: 40.0,
|
||||
// Alpha channel of the color controls transparency.
|
||||
color: Color::rgba(1.0, 1.0, 1.0, 0.2),
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@ -57,22 +57,20 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
})
|
||||
.with_children(|parent| {
|
||||
// text
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
margin: UiRect::all(Val::Px(5.0)),
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Text Example",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 30.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
)
|
||||
.with_style(Style {
|
||||
margin: UiRect::all(Val::Px(5.0)),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
// right vertical fill
|
||||
@ -89,8 +87,16 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
})
|
||||
.with_children(|parent| {
|
||||
// Title
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Scrolling list",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 25.,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
size: Size::new(Val::Undefined, Val::Px(25.)),
|
||||
margin: UiRect {
|
||||
left: Val::Auto,
|
||||
@ -98,18 +104,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
"Scrolling list",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 25.,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
// List with hidden overflow
|
||||
parent
|
||||
.spawn_bundle(NodeBundle {
|
||||
@ -140,8 +136,17 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
.with_children(|parent| {
|
||||
// List items
|
||||
for i in 0..30 {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
format!("Item {i}"),
|
||||
TextStyle {
|
||||
font: asset_server
|
||||
.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
flex_shrink: 0.,
|
||||
size: Size::new(Val::Undefined, Val::Px(20.)),
|
||||
margin: UiRect {
|
||||
@ -150,19 +155,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
format!("Item {}", i),
|
||||
TextStyle {
|
||||
font: asset_server
|
||||
.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 20.,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -131,8 +131,9 @@ pub(crate) mod test_setup {
|
||||
ExampleMode::Application => "desktop_app(), reactive",
|
||||
ExampleMode::ApplicationWithRedraw => "desktop_app(), reactive, RequestRedraw sent",
|
||||
};
|
||||
query.get_single_mut().unwrap().sections[1].value = mode.to_string();
|
||||
query.get_single_mut().unwrap().sections[3].value = format!("{}", *frame);
|
||||
let mut text = query.single_mut();
|
||||
text.sections[1].value = mode.to_string();
|
||||
text.sections[3].value = frame.to_string();
|
||||
}
|
||||
|
||||
/// Set up a scene with a cube and some text
|
||||
@ -165,8 +166,36 @@ pub(crate) mod test_setup {
|
||||
});
|
||||
event.send(RequestRedraw);
|
||||
commands
|
||||
.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
.spawn_bundle(
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"Press spacebar to cycle modes\n",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::GREEN,
|
||||
}),
|
||||
TextSection::new(
|
||||
"\nFrame: ",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
),
|
||||
TextSection::from_style(TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::YELLOW,
|
||||
}),
|
||||
])
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexStart,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
@ -175,46 +204,8 @@ pub(crate) mod test_setup {
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection {
|
||||
value: "Press spacebar to cycle modes\n".into(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".into(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::GREEN,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "\nFrame: ".into(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
},
|
||||
TextSection {
|
||||
value: "".into(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 50.0,
|
||||
color: Color::YELLOW,
|
||||
},
|
||||
},
|
||||
],
|
||||
alignment: TextAlignment::default(),
|
||||
},
|
||||
..default()
|
||||
})
|
||||
}),
|
||||
)
|
||||
.insert(ModeText);
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,22 +44,20 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn_bundle(TextBundle {
|
||||
style: Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
},
|
||||
text: Text::with_section(
|
||||
parent.spawn_bundle(
|
||||
TextBundle::from_section(
|
||||
"Example text",
|
||||
TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 30.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
..default()
|
||||
});
|
||||
)
|
||||
.with_style(Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user