mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Add text wrapping support to Text2d (#4347)
# Objective Fixes #4344. ## Solution Add a new component `Text2dBounds` to `Text2dBundle` that specifies the maximum width and height of text. Text will wrap according to this size.
This commit is contained in:
parent
c7c08f95cb
commit
954022c799
3 changed files with 82 additions and 29 deletions
|
@ -1,6 +1,5 @@
|
|||
use bevy_asset::Handle;
|
||||
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
|
||||
use bevy_math::Size;
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
|
||||
use bevy_render::color::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -150,9 +149,3 @@ impl Default for TextStyle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Copy, Clone, Debug, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Text2dSize {
|
||||
pub size: Size,
|
||||
}
|
||||
|
|
|
@ -1,46 +1,63 @@
|
|||
use bevy_asset::Assets;
|
||||
use bevy_ecs::{
|
||||
bundle::Bundle,
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
query::{Changed, QueryState, With},
|
||||
reflect::ReflectComponent,
|
||||
system::{Local, Query, QuerySet, Res, ResMut},
|
||||
};
|
||||
use bevy_math::{Size, Vec3};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_render::{texture::Image, view::Visibility, RenderWorld};
|
||||
use bevy_sprite::{ExtractedSprite, ExtractedSprites, TextureAtlas};
|
||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
use bevy_window::{WindowId, Windows};
|
||||
|
||||
use crate::{
|
||||
DefaultTextPipeline, Font, FontAtlasSet, HorizontalAlign, Text, Text2dSize, TextError,
|
||||
VerticalAlign,
|
||||
DefaultTextPipeline, Font, FontAtlasSet, HorizontalAlign, Text, TextError, VerticalAlign,
|
||||
};
|
||||
|
||||
/// The calculated size of text drawn in 2D scene.
|
||||
#[derive(Component, Default, Copy, Clone, Debug, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Text2dSize {
|
||||
pub size: Size,
|
||||
}
|
||||
|
||||
/// The maximum width and height of text. The text will wrap according to the specified size.
|
||||
/// Characters out of the bounds after wrapping will be truncated. Text is aligned according to the
|
||||
/// specified `TextAlignment`.
|
||||
///
|
||||
/// Note: only characters that are completely out of the bounds will be truncated, so this is not a
|
||||
/// reliable limit if it is necessary to contain the text strictly in the bounds. Currently this
|
||||
/// component is mainly useful for text wrapping only.
|
||||
#[derive(Component, Copy, Clone, Debug, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Text2dBounds {
|
||||
pub size: Size,
|
||||
}
|
||||
|
||||
impl Default for Text2dBounds {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size: Size::new(f32::MAX, f32::MAX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The bundle of components needed to draw text in a 2D scene via a 2D `OrthographicCameraBundle`.
|
||||
/// [Example usage.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/text2d.rs)
|
||||
#[derive(Bundle, Clone, Debug)]
|
||||
#[derive(Bundle, Clone, Debug, Default)]
|
||||
pub struct Text2dBundle {
|
||||
pub text: Text,
|
||||
pub transform: Transform,
|
||||
pub global_transform: GlobalTransform,
|
||||
pub text_2d_size: Text2dSize,
|
||||
pub text_2d_bounds: Text2dBounds,
|
||||
pub visibility: Visibility,
|
||||
}
|
||||
|
||||
impl Default for Text2dBundle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
text: Default::default(),
|
||||
transform: Default::default(),
|
||||
global_transform: Default::default(),
|
||||
text_2d_size: Text2dSize {
|
||||
size: Size::default(),
|
||||
},
|
||||
visibility: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_text2d_sprite(
|
||||
mut render_world: ResMut<RenderWorld>,
|
||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||
|
@ -123,7 +140,7 @@ pub fn text2d_system(
|
|||
mut text_pipeline: ResMut<DefaultTextPipeline>,
|
||||
mut text_queries: QuerySet<(
|
||||
QueryState<Entity, (With<Text2dSize>, Changed<Text>)>,
|
||||
QueryState<(&Text, &mut Text2dSize), With<Text2dSize>>,
|
||||
QueryState<(&Text, Option<&Text2dBounds>, &mut Text2dSize), With<Text2dSize>>,
|
||||
)>,
|
||||
) {
|
||||
// Adds all entities where the text or the style has changed to the local queue
|
||||
|
@ -141,14 +158,21 @@ pub fn text2d_system(
|
|||
let mut new_queue = Vec::new();
|
||||
let mut query = text_queries.q1();
|
||||
for entity in queued_text.entities.drain(..) {
|
||||
if let Ok((text, mut calculated_size)) = query.get_mut(entity) {
|
||||
if let Ok((text, bounds, mut calculated_size)) = query.get_mut(entity) {
|
||||
let text_bounds = match bounds {
|
||||
Some(bounds) => Size {
|
||||
width: scale_value(bounds.size.width, scale_factor),
|
||||
height: scale_value(bounds.size.height, scale_factor),
|
||||
},
|
||||
None => Size::new(f32::MAX, f32::MAX),
|
||||
};
|
||||
match text_pipeline.queue_text(
|
||||
entity,
|
||||
&fonts,
|
||||
&text.sections,
|
||||
scale_factor,
|
||||
text.alignment,
|
||||
Size::new(f32::MAX, f32::MAX),
|
||||
text_bounds,
|
||||
&mut *font_atlas_set_storage,
|
||||
&mut *texture_atlases,
|
||||
&mut *textures,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy::{prelude::*, text::Text2dBounds};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
|
@ -47,10 +47,46 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
// Demonstrate changing scale
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section("scale", text_style, text_alignment),
|
||||
text: Text::with_section("scale", text_style.clone(), text_alignment),
|
||||
..default()
|
||||
})
|
||||
.insert(AnimateScale);
|
||||
// Demonstrate text wrapping
|
||||
let box_size = Size::new(300.0, 200.0);
|
||||
let box_position = Vec2::new(0.0, -250.0);
|
||||
commands.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::rgb(0.25, 0.25, 0.75),
|
||||
custom_size: Some(Vec2::new(box_size.width, box_size.height)),
|
||||
..default()
|
||||
},
|
||||
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_2d_bounds: Text2dBounds {
|
||||
// Wrap text in the rectangle
|
||||
size: box_size,
|
||||
},
|
||||
// We align text to the top-left, so this transform is the top-left corner of our text. The
|
||||
// box is centered at box_position, so it is necessary to move by half of the box size to
|
||||
// keep the text in the box.
|
||||
transform: Transform::from_xyz(
|
||||
box_position.x - box_size.width / 2.0,
|
||||
box_position.y + box_size.height / 2.0,
|
||||
1.0,
|
||||
),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
fn animate_translation(
|
||||
|
|
Loading…
Reference in a new issue