diff --git a/crates/bevy_text/src/pipeline.rs b/crates/bevy_text/src/pipeline.rs index 7f7004e71ee04..381b1686ff400 100644 --- a/crates/bevy_text/src/pipeline.rs +++ b/crates/bevy_text/src/pipeline.rs @@ -218,7 +218,7 @@ impl TextPipeline { YAxisOrientation::BottomToTop => box_size.y - y, }; - let position = Vec2::new(x, y); + let position = Vec2::new(x, y) + sections[section_index].offset; // TODO: recreate the byte index, that keeps track of where a cursor is, // when glyphs are not limited to single byte representation, relevant for #1319 diff --git a/crates/bevy_text/src/text.rs b/crates/bevy_text/src/text.rs index 39723a06ee413..865ebcdfd63ac 100644 --- a/crates/bevy_text/src/text.rs +++ b/crates/bevy_text/src/text.rs @@ -2,6 +2,7 @@ use bevy_asset::Handle; use bevy_color::Color; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; +use bevy_math::Vec2; use bevy_reflect::prelude::*; use bevy_utils::default; use cosmic_text::{Buffer, Metrics}; @@ -127,12 +128,24 @@ impl Text { } /// Contains the value of the text in a section and how it should be styled. -#[derive(Debug, Default, Clone, Reflect)] +#[derive(Debug, Clone, Reflect)] pub struct TextSection { /// The content (in `String` form) of the text in the section. pub value: String, /// The style of the text in the section, including the font face, font size, and color. pub style: TextStyle, + /// An offset to be applied to each glyph in the section. + pub offset: Vec2, +} + +impl Default for TextSection { + fn default() -> Self { + Self { + value: Default::default(), + style: Default::default(), + offset: Vec2::new(0., 0.), + } + } } impl TextSection { @@ -141,14 +154,18 @@ impl TextSection { Self { value: value.into(), style, + + ..Default::default() } } /// 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, + + value: String::new(), + offset: Vec2::new(0., 0.), } } } diff --git a/examples/asset/multi_asset_sync.rs b/examples/asset/multi_asset_sync.rs index db2306ae75918..04a87ebc80c23 100644 --- a/examples/asset/multi_asset_sync.rs +++ b/examples/asset/multi_asset_sync.rs @@ -189,6 +189,7 @@ fn setup_ui(mut commands: Commands) { color: Color::BLACK, ..Default::default() }, + ..Default::default() }], justify: JustifyText::Right, ..Default::default() diff --git a/examples/input/text_input.rs b/examples/input/text_input.rs index 2adfad7cf4399..4d65f8df877d4 100644 --- a/examples/input/text_input.rs +++ b/examples/input/text_input.rs @@ -43,6 +43,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res) { font: font.clone_weak(), ..default() }, + ..default() }, TextSection { value: "false\n".to_string(), @@ -51,6 +52,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res) { font_size: 30.0, ..default() }, + ..default() }, TextSection { value: "IME Active: ".to_string(), @@ -58,6 +60,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res) { font: font.clone_weak(), ..default() }, + ..default() }, TextSection { value: "false\n".to_string(), @@ -66,6 +69,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res) { font_size: 30.0, ..default() }, + ..default() }, TextSection { value: "click to toggle IME, press return to start a new line\n\n".to_string(), @@ -74,6 +78,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res) { font_size: 18.0, ..default() }, + ..default() }, TextSection { value: "".to_string(), @@ -82,6 +87,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res) { font_size: 25.0, ..default() }, + ..default() }, ]) .with_style(Style { diff --git a/examples/stress_tests/many_glyphs.rs b/examples/stress_tests/many_glyphs.rs index 51a31f8cd622f..06567046a503c 100644 --- a/examples/stress_tests/many_glyphs.rs +++ b/examples/stress_tests/many_glyphs.rs @@ -52,6 +52,7 @@ fn setup(mut commands: Commands) { font_size: 4., ..default() }, + ..default() }], justify: JustifyText::Left, linebreak_behavior: BreakLineOn::AnyCharacter, diff --git a/examples/stress_tests/text_pipeline.rs b/examples/stress_tests/text_pipeline.rs index 1dc69af6ed9e2..52b56a4c4ea54 100644 --- a/examples/stress_tests/text_pipeline.rs +++ b/examples/stress_tests/text_pipeline.rs @@ -49,6 +49,7 @@ fn spawn(mut commands: Commands, asset_server: Res) { font_size: (4 + i % 10) as f32, color: BLUE.into(), }, + ..default() }, TextSection { value: "pipeline".repeat(i), @@ -57,6 +58,7 @@ fn spawn(mut commands: Commands, asset_server: Res) { font_size: (4 + i % 11) as f32, color: YELLOW.into(), }, + ..default() }, ] }) diff --git a/examples/tools/gamepad_viewer.rs b/examples/tools/gamepad_viewer.rs index cfcb51529fbb6..feadbfa9d31f4 100644 --- a/examples/tools/gamepad_viewer.rs +++ b/examples/tools/gamepad_viewer.rs @@ -321,14 +321,17 @@ fn setup_sticks( TextSection { value: format!("{:.3}", 0.), style: style.clone(), + ..default() }, TextSection { value: ", ".to_string(), style: style.clone(), + ..default() }, TextSection { value: format!("{:.3}", 0.), style, + ..default() }, ]), text_anchor: Anchor::BottomCenter, @@ -424,10 +427,12 @@ fn setup_connected(mut commands: Commands) { TextSection { value: "Connected Gamepads:\n".to_string(), style: text_style.clone(), + ..default() }, TextSection { value: "None".to_string(), style: text_style, + ..default() }, ]), style: Style { diff --git a/examples/ui/text.rs b/examples/ui/text.rs index 197c8ddf4c8b0..fa4604f17c914 100644 --- a/examples/ui/text.rs +++ b/examples/ui/text.rs @@ -13,7 +13,10 @@ fn main() { App::new() .add_plugins((DefaultPlugins, FrameTimeDiagnosticsPlugin)) .add_systems(Startup, setup) - .add_systems(Update, (text_update_system, text_color_system)) + .add_systems( + Update, + (text_update_system, text_color_system, text_wave_system), + ) .run(); } @@ -21,6 +24,10 @@ fn main() { #[derive(Component)] struct FpsText; +// A unit struct to help identify the wavy Text component +#[derive(Component)] +struct WavyText; + // A unit struct to help identify the color-changing Text component #[derive(Component)] struct ColorText; @@ -84,6 +91,28 @@ fn setup(mut commands: Commands, asset_server: Res) { FpsText, )); + commands.spawn(( + TextBundle { + text: Text::from_sections((0..100).map(|i| TextSection { + value: (i % 10).to_string(), + style: TextStyle { + font: asset_server.load("fonts/FiraMono-Medium.ttf"), + font_size: 10.0, + color: GOLD.into(), + }, + offset: Vec2::new(0., i as f32), + })), + style: Style { + position_type: PositionType::Absolute, + bottom: Val::Px(60.0), + left: Val::Px(15.0), + ..default() + }, + ..default() + }, + WavyText, + )); + #[cfg(feature = "default_font")] commands.spawn( // Here we are able to call the `From` method instead of creating a new `TextSection`. @@ -142,3 +171,13 @@ fn text_update_system( } } } + +fn text_wave_system(time: Res