Skip to content

Adding TextBounds to Text2d causes weird offsets #18789

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

Closed
keithsharp opened this issue Apr 10, 2025 · 2 comments
Closed

Adding TextBounds to Text2d causes weird offsets #18789

keithsharp opened this issue Apr 10, 2025 · 2 comments
Labels
A-Text Rendering and layout for characters C-Bug An unexpected or incorrect behavior S-Needs-Investigation This issue requires detective work to figure out what's going wrong

Comments

@keithsharp
Copy link

Bevy 0.15.3; Rust 1.86.0; macOS 15.3.2.

AdapterInfo { name: "Apple M3 Max", vendor: 0, device: 0, device_type: IntegratedGpu, driver: "", driver_info: "", backend: Metal }

Initial discussion in #18679 and might be related to #12319.

I wanted to draw a 2d shape such as a square and have some text centred, vertically and horizontally, on top of the shape. Using code such as:

commands.spawn(
    // Create mesh with shape here
).with_children(|parent| {
    Text2d("My text string".to_string()),
    TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
    TextBounds::new_horizontal(BOUNDS_WIDTH),
});

I was expecting the text to be centred, however the TextBounds component, especially combined with wrapping, appears to be adding weird offsets depending on the length of the string, the length of words within the string, and where the line breaks can be inserted.

This image shows the output of my reproducer code:
Image

The code to reproduce this image (without the annotations):

use bevy::color::palettes::css::{DIM_GRAY, WHITE};
use bevy::prelude::*;
use bevy::text::TextBounds;

const BOUNDS_WIDTH: f32 = 80.0;

fn main() {
    App::new()
        .insert_resource(ClearColor(Color::Srgba(DIM_GRAY)))
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                title: "TextBounds".to_string(),
                resolution: (300.0, 400.0).into(),
                ..default()
            }),
            ..default()
        }))
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn(Camera2d);

    let white = materials.add(Color::Srgba(WHITE));
    let square = meshes.add(Rectangle::new(100.0, 100.0));

    // Top Left
    commands
        .spawn((
            Mesh2d(square.clone()),
            MeshMaterial2d(white.clone()),
            Transform::from_xyz(-60.0, 120.0, 0.0),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text2d("No wrap".to_string()),
                TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
                TextColor(Color::BLACK),
                TextFont {
                    font_size: 15.0,
                    ..default()
                },
            ));
        });

    // Top Right
    commands
        .spawn((
            Mesh2d(square.clone()),
            MeshMaterial2d(white.clone()),
            Transform::from_xyz(60.0, 120.0, 0.0),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text2d("No wrap".to_string()),
                TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
                TextBounds::new_horizontal(BOUNDS_WIDTH),
                TextColor(Color::BLACK),
                TextFont {
                    font_size: 15.0,
                    ..default()
                },
            ));
        });

    // Middle Left
    commands
        .spawn((
            Mesh2d(square.clone()),
            MeshMaterial2d(white.clone()),
            Transform::from_xyz(-60.0, 0.0, 0.0),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text2d("Long with wrap".to_string()),
                TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
                TextBounds::new_horizontal(BOUNDS_WIDTH),
                TextColor(Color::BLACK),
                TextFont {
                    font_size: 15.0,
                    ..default()
                },
            ));
        });

    // Middle Right
    commands
        .spawn((
            Mesh2d(square.clone()),
            MeshMaterial2d(white.clone()),
            Transform::from_xyz(60.0, 0.0, 0.0),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text2d("Long with wrap".to_string()),
                TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
                TextBounds::new_horizontal(BOUNDS_WIDTH),
                Transform::from_xyz(-(BOUNDS_WIDTH / 4.0), 0.0, 0.0),
                TextColor(Color::BLACK),
                TextFont {
                    font_size: 15.0,
                    ..default()
                },
            ));
        });

    // Bottom Left
    commands
        .spawn((
            Mesh2d(square.clone()),
            MeshMaterial2d(white.clone()),
            Transform::from_xyz(-60.0, -120.0, 0.0),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text2d("Longword with wrap".to_string()),
                TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
                TextBounds::new_horizontal(BOUNDS_WIDTH),
                Transform::from_xyz(-(BOUNDS_WIDTH / 4.0), 0.0, 0.0),
                TextColor(Color::BLACK),
                TextFont {
                    font_size: 15.0,
                    ..default()
                },
            ));
        });

    // Bottom Right
    commands
        .spawn((
            Mesh2d(square.clone()),
            MeshMaterial2d(white.clone()),
            Transform::from_xyz(60.0, -120.0, 0.0),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text2d("Longword with wrap".to_string()),
                TextLayout::new(JustifyText::Center, LineBreak::WordBoundary),
                TextBounds::new_horizontal(BOUNDS_WIDTH),
                Transform::from_xyz(0.0, 0.0, 0.0),
                TextColor(Color::BLACK),
                TextFont {
                    font_size: 15.0,
                    ..default()
                },
            ));
        });
}
@keithsharp keithsharp added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Apr 10, 2025
@alice-i-cecile alice-i-cecile added A-Text Rendering and layout for characters S-Needs-Investigation This issue requires detective work to figure out what's going wrong and removed S-Needs-Triage This issue needs to be labelled labels Apr 10, 2025
@ickshonpe
Copy link
Contributor

This might be fixed in 0.16? I'll try the examples later.

@keithsharp
Copy link
Author

I can confirm this is fixed in 0.16.0-rc.5.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Text Rendering and layout for characters C-Bug An unexpected or incorrect behavior S-Needs-Investigation This issue requires detective work to figure out what's going wrong
Projects
None yet
Development

No branches or pull requests

3 participants