Text2d scalefactor change detection fix (#16264)

# Objective 

Text2d doesn't respond to changes to the window scalefactor.

Fixes #16223

## Solution 

In `update_text2d_layout` store the previous scale factor in a `Local`
instead and check against the current scale factor to detect changes.

It seems like previously the text wasn't updated because of a bug with
the `WindowScaleFactorChanged` event and it isn't emitted after changes
to the scale factor. That needs to be looked into, but this will work
for now.

## Testing

Really simple app that draws a big message in the middle of the window:

```
use bevy::prelude::*;

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

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands.spawn((
        Text2d::new("Hello"),
        TextFont {
            font_size: 400.,
            ..Default::default()
        },
    ));
}
```

Looks fine:
<img width="500" alt="hello1"
src="https://github.com/user-attachments/assets/5320746b-687e-4682-9e4c-bc43ab7ff9d3">

On main, after changing the monitor's scale factor:
<img width="500" alt="hello2"
src="https://github.com/user-attachments/assets/486cea16-fc44-4d66-9468-6f68905d4196">


With this PR the text maintains the same size and position after the
scale factor is changed.
This commit is contained in:
ickshonpe 2024-11-13 21:22:20 +00:00 committed by GitHub
parent bf9971f239
commit c0fbadbc4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,7 +11,6 @@ use bevy_ecs::component::Component;
use bevy_ecs::{ use bevy_ecs::{
change_detection::{DetectChanges, Ref}, change_detection::{DetectChanges, Ref},
entity::Entity, entity::Entity,
event::EventReader,
prelude::{ReflectComponent, With}, prelude::{ReflectComponent, With},
query::{Changed, Without}, query::{Changed, Without},
system::{Commands, Local, Query, Res, ResMut}, system::{Commands, Local, Query, Res, ResMut},
@ -30,7 +29,7 @@ use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, SpriteSource, Textu
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use bevy_transform::prelude::GlobalTransform; use bevy_transform::prelude::GlobalTransform;
use bevy_utils::HashSet; use bevy_utils::HashSet;
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged}; use bevy_window::{PrimaryWindow, Window};
/// [`Text2dBundle`] was removed in favor of required components. /// [`Text2dBundle`] was removed in favor of required components.
/// The core component is now [`Text2d`] which can contain a single text segment. /// The core component is now [`Text2d`] which can contain a single text segment.
@ -235,12 +234,12 @@ pub fn extract_text2d_sprite(
/// It does not modify or observe existing ones. /// It does not modify or observe existing ones.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn update_text2d_layout( pub fn update_text2d_layout(
mut last_scale_factor: Local<f32>,
// Text items which should be reprocessed again, generally when the font hasn't loaded yet. // Text items which should be reprocessed again, generally when the font hasn't loaded yet.
mut queue: Local<HashSet<Entity>>, mut queue: Local<HashSet<Entity>>,
mut textures: ResMut<Assets<Image>>, mut textures: ResMut<Assets<Image>>,
fonts: Res<Assets<Font>>, fonts: Res<Assets<Font>>,
windows: Query<&Window, With<PrimaryWindow>>, windows: Query<&Window, With<PrimaryWindow>>,
mut scale_factor_changed: EventReader<WindowScaleFactorChanged>,
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>, mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
mut font_atlas_sets: ResMut<FontAtlasSets>, mut font_atlas_sets: ResMut<FontAtlasSets>,
mut text_pipeline: ResMut<TextPipeline>, mut text_pipeline: ResMut<TextPipeline>,
@ -255,9 +254,6 @@ pub fn update_text2d_layout(
mut font_system: ResMut<CosmicFontSystem>, mut font_system: ResMut<CosmicFontSystem>,
mut swash_cache: ResMut<SwashCache>, mut swash_cache: ResMut<SwashCache>,
) { ) {
// We need to consume the entire iterator, hence `last`
let factor_changed = scale_factor_changed.read().last().is_some();
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621 // TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows let scale_factor = windows
.get_single() .get_single()
@ -266,6 +262,9 @@ pub fn update_text2d_layout(
let inverse_scale_factor = scale_factor.recip(); let inverse_scale_factor = scale_factor.recip();
let factor_changed = *last_scale_factor != scale_factor;
*last_scale_factor = scale_factor;
for (entity, block, bounds, text_layout_info, mut computed) in &mut text_query { for (entity, block, bounds, text_layout_info, mut computed) in &mut text_query {
if factor_changed if factor_changed
|| computed.needs_rerender() || computed.needs_rerender()
@ -359,7 +358,7 @@ mod tests {
use bevy_app::{App, Update}; use bevy_app::{App, Update};
use bevy_asset::{load_internal_binary_asset, Handle}; use bevy_asset::{load_internal_binary_asset, Handle};
use bevy_ecs::{event::Events, schedule::IntoSystemConfigs}; use bevy_ecs::schedule::IntoSystemConfigs;
use crate::{detect_text_needs_rerender, TextIterScratch}; use crate::{detect_text_needs_rerender, TextIterScratch};
@ -374,7 +373,6 @@ mod tests {
.init_resource::<Assets<Image>>() .init_resource::<Assets<Image>>()
.init_resource::<Assets<TextureAtlasLayout>>() .init_resource::<Assets<TextureAtlasLayout>>()
.init_resource::<FontAtlasSets>() .init_resource::<FontAtlasSets>()
.init_resource::<Events<WindowScaleFactorChanged>>()
.init_resource::<TextPipeline>() .init_resource::<TextPipeline>()
.init_resource::<CosmicFontSystem>() .init_resource::<CosmicFontSystem>()
.init_resource::<SwashCache>() .init_resource::<SwashCache>()