Skip to content

Commit b983261

Browse files
committed
[bevy_text] Add offset to TextSection
This makes it possible to shift text (such as for animations), while still laying it out with `cosmic-text`.
1 parent 09d86bf commit b983261

File tree

9 files changed

+76
-4
lines changed

9 files changed

+76
-4
lines changed

crates/bevy_text/src/pipeline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl TextPipeline {
218218
YAxisOrientation::BottomToTop => box_size.y - y,
219219
};
220220

221-
let position = Vec2::new(x, y);
221+
let position = Vec2::new(x, y) + sections[section_index].offset;
222222

223223
// TODO: recreate the byte index, that keeps track of where a cursor is,
224224
// when glyphs are not limited to single byte representation, relevant for #1319

crates/bevy_text/src/text.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use bevy_asset::Handle;
22
use bevy_color::Color;
33
use bevy_derive::{Deref, DerefMut};
44
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
5+
use bevy_math::Vec2;
56
use bevy_reflect::prelude::*;
67
use bevy_utils::default;
78
use cosmic_text::{Buffer, Metrics};
@@ -127,12 +128,24 @@ impl Text {
127128
}
128129

129130
/// Contains the value of the text in a section and how it should be styled.
130-
#[derive(Debug, Default, Clone, Reflect)]
131+
#[derive(Debug, Clone, Reflect)]
131132
pub struct TextSection {
132133
/// The content (in `String` form) of the text in the section.
133134
pub value: String,
134135
/// The style of the text in the section, including the font face, font size, and color.
135136
pub style: TextStyle,
137+
/// An offset to be applied to each glyph in the section.
138+
pub offset: Vec2,
139+
}
140+
141+
impl Default for TextSection {
142+
fn default() -> Self {
143+
Self {
144+
value: Default::default(),
145+
style: Default::default(),
146+
offset: Vec2::new(0., 0.),
147+
}
148+
}
136149
}
137150

138151
impl TextSection {
@@ -141,14 +154,18 @@ impl TextSection {
141154
Self {
142155
value: value.into(),
143156
style,
157+
158+
..Default::default()
144159
}
145160
}
146161

147162
/// Create an empty [`TextSection`] from a style. Useful when the value will be set dynamically.
148163
pub const fn from_style(style: TextStyle) -> Self {
149164
Self {
150-
value: String::new(),
151165
style,
166+
167+
value: String::new(),
168+
offset: Vec2::new(0., 0.),
152169
}
153170
}
154171
}

examples/asset/multi_asset_sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ fn setup_ui(mut commands: Commands) {
189189
color: Color::BLACK,
190190
..Default::default()
191191
},
192+
..Default::default()
192193
}],
193194
justify: JustifyText::Right,
194195
..Default::default()

examples/input/text_input.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
4343
font: font.clone_weak(),
4444
..default()
4545
},
46+
..default()
4647
},
4748
TextSection {
4849
value: "false\n".to_string(),
@@ -51,13 +52,15 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
5152
font_size: 30.0,
5253
..default()
5354
},
55+
..default()
5456
},
5557
TextSection {
5658
value: "IME Active: ".to_string(),
5759
style: TextStyle {
5860
font: font.clone_weak(),
5961
..default()
6062
},
63+
..default()
6164
},
6265
TextSection {
6366
value: "false\n".to_string(),
@@ -66,6 +69,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
6669
font_size: 30.0,
6770
..default()
6871
},
72+
..default()
6973
},
7074
TextSection {
7175
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<AssetServer>) {
7478
font_size: 18.0,
7579
..default()
7680
},
81+
..default()
7782
},
7883
TextSection {
7984
value: "".to_string(),
@@ -82,6 +87,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
8287
font_size: 25.0,
8388
..default()
8489
},
90+
..default()
8591
},
8692
])
8793
.with_style(Style {

examples/stress_tests/many_glyphs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ fn setup(mut commands: Commands) {
5252
font_size: 4.,
5353
..default()
5454
},
55+
..default()
5556
}],
5657
justify: JustifyText::Left,
5758
linebreak_behavior: BreakLineOn::AnyCharacter,

