Skip to content
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

Divide by UiScale when converting UI coordinates from physical to logical #8720

Merged
merged 14 commits into from
Jul 6, 2023

Conversation

ickshonpe
Copy link
Contributor

@ickshonpe ickshonpe commented May 30, 2023

Objective

After the UI layout is computed when the coordinates are converted back from physical coordinates to logical coordinates the UiScale is ignored. This results in a confusing situation where we have two different systems of logical coordinates.

Example:

use bevy::prelude::*;

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

fn setup(mut commands: Commands, mut ui_scale: ResMut<UiScale>) {
    ui_scale.scale = 4.;

    commands.spawn(Camera2dBundle::default());
    commands.spawn(NodeBundle {
        style: Style {
            align_items: AlignItems::Center,
            justify_content: JustifyContent::Center,
            width: Val::Percent(100.),
            ..Default::default()
        },
        ..Default::default()
    })
    .with_children(|builder| {
        builder.spawn(NodeBundle {
            style: Style {
                width: Val::Px(100.),
                height: Val::Px(100.),
                ..Default::default()
            },
            background_color: Color::MAROON.into(),
            ..Default::default()
        }).with_children(|builder| {
            builder.spawn(TextBundle::from_section("", TextStyle::default());
        });
    });
}

fn update(
    mut text_query: Query<(&mut Text, &Parent)>,
    node_query: Query<Ref<Node>>,
) {
    for (mut text, parent) in text_query.iter_mut() {
        let node = node_query.get(parent.get()).unwrap();
        if node.is_changed() {
            text.sections[0].value = format!("size: {}", node.size());
        }
    }
}

result:

Bevy App 30_05_2023 16_54_32

We asked for a 100x100 UI node but the Node's size is multiplied by the value of UiScale to give a logical size of 400x400.

Solution

Divide the output physical coordinates by UiScale in ui_layout_system and multiply the logical viewport size by UiScale when creating the projection matrix for the UI's ExtractedView in extract_default_ui_camera_view.


Changelog

  • The UI layout's physical coordinates are divided by both the window scale factor and UiScale when converting them back to logical coordinates. The logical size of Ui nodes now matches the values given to their size constraints.
  • Multiply the logical viewport size by UiScale before creating the projection matrix for the UI's ExtractedView in extract_default_ui_camera_view.
  • In ui_focus_system the cursor position returned from Window is divided by UiScale.
  • Added a scale factor parameter to Node::physical_size and Node::physical_rect.
  • The example viewport_debug now uses a UiScale of 2. to ensure that viewport coordinates are working correctly with a non-unit UiScale.

Migration Guide

Physical UI coordinates are now divided by both the UiScale and the window's scale factor to compute the logical sizes and positions of UI nodes.

This ensures that UI Node size and position values, held by the Node and GlobalTransform components, conform to the same logical coordinate system as the style constraints from which they are derived, irrespective of the current scale_factor and UiScale.

@ickshonpe ickshonpe changed the title Combined scale factor Divide by UiScale when calculating UI logical coordinates May 30, 2023
@ickshonpe ickshonpe changed the title Divide by UiScale when calculating UI logical coordinates Divide by UiScale when converting from physical to logical coordinates May 30, 2023
@ickshonpe ickshonpe changed the title Divide by UiScale when converting from physical to logical coordinates Divide by UiScale when converting UI coordinates from physical to logical May 30, 2023
@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-UI Graphical user interfaces, styles, layouts, and widgets labels Jun 2, 2023
@alice-i-cecile
Copy link
Member

@ickshonpe can you resolve conflicts? I think this is likely to get merged preferentially.

@ickshonpe
Copy link
Contributor Author

ickshonpe commented Jun 20, 2023

@ickshonpe can you resolve conflicts? I think this is likely to get merged preferentially.

Yep done. This seems like the right approach. Once users have set up their window and set UiScale they shouldn't need to ever be conscious of anything other than logical coords.

I can't see where there are any tangible benefits from the extra precision using f64s for the scaling, I think that's one of the first things we should deal with after 0.11 is done. I'll make an issue.

@ickshonpe
Copy link
Contributor Author

ickshonpe commented Jun 20, 2023

Checked all the examples and they all seem fine. ui_scaling, overflow, game_menu, relative_cursor_position and ui are the ones most likely to go wrong if there are any bugs.

Just going to fix up the viewport_debug example for this, as I want to make sure the Val viewport coordinates variants still work correctly with borders and this PR together.

Added an extract parameter for `UiScale`.
The window's logical size is now divided by `UiScale::scale` to calculate the logical size of the UI's viewport for the border thickness resolution calculations.

`viewport_debug`
Doubled the physical resolution of the window for this example and set `UiScale` to `2.` so the logical size of the UI's viewport remains the same.

Also simplified this example by instead of spawning and despawning the two different uinode trees on state changes, using the `Display` style property to disable the trees one at a time.
@ickshonpe
Copy link
Contributor Author

ickshonpe commented Jun 20, 2023

Updated viewport_debug so it uses a UiScale of 2.
Changed extract_ui_borders to divide the UI's viewport size by UiScale so the border thicknesses with viewport coords are resolved correctly.
In ui_focus_system the cursor position was scaled incorrectly as well.

Shouldn't be any other problems.

ickshonpe and others added 4 commits June 20, 2023 12:20
Divide cursor postion by UiScale, not multiply.
Renamed `logical_viewport_size` to  `ui_logical_viewport_size` to be extra explicit.

Added a comment explaining why we have to divide by `UiScale` here.
Copy link
Member

@cart cart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me / seems like the right fixes.

@cart cart enabled auto-merge July 6, 2023 20:16
@cart cart added this pull request to the merge queue Jul 6, 2023
Merged via the queue into bevyengine:main with commit 9655ace Jul 6, 2023
20 checks passed
@IceSentry IceSentry added the C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide label Jul 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-UI Graphical user interfaces, styles, layouts, and widgets C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide C-Bug An unexpected or incorrect behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants