bevy_ui_render/
debug_overlay.rs1use super::ExtractedUiItem;
2use super::ExtractedUiNode;
3use super::ExtractedUiNodes;
4use super::NodeType;
5use super::UiCameraMap;
6use crate::shader_flags;
7use bevy_asset::AssetId;
8use bevy_camera::visibility::InheritedVisibility;
9use bevy_color::Hsla;
10use bevy_color::LinearRgba;
11use bevy_ecs::entity::Entity;
12use bevy_ecs::prelude::ReflectResource;
13use bevy_ecs::resource::Resource;
14use bevy_ecs::system::Commands;
15use bevy_ecs::system::Query;
16use bevy_ecs::system::Res;
17use bevy_ecs::system::ResMut;
18use bevy_math::Rect;
19use bevy_math::Vec2;
20use bevy_reflect::Reflect;
21use bevy_render::sync_world::TemporaryRenderEntity;
22use bevy_render::Extract;
23use bevy_sprite::BorderRect;
24use bevy_ui::ui_transform::UiGlobalTransform;
25use bevy_ui::CalculatedClip;
26use bevy_ui::ComputedNode;
27use bevy_ui::ComputedUiTargetCamera;
28use bevy_ui::UiStack;
29
30#[derive(Resource, Reflect)]
32#[reflect(Resource)]
33pub struct UiDebugOptions {
34 pub enabled: bool,
36 pub line_width: f32,
38 pub line_color_override: Option<LinearRgba>,
40 pub show_hidden: bool,
42 pub show_clipped: bool,
44}
45
46impl UiDebugOptions {
47 pub fn toggle(&mut self) {
48 self.enabled = !self.enabled;
49 }
50}
51
52impl Default for UiDebugOptions {
53 fn default() -> Self {
54 Self {
55 enabled: false,
56 line_width: 1.,
57 line_color_override: None,
58 show_hidden: false,
59 show_clipped: false,
60 }
61 }
62}
63
64pub fn extract_debug_overlay(
65 mut commands: Commands,
66 debug_options: Extract<Res<UiDebugOptions>>,
67 mut extracted_uinodes: ResMut<ExtractedUiNodes>,
68 uinode_query: Extract<
69 Query<(
70 Entity,
71 &ComputedNode,
72 &UiGlobalTransform,
73 &InheritedVisibility,
74 Option<&CalculatedClip>,
75 &ComputedUiTargetCamera,
76 )>,
77 >,
78 ui_stack: Extract<Res<UiStack>>,
79 camera_map: Extract<UiCameraMap>,
80) {
81 if !debug_options.enabled {
82 return;
83 }
84
85 let mut camera_mapper = camera_map.get_mapper();
86
87 for (entity, uinode, transform, visibility, maybe_clip, computed_target) in &uinode_query {
88 if !debug_options.show_hidden && !visibility.get() {
89 continue;
90 }
91
92 let Some(extracted_camera_entity) = camera_mapper.map(computed_target) else {
93 continue;
94 };
95
96 extracted_uinodes.uinodes.push(ExtractedUiNode {
98 render_entity: commands.spawn(TemporaryRenderEntity).id(),
99 z_order: (ui_stack.uinodes.len() as u32 + uinode.stack_index()) as f32,
101 clip: maybe_clip
102 .filter(|_| !debug_options.show_clipped)
103 .map(|clip| clip.clip),
104 image: AssetId::default(),
105 extracted_camera_entity,
106 transform: transform.into(),
107 item: ExtractedUiItem::Node {
108 color: debug_options
109 .line_color_override
110 .unwrap_or_else(|| Hsla::sequential_dispersed(entity.index_u32()).into()),
111 rect: Rect {
112 min: Vec2::ZERO,
113 max: uinode.size,
114 },
115 atlas_scaling: None,
116 flip_x: false,
117 flip_y: false,
118 border: BorderRect::all(debug_options.line_width / uinode.inverse_scale_factor()),
119 border_radius: uinode.border_radius(),
120 node_type: NodeType::Border(shader_flags::BORDER_ALL),
121 },
122 main_entity: entity.into(),
123 });
124 }
125}