bevy/crates
ickshonpe 3f99a3e8cd
Text 2d alignment fix (#17365)
# Objective

`Text2d` ignores `TextBounds` when calculating the offset for text
aligment.
On main a text entity positioned in the center of the window with center
justification and 600px horizontal text bounds isn't centered like it
should be but shifted off to the right:
<img width="305" alt="hellox"
src="https://github.com/user-attachments/assets/8896c6f0-1b9f-4633-9c12-1de6eff5f3e1"
/>
(second example in the testing section below)

Fixes #14266

I already had a PR in review for this (#14270) but it used post layout
adjustment (which we want to avoid) and ignored `TextBounds`.

## Solution

* If `TextBounds` are present for an axis, use them instead of the size
of the computed text layout size to calculate the offset.
* Adjust the vertical offset of text so it's top is aligned with the top
of the texts bounding rect (when present).

## Testing

```
use bevy::prelude::*;
use bevy::color::palettes;
use bevy::sprite::Anchor;
use bevy::text::TextBounds;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn example(commands: &mut Commands, dest: Vec3, justify: JustifyText) {
    commands.spawn((
        Sprite {
            color: palettes::css::YELLOW.into(),
            custom_size: Some(10. * Vec2::ONE),
            anchor: Anchor::Center,
            ..Default::default()
        },
        Transform::from_translation(dest),
    ));

    for a in [
        Anchor::TopLeft,
        Anchor::TopRight,
        Anchor::BottomRight,
        Anchor::BottomLeft,
    ] {
        commands.spawn((
            Text2d(format!("L R\n{:?}\n{:?}", a, justify)),
            TextFont {
                font_size: 14.0,
                ..default()
            },
            TextLayout {
                justify,
                ..Default::default()
            },
            TextBounds::new(300., 75.),
            Transform::from_translation(dest + Vec3::Z),
            a,
        ));
    }
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());

    for (i, j) in [
        JustifyText::Left,
        JustifyText::Right,
        JustifyText::Center,
        JustifyText::Justified,
    ]
    .into_iter()
    .enumerate()
    {
        example(&mut commands, (300. - 150. * i as f32) * Vec3::Y, j);
    }

    commands.spawn(Sprite {
        color: palettes::css::YELLOW.into(),
        custom_size: Some(10. * Vec2::ONE),
        anchor: Anchor::Center,
        ..Default::default()
    });
}
```

<img width="566" alt="cap"
src="https://github.com/user-attachments/assets/e6a98fa5-80b2-4380-a9b7-155bb49635b8"
/>

This probably looks really confusing but it should make sense if you
imagine each block of text surrounded by a 300x75 rectangle that is
anchored to the center of the yellow square.

# 

```
use bevy::prelude::*;
use bevy::sprite::Anchor;
use bevy::text::TextBounds;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());

    commands.spawn((
        Text2d::new("hello"),
        TextFont {
            font_size: 60.0,
            ..default()
        },
        TextLayout::new_with_justify(JustifyText::Center),
        TextBounds::new(600., 200.),
        Anchor::Center,
    ));
}
```

<img width="338" alt="hello"
src="https://github.com/user-attachments/assets/e5e89364-afda-4baa-aca8-df4cdacbb4ed"
/>

The text being above the center is intended. When `TextBounds` are
present, the text block's offset is calculated using its `TextBounds`
not the layout size returned by cosmic-text.

# 

Probably we should add a vertical alignment setting for Text2d. Didn't
do it here as this is intended for a 0.15.2 release.
2025-01-20 20:54:32 +00:00
..
bevy_a11y Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_animation Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_app Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_asset Switch bevy_asset to core::prelude (#17442) 2025-01-20 04:14:48 +00:00
bevy_audio Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_color Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_core_pipeline Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_derive Bump Version after Release (#17176) 2025-01-06 00:04:44 +00:00
bevy_dev_tools Rename TargetCamera to UiTargetCamera (#17403) 2025-01-19 19:56:57 +00:00
bevy_diagnostic Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_dylib Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_ecs FromWorld derive macro (#17352) 2025-01-20 20:51:30 +00:00
bevy_encase_derive Bump Version after Release (#17176) 2025-01-06 00:04:44 +00:00
bevy_gilrs Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_gizmos Reworked Segment types into their cartesian forms (#17404) 2025-01-19 03:54:45 +00:00
bevy_gltf Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_image Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_input Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_input_focus Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_internal Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_log Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_macro_utils Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_math Reworked Segment types into their cartesian forms (#17404) 2025-01-19 03:54:45 +00:00
bevy_mesh Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_mikktspace Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_pbr Forward decals (port of bevy_contact_projective_decals) (#16600) 2025-01-15 02:31:30 +00:00
bevy_picking Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_platform_support Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_ptr Update safety docs for Ptr::assert_unique (#17394) 2025-01-16 03:25:19 +00:00
bevy_reflect Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_remote Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_render Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_scene Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_sprite Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_state Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_tasks Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_text Text 2d alignment fix (#17365) 2025-01-20 20:54:32 +00:00
bevy_time Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_transform Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
bevy_ui Rename TargetCamera to UiTargetCamera (#17403) 2025-01-19 19:56:57 +00:00
bevy_utils Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00
bevy_window Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374) 2025-01-15 01:14:58 +00:00
bevy_winit Create bevy_platform_support Crate (#17250) 2025-01-20 20:45:30 +00:00