Correctly handle UI hierarchy without a camera (#12816)

# Objective
Add support so bevy_ui can correctly handle an UI hierarchy without a
camera present.

- Fixes #12184

## Solution

As there was no default behavior for what should happen when a camera is
not present in a UI hierarchy, the solution
was based in defining that default behavior and improving the overall
handling of this "exception".


## Changelog

- Create default values to be used in upsert_node
- Add flag to control warnings about no camera present
- Create unit test no_camera_ui (to test if ui handles no camera
present)
This commit is contained in:
Salvador Carvalhinho 2024-04-22 17:42:13 +01:00 committed by GitHub
parent e9be54b0ea
commit 16ff354f1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -32,6 +32,12 @@ pub struct LayoutContext {
}
impl LayoutContext {
pub const DEFAULT: Self = Self {
scale_factor: 1.0,
physical_size: Vec2::ZERO,
min_size: 0.0,
max_size: 0.0,
};
/// create new a [`LayoutContext`] from the window's physical size and scale factor
fn new(scale_factor: f32, physical_size: Vec2) -> Self {
Self {
@ -43,6 +49,12 @@ impl LayoutContext {
}
}
impl Default for LayoutContext {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Debug, Error)]
pub enum LayoutError {
#[error("Invalid hierarchy")]
@ -156,6 +168,8 @@ pub fn ui_layout_system(
);
ui_surface.upsert_node(entity, &style, &layout_context);
}
} else {
ui_surface.upsert_node(entity, &Style::default(), &LayoutContext::default());
}
}
scale_factor_events.clear();
@ -1000,4 +1014,65 @@ mod tests {
}
}
}
#[test]
fn no_camera_ui() {
let mut world = World::new();
world.init_resource::<UiScale>();
world.init_resource::<UiSurface>();
world.init_resource::<Events<WindowScaleFactorChanged>>();
world.init_resource::<Events<WindowResized>>();
// Required for the camera system
world.init_resource::<Events<WindowCreated>>();
world.init_resource::<Events<AssetEvent<Image>>>();
world.init_resource::<Assets<Image>>();
world.init_resource::<ManualTextureViews>();
// spawn a dummy primary window and camera
world.spawn((
Window {
resolution: WindowResolution::new(WINDOW_WIDTH, WINDOW_HEIGHT),
..default()
},
PrimaryWindow,
));
let mut ui_schedule = Schedule::default();
ui_schedule.add_systems(
(
// UI is driven by calculated camera target info, so we need to run the camera system first
bevy_render::camera::camera_system::<OrthographicProjection>,
update_target_camera_system,
apply_deferred,
ui_layout_system,
)
.chain(),
);
let ui_root = world
.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.),
height: Val::Percent(100.),
..default()
},
..default()
})
.id();
let ui_child = world
.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.),
height: Val::Percent(100.),
..default()
},
..default()
})
.id();
world.entity_mut(ui_root).add_child(ui_child);
ui_schedule.run(&mut world);
}
}