Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui_render/src/debug_overlay.rs
9356 views
1
use super::ExtractedUiItem;
2
use super::ExtractedUiNode;
3
use super::ExtractedUiNodes;
4
use super::NodeType;
5
use super::UiCameraMap;
6
use crate::shader_flags;
7
use bevy_asset::AssetId;
8
use bevy_camera::visibility::InheritedVisibility;
9
use bevy_color::Hsla;
10
use bevy_color::LinearRgba;
11
use bevy_ecs::entity::Entity;
12
use bevy_ecs::prelude::Component;
13
use bevy_ecs::prelude::ReflectComponent;
14
use bevy_ecs::prelude::ReflectResource;
15
use bevy_ecs::resource::Resource;
16
use bevy_ecs::system::Commands;
17
use bevy_ecs::system::Query;
18
use bevy_ecs::system::Res;
19
use bevy_ecs::system::ResMut;
20
use bevy_math::Affine2;
21
use bevy_math::Rect;
22
use bevy_math::Vec2;
23
use bevy_reflect::Reflect;
24
use bevy_render::sync_world::TemporaryRenderEntity;
25
use bevy_render::Extract;
26
use bevy_sprite::BorderRect;
27
use bevy_ui::ui_transform::UiGlobalTransform;
28
use bevy_ui::CalculatedClip;
29
use bevy_ui::ComputedNode;
30
use bevy_ui::ComputedUiTargetCamera;
31
use bevy_ui::ResolvedBorderRadius;
32
use bevy_ui::UiStack;
33
34
/// Configuration for the UI debug overlay
35
///
36
/// Can be added as both a global `Resource` and locally as a `Component` to individual UI node entities.
37
/// The local component options override the global resource.
38
#[derive(Component, Resource, Reflect)]
39
#[reflect(Component, Resource)]
40
pub struct UiDebugOptions {
41
/// Set to true to enable the UI debug overlay
42
pub enabled: bool,
43
/// Show outlines for the border boxes of UI nodes
44
pub outline_border_box: bool,
45
/// Show outlines for the padding boxes of UI nodes
46
pub outline_padding_box: bool,
47
/// Show outlines for the content boxes of UI nodes
48
pub outline_content_box: bool,
49
/// Show outlines for the scrollbar regions of UI nodes
50
pub outline_scrollbars: bool,
51
/// Width of the overlay's lines in logical pixels
52
pub line_width: f32,
53
/// Override Color for the overlay's lines
54
pub line_color_override: Option<LinearRgba>,
55
/// Show outlines for non-visible UI nodes
56
pub show_hidden: bool,
57
/// Show outlines for clipped sections of UI nodes
58
pub show_clipped: bool,
59
/// Draw outlines with sharp corners even if the UI nodes have border radii
60
pub ignore_border_radius: bool,
61
}
62
63
impl UiDebugOptions {
64
pub fn toggle(&mut self) {
65
self.enabled = !self.enabled;
66
}
67
}
68
69
impl Default for UiDebugOptions {
70
fn default() -> Self {
71
Self {
72
enabled: false,
73
line_width: 1.,
74
line_color_override: None,
75
show_hidden: false,
76
show_clipped: false,
77
ignore_border_radius: false,
78
outline_border_box: true,
79
outline_padding_box: false,
80
outline_content_box: false,
81
outline_scrollbars: false,
82
}
83
}
84
}
85
86
pub fn extract_debug_overlay(
87
mut commands: Commands,
88
debug_options: Extract<Res<UiDebugOptions>>,
89
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
90
uinode_query: Extract<
91
Query<(
92
Entity,
93
&ComputedNode,
94
&UiGlobalTransform,
95
&InheritedVisibility,
96
Option<&CalculatedClip>,
97
&ComputedUiTargetCamera,
98
Option<&UiDebugOptions>,
99
)>,
100
>,
101
ui_stack: Extract<Res<UiStack>>,
102
camera_map: Extract<UiCameraMap>,
103
) {
104
let mut camera_mapper = camera_map.get_mapper();
105
106
for (entity, uinode, transform, visibility, maybe_clip, computed_target, debug) in &uinode_query
107
{
108
let debug_options = debug.unwrap_or(&debug_options);
109
if !debug_options.enabled {
110
continue;
111
}
112
if !debug_options.show_hidden && !visibility.get() {
113
continue;
114
}
115
116
let Some(extracted_camera_entity) = camera_mapper.map(computed_target) else {
117
continue;
118
};
119
120
let color = debug_options
121
.line_color_override
122
.unwrap_or_else(|| Hsla::sequential_dispersed(entity.index_u32()).into());
123
let z_order = (ui_stack.uinodes.len() as u32 + uinode.stack_index()) as f32;
124
let border = BorderRect::all(debug_options.line_width / uinode.inverse_scale_factor());
125
let transform = transform.affine();
126
127
let mut push_outline = |rect: Rect, radius: ResolvedBorderRadius| {
128
if rect.is_empty() {
129
return;
130
}
131
132
extracted_uinodes.uinodes.push(ExtractedUiNode {
133
render_entity: commands.spawn(TemporaryRenderEntity).id(),
134
// Keep all overlays above UI, and nudge each type slightly in Z so ordering is stable.
135
z_order,
136
clip: maybe_clip
137
.filter(|_| !debug_options.show_clipped)
138
.map(|clip| clip.clip),
139
image: AssetId::default(),
140
extracted_camera_entity,
141
transform: transform * Affine2::from_translation(rect.center()),
142
item: ExtractedUiItem::Node {
143
color,
144
rect: Rect {
145
min: Vec2::ZERO,
146
max: rect.size(),
147
},
148
atlas_scaling: None,
149
flip_x: false,
150
flip_y: false,
151
border,
152
border_radius: radius,
153
node_type: NodeType::Border(shader_flags::BORDER_ALL),
154
},
155
main_entity: entity.into(),
156
});
157
};
158
159
let border_box = Rect::from_center_size(Vec2::ZERO, uinode.size);
160
161
if debug_options.outline_border_box {
162
push_outline(border_box, uinode.border_radius());
163
}
164
165
if debug_options.outline_padding_box {
166
let mut padding_box = border_box;
167
padding_box.min += uinode.border.min_inset;
168
padding_box.max -= uinode.border.max_inset;
169
push_outline(padding_box, uinode.inner_radius());
170
}
171
172
if debug_options.outline_content_box {
173
let mut content_box = border_box;
174
let content_inset = uinode.content_inset();
175
content_box.min += content_inset.min_inset;
176
content_box.max -= content_inset.max_inset;
177
push_outline(content_box, ResolvedBorderRadius::ZERO);
178
}
179
180
if debug_options.outline_scrollbars {
181
if let Some((gutter, [thumb_min, thumb_max])) = uinode.horizontal_scrollbar() {
182
push_outline(gutter, ResolvedBorderRadius::ZERO);
183
push_outline(
184
Rect {
185
min: Vec2::new(thumb_min, gutter.min.y),
186
max: Vec2::new(thumb_max, gutter.max.y),
187
},
188
ResolvedBorderRadius::ZERO,
189
);
190
}
191
if let Some((gutter, [thumb_min, thumb_max])) = uinode.vertical_scrollbar() {
192
push_outline(gutter, ResolvedBorderRadius::ZERO);
193
push_outline(
194
Rect {
195
min: Vec2::new(gutter.min.x, thumb_min),
196
max: Vec2::new(gutter.max.x, thumb_max),
197
},
198
ResolvedBorderRadius::ZERO,
199
);
200
}
201
}
202
}
203
}
204
205