Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Improve ergonomics and reduce boilerplate around creating text elements. #5343

Closed
wants to merge 13 commits into from
2 changes: 1 addition & 1 deletion crates/bevy_render/src/mesh/mesh/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
151 changes: 127 additions & 24 deletions crates/bevy_text/src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,54 +14,86 @@ 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,
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 {
tim-blackbird marked this conversation as resolved.
Show resolved Hide resolved
Self {
sections: sections.into_iter().collect(),
alignment: Default::default(),
}
}

/// Returns this [`Text`] with a new [`TextAlignment`].
pub fn with_alignment(mut self, alignment: TextAlignment) -> Self {
self.alignment = alignment;
self
}
}

#[derive(Debug, Default, Clone, FromReflect, Reflect)]
Expand All @@ -70,18 +102,89 @@ pub struct TextSection {
pub style: TextStyle,
}

impl TextSection {
/// Create a new [`TextSection`].
pub fn new(text: impl Into<String>, style: TextStyle) -> Self {
Self {
value: text.into(),
style,
}
}

/// Create an empty [`TextSection`] from a style. Useful when the text will be set dynamically.
pub fn from_style(style: TextStyle) -> Self {
Self {
value: Default::default(),
style,
}
}
}

#[derive(Debug, Clone, Copy, Reflect)]
pub struct TextAlignment {
pub vertical: VerticalAlign,
pub horizontal: HorizontalAlign,
}

impl TextAlignment {
tim-blackbird marked this conversation as resolved.
Show resolved Hide resolved
/// 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
}
}

Expand Down
22 changes: 6 additions & 16 deletions examples/2d/text2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand Down
8 changes: 3 additions & 5 deletions examples/async_tasks/external_source_external_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions examples/ecs/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,13 @@ fn setup_menu(mut commands: Commands, asset_server: Res<AssetServer>) {
})
.with_children(|parent| {
parent.spawn_bundle(TextBundle {
text: Text::with_section(
text: Text::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),
},
Default::default(),
),
..default()
});
Expand Down
6 changes: 2 additions & 4 deletions examples/games/alien_cake_addict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu

// scoreboard
commands.spawn_bundle(TextBundle {
text: Text::with_section(
text: Text::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 {
position_type: PositionType::Absolute,
Expand Down Expand Up @@ -384,14 +383,13 @@ fn display_score(mut commands: Commands, asset_server: Res<AssetServer>, game: R
})
.with_children(|parent| {
parent.spawn_bundle(TextBundle {
text: Text::with_section(
text: Text::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),
},
Default::default(),
),
..default()
});
Expand Down
36 changes: 15 additions & 21 deletions examples/games/breakout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,27 +217,21 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {

// 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,
},
},
TextSection {
value: "".to_string(),
style: TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: SCOREBOARD_FONT_SIZE,
color: SCORE_COLOR,
},
text: Text::from_sections([
TextSection::new(
"Score: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: SCOREBOARD_FONT_SIZE,
color: TEXT_COLOR,
},
],
..default()
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: SCOREBOARD_FONT_SIZE,
color: SCORE_COLOR,
}),
]),
style: Style {
position_type: PositionType::Absolute,
position: UiRect {
Expand Down Expand Up @@ -351,7 +345,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(
Expand Down
Loading