examples/stress_tests/text_pipeline.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
4949
font_size: (4 + i % 10) as f32,
5050
color: BLUE.into(),
5151
},
52+
..default()
5253
},
5354
TextSection {
5455
value: "pipeline".repeat(i),
@@ -57,6 +58,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
5758
font_size: (4 + i % 11) as f32,
5859
color: YELLOW.into(),
5960
},
61+
..default()
6062
},
6163
]
6264
})

examples/tools/gamepad_viewer.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,17 @@ fn setup_sticks(
321321
TextSection {
322322
value: format!("{:.3}", 0.),
323323
style: style.clone(),
324+
..default()
324325
},
325326
TextSection {
326327
value: ", ".to_string(),
327328
style: style.clone(),
329+
..default()
328330
},
329331
TextSection {
330332
value: format!("{:.3}", 0.),
331333
style,
334+
..default()
332335
},
333336
]),
334337
text_anchor: Anchor::BottomCenter,
@@ -424,10 +427,12 @@ fn setup_connected(mut commands: Commands) {
424427
TextSection {
425428
value: "Connected Gamepads:\n".to_string(),
426429
style: text_style.clone(),
430+
..default()
427431
},
428432
TextSection {
429433
value: "None".to_string(),
430434
style: text_style,
435+
..default()
431436
},
432437
]),
433438
style: Style {

examples/ui/text.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ fn main() {
1313
App::new()
1414
.add_plugins((DefaultPlugins, FrameTimeDiagnosticsPlugin))
1515
.add_systems(Startup, setup)
16-
.add_systems(Update, (text_update_system, text_color_system))
16+
.add_systems(
17+
Update,
18+
(text_update_system, text_color_system, text_wave_system),
19+
)
1720
.run();
1821
}
1922

2023
// A unit struct to help identify the FPS UI component, since there may be many Text components
2124
#[derive(Component)]
2225
struct FpsText;
2326

27+
// A unit struct to help identify the wavy Text component
28+
#[derive(Component)]
29+
struct WavyText;
30+
2431
// A unit struct to help identify the color-changing Text component
2532
#[derive(Component)]
2633
struct ColorText;
@@ -84,6 +91,28 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
8491
FpsText,
8592
));
8693

94+
commands.spawn((
95+
TextBundle {
96+
text: Text::from_sections((0..100).map(|i| TextSection {
97+
value: (i % 10).to_string(),
98+
style: TextStyle {
99+
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
100+
font_size: 10.0,
101+
color: GOLD.into(),
102+
},
103+
offset: Vec2::new(0., i as f32),
104+
})),
105+
style: Style {
106+
position_type: PositionType::Absolute,
107+
bottom: Val::Px(60.0),
108+
left: Val::Px(15.0),
109+
..default()
110+
},
111+
..default()
112+
},
113+
WavyText,
114+
));
115+
87116
#[cfg(feature = "default_font")]
88117
commands.spawn(
89118
// Here we are able to call the `From` method instead of creating a new `TextSection`.
@@ -142,3 +171,13 @@ fn text_update_system(
142171
}
143172
}
144173
}
174+
175+
fn text_wave_system(time: Res<Time>, mut query: Query<&mut Text, With<WavyText>>) {
176+
for mut text in &mut query {
177+
for (i, section) in text.sections.iter_mut().enumerate() {
178+
let seconds = (time.elapsed_seconds() + (i as f32 / 10.0)) % 2.0;
179+
let seconds = f32::sin(seconds * std::f32::consts::PI);
180+
section.offset = Vec2::new(0.0, seconds * 40.0);
181+
}
182+
}
183+
}

examples/ui/text_wrap_debug.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
126126
sections: vec![TextSection {
127127
value: message.clone(),
128128
style: text_style.clone(),
129+
..Default::default()
129130
}],
130131
justify: JustifyText::Left,
131132
linebreak_behavior,

0 commit comments

Comments
 (0)