Skip to content

Commit 16ff354

Browse files
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)
1 parent e9be54b commit 16ff354

File tree

1 file changed

+75
-0
lines changed
  • crates/bevy_ui/src/layout

1 file changed

+75
-0
lines changed

crates/bevy_ui/src/layout/mod.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ pub struct LayoutContext {
3232
}
3333

3434
impl LayoutContext {
35+
pub const DEFAULT: Self = Self {
36+
scale_factor: 1.0,
37+
physical_size: Vec2::ZERO,
38+
min_size: 0.0,
39+
max_size: 0.0,
40+
};
3541
/// create new a [`LayoutContext`] from the window's physical size and scale factor
3642
fn new(scale_factor: f32, physical_size: Vec2) -> Self {
3743
Self {
@@ -43,6 +49,12 @@ impl LayoutContext {
4349
}
4450
}
4551

52+
impl Default for LayoutContext {
53+
fn default() -> Self {
54+
Self::DEFAULT
55+
}
56+
}
57+
4658
#[derive(Debug, Error)]
4759
pub enum LayoutError {
4860
#[error("Invalid hierarchy")]
@@ -156,6 +168,8 @@ pub fn ui_layout_system(
156168
);
157169
ui_surface.upsert_node(entity, &style, &layout_context);
158170
}
171+
} else {
172+
ui_surface.upsert_node(entity, &Style::default(), &LayoutContext::default());
159173
}
160174
}
161175
scale_factor_events.clear();
@@ -1000,4 +1014,65 @@ mod tests {
10001014
}
10011015
}
10021016
}
1017+
1018+
#[test]
1019+
fn no_camera_ui() {
1020+
let mut world = World::new();
1021+
world.init_resource::<UiScale>();
1022+
world.init_resource::<UiSurface>();
1023+
world.init_resource::<Events<WindowScaleFactorChanged>>();
1024+
world.init_resource::<Events<WindowResized>>();
1025+
// Required for the camera system
1026+
world.init_resource::<Events<WindowCreated>>();
1027+
world.init_resource::<Events<AssetEvent<Image>>>();
1028+
world.init_resource::<Assets<Image>>();
1029+
world.init_resource::<ManualTextureViews>();
1030+
1031+
// spawn a dummy primary window and camera
1032+
world.spawn((
1033+
Window {
1034+
resolution: WindowResolution::new(WINDOW_WIDTH, WINDOW_HEIGHT),
1035+
..default()
1036+
},
1037+
PrimaryWindow,
1038+
));
1039+
1040+
let mut ui_schedule = Schedule::default();
1041+
ui_schedule.add_systems(
1042+
(
1043+
// UI is driven by calculated camera target info, so we need to run the camera system first
1044+
bevy_render::camera::camera_system::<OrthographicProjection>,
1045+
update_target_camera_system,
1046+
apply_deferred,
1047+
ui_layout_system,
1048+
)
1049+
.chain(),
1050+
);
1051+
1052+
let ui_root = world
1053+
.spawn(NodeBundle {
1054+
style: Style {
1055+
width: Val::Percent(100.),
1056+
height: Val::Percent(100.),
1057+
..default()
1058+
},
1059+
..default()
1060+
})
1061+
.id();
1062+
1063+
let ui_child = world
1064+
.spawn(NodeBundle {
1065+
style: Style {
1066+
width: Val::Percent(100.),
1067+
height: Val::Percent(100.),
1068+
..default()
1069+
},
1070+
..default()
1071+
})
1072+
.id();
1073+
1074+
world.entity_mut(ui_root).add_child(ui_child);
1075+
1076+
ui_schedule.run(&mut world);
1077+
}
10031078
}

0 commit comments

Comments
 (0)