
# Objective During the migration to required components a lot of things were changed around and somehow the draw order for some UI elements ended up depending on the system ordering in `RenderSystems::Queue`, which can sometimes result in the elements being drawn in the wrong order. Fixes #19674 ## Solution * Added some more `stack_z_offsets` constants and used them to enforce an explicit ordering. * Removed the `stack_index: u32` field from `ExtractedUiNodes` and replaced it with a `z_order: f32` field. These changes should fix all the ordering problems. ## Testing I added a nine-patched bordered node with a navy background color to the slice section of the `testbed_ui` example. The border should always be drawn above the background color.
117 lines
3.5 KiB
Rust
117 lines
3.5 KiB
Rust
use super::ExtractedUiItem;
|
|
use super::ExtractedUiNode;
|
|
use super::ExtractedUiNodes;
|
|
use super::NodeType;
|
|
use super::UiCameraMap;
|
|
use crate::shader_flags;
|
|
use crate::ui_node::ComputedNodeTarget;
|
|
use crate::ui_transform::UiGlobalTransform;
|
|
use crate::CalculatedClip;
|
|
use crate::ComputedNode;
|
|
use crate::UiStack;
|
|
use bevy_asset::AssetId;
|
|
use bevy_color::Hsla;
|
|
use bevy_ecs::entity::Entity;
|
|
use bevy_ecs::resource::Resource;
|
|
use bevy_ecs::system::Commands;
|
|
use bevy_ecs::system::Query;
|
|
use bevy_ecs::system::Res;
|
|
use bevy_ecs::system::ResMut;
|
|
use bevy_math::Rect;
|
|
use bevy_math::Vec2;
|
|
use bevy_render::sync_world::TemporaryRenderEntity;
|
|
use bevy_render::view::InheritedVisibility;
|
|
use bevy_render::Extract;
|
|
use bevy_sprite::BorderRect;
|
|
|
|
/// Configuration for the UI debug overlay
|
|
#[derive(Resource)]
|
|
pub struct UiDebugOptions {
|
|
/// Set to true to enable the UI debug overlay
|
|
pub enabled: bool,
|
|
/// Width of the overlay's lines in logical pixels
|
|
pub line_width: f32,
|
|
/// Show outlines for non-visible UI nodes
|
|
pub show_hidden: bool,
|
|
/// Show outlines for clipped sections of UI nodes
|
|
pub show_clipped: bool,
|
|
}
|
|
|
|
impl UiDebugOptions {
|
|
pub fn toggle(&mut self) {
|
|
self.enabled = !self.enabled;
|
|
}
|
|
}
|
|
|
|
impl Default for UiDebugOptions {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: false,
|
|
line_width: 1.,
|
|
show_hidden: false,
|
|
show_clipped: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn extract_debug_overlay(
|
|
mut commands: Commands,
|
|
debug_options: Extract<Res<UiDebugOptions>>,
|
|
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
|
uinode_query: Extract<
|
|
Query<(
|
|
Entity,
|
|
&ComputedNode,
|
|
&UiGlobalTransform,
|
|
&InheritedVisibility,
|
|
Option<&CalculatedClip>,
|
|
&ComputedNodeTarget,
|
|
)>,
|
|
>,
|
|
ui_stack: Extract<Res<UiStack>>,
|
|
camera_map: Extract<UiCameraMap>,
|
|
) {
|
|
if !debug_options.enabled {
|
|
return;
|
|
}
|
|
|
|
let mut camera_mapper = camera_map.get_mapper();
|
|
|
|
for (entity, uinode, transform, visibility, maybe_clip, computed_target) in &uinode_query {
|
|
if !debug_options.show_hidden && !visibility.get() {
|
|
continue;
|
|
}
|
|
|
|
let Some(extracted_camera_entity) = camera_mapper.map(computed_target) else {
|
|
continue;
|
|
};
|
|
|
|
// Extract a border box to display an outline for every UI Node in the layout
|
|
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
|
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
|
// Add a large number to the UI node's stack index so that the overlay is always drawn on top
|
|
z_order: (ui_stack.uinodes.len() as u32 + uinode.stack_index()) as f32,
|
|
color: Hsla::sequential_dispersed(entity.index()).into(),
|
|
rect: Rect {
|
|
min: Vec2::ZERO,
|
|
max: uinode.size,
|
|
},
|
|
clip: maybe_clip
|
|
.filter(|_| !debug_options.show_clipped)
|
|
.map(|clip| clip.clip),
|
|
image: AssetId::default(),
|
|
extracted_camera_entity,
|
|
item: ExtractedUiItem::Node {
|
|
atlas_scaling: None,
|
|
transform: transform.into(),
|
|
flip_x: false,
|
|
flip_y: false,
|
|
border: BorderRect::all(debug_options.line_width / uinode.inverse_scale_factor()),
|
|
border_radius: uinode.border_radius(),
|
|
node_type: NodeType::Border(shader_flags::BORDER_ALL),
|
|
},
|
|
main_entity: entity.into(),
|
|
});
|
|
}
|
|
}
|