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

[bevy_text] Enable shifting text position within a Text component #14030

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bevy_text/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions crates/bevy_text/src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand All @@ -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.),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/asset/multi_asset_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ fn setup_ui(mut commands: Commands) {
color: Color::BLACK,
..Default::default()
},
..Default::default()
}],
justify: JustifyText::Right,
..Default::default()
Expand Down
6 changes: 6 additions & 0 deletions examples/input/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
font: font.clone_weak(),
..default()
},
..default()
},
TextSection {
value: "false\n".to_string(),
Expand All @@ -51,13 +52,15 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
font_size: 30.0,
..default()
},
..default()
},
TextSection {
value: "IME Active: ".to_string(),
style: TextStyle {
font: font.clone_weak(),
..default()
},
..default()
},
TextSection {
value: "false\n".to_string(),
Expand All @@ -66,6 +69,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
font_size: 30.0,
..default()
},
..default()
},
TextSection {
value: "click to toggle IME, press return to start a new line\n\n".to_string(),
Expand All @@ -74,6 +78,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
font_size: 18.0,
..default()
},
..default()
},
TextSection {
value: "".to_string(),
Expand All @@ -82,6 +87,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
font_size: 25.0,
..default()
},
..default()
},
])
.with_style(Style {
Expand Down
1 change: 1 addition & 0 deletions examples/stress_tests/many_glyphs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ fn setup(mut commands: Commands) {
font_size: 4.,
..default()
},
..default()
}],
justify: JustifyText::Left,
linebreak_behavior: BreakLineOn::AnyCharacter,
Expand Down
2 changes: 2 additions & 0 deletions examples/stress_tests/text_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
font_size: (4 + i % 10) as f32,
color: BLUE.into(),
},
..default()
},
TextSection {
value: "pipeline".repeat(i),
Expand All @@ -57,6 +58,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
font_size: (4 + i % 11) as f32,
color: YELLOW.into(),
},
..default()
},
]
})
Expand Down
5 changes: 5 additions & 0 deletions examples/tools/gamepad_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
41 changes: 40 additions & 1 deletion examples/ui/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ 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();
}

// A unit struct to help identify the FPS UI component, since there may be many Text components
#[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;
Expand Down Expand Up @@ -84,6 +91,28 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
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`.
Expand Down Expand Up @@ -142,3 +171,13 @@ fn text_update_system(
}
}
}

fn text_wave_system(time: Res<Time>, mut query: Query<&mut Text, With<WavyText>>) {
for mut text in &mut query {
for (i, section) in text.sections.iter_mut().enumerate() {
let seconds = (time.elapsed_seconds() + (i as f32 / 10.0)) % 2.0;
let seconds = f32::sin(seconds * std::f32::consts::PI);
section.offset = Vec2::new(0.0, seconds * 40.0);
}
}
}
1 change: 1 addition & 0 deletions examples/ui/text_wrap_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
sections: vec![TextSection {
value: message.clone(),
style: text_style.clone(),
..Default::default()
}],
justify: JustifyText::Left,
linebreak_behavior,
Expand Down