diff --git a/Cargo.toml b/Cargo.toml index 6a20f08160841..fd61ba79853ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,6 +156,9 @@ bevy_ui_picking_backend = [ "bevy_internal/bevy_ui_picking_backend", ] +# Provides a debug overlay for bevy UI +bevy_ui_debug = ["bevy_internal/bevy_ui_debug"] + # Force dynamic linking, which improves iterative compile times dynamic_linking = ["dep:bevy_dylib", "bevy_internal/dynamic_linking"] diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 322c924f81f34..1d426853cee80 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -9,29 +9,19 @@ license = "MIT OR Apache-2.0" keywords = ["bevy"] [features] -default = ["bevy_ui_debug"] bevy_ci_testing = ["serde", "ron"] -bevy_ui_debug = [] [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" } bevy_color = { path = "../bevy_color", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } -bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } -bevy_gizmos = { path = "../bevy_gizmos", version = "0.15.0-dev", features = [ - "bevy_render", -] } bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" } bevy_input = { path = "../bevy_input", version = "0.15.0-dev" } -bevy_math = { path = "../bevy_math", version = "0.15.0-dev" } -bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" } bevy_render = { path = "../bevy_render", version = "0.15.0-dev" } bevy_time = { path = "../bevy_time", version = "0.15.0-dev" } -bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" } bevy_text = { path = "../bevy_text", version = "0.15.0-dev" } bevy_ui = { path = "../bevy_ui", version = "0.15.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" } diff --git a/crates/bevy_dev_tools/src/lib.rs b/crates/bevy_dev_tools/src/lib.rs index d5563a0bd946a..b49604e6c885d 100644 --- a/crates/bevy_dev_tools/src/lib.rs +++ b/crates/bevy_dev_tools/src/lib.rs @@ -15,9 +15,6 @@ pub mod ci_testing; pub mod fps_overlay; -#[cfg(feature = "bevy_ui_debug")] -pub mod ui_debug_overlay; - pub mod states; /// Enables developer tools in an [`App`]. This plugin is added automatically with `bevy_dev_tools` diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs deleted file mode 100644 index 7c076a2c8aee7..0000000000000 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs +++ /dev/null @@ -1,192 +0,0 @@ -use bevy_color::Color; -use bevy_gizmos::{config::GizmoConfigGroup, prelude::Gizmos}; -use bevy_math::{Vec2, Vec2Swizzles}; -use bevy_reflect::Reflect; -use bevy_transform::prelude::GlobalTransform; -use bevy_utils::HashMap; - -use super::{CameraQuery, LayoutRect}; - -// Function used here so we don't need to redraw lines that are fairly close to each other. -fn approx_eq(compared: f32, other: f32) -> bool { - (compared - other).abs() < 0.001 -} - -fn rect_border_axis(rect: LayoutRect) -> (f32, f32, f32, f32) { - let pos = rect.pos; - let size = rect.size; - let offset = pos + size; - (pos.x, offset.x, pos.y, offset.y) -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] -enum Dir { - Start, - End, -} -impl Dir { - const fn increments(self) -> i64 { - match self { - Dir::Start => 1, - Dir::End => -1, - } - } -} -impl From for Dir { - fn from(value: i64) -> Self { - if value.is_positive() { - Dir::Start - } else { - Dir::End - } - } -} -/// Collection of axis aligned "lines" (actually just their coordinate on -/// a given axis). -#[derive(Debug, Clone)] -struct DrawnLines { - lines: HashMap, - width: f32, -} -#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] -impl DrawnLines { - fn new(width: f32) -> Self { - DrawnLines { - lines: HashMap::default(), - width, - } - } - /// Return `value` offset by as many `increment`s as necessary to make it - /// not overlap with already drawn lines. - fn inset(&self, value: f32) -> f32 { - let scaled = value / self.width; - let fract = scaled.fract(); - let mut on_grid = scaled.floor() as i64; - for _ in 0..10 { - let Some(dir) = self.lines.get(&on_grid) else { - break; - }; - // TODO(clean): This fixes a panic, but I'm not sure how valid this is - let Some(added) = on_grid.checked_add(dir.increments()) else { - break; - }; - on_grid = added; - } - ((on_grid as f32) + fract) * self.width - } - /// Remove a line from the collection of drawn lines. - /// - /// Typically, we only care for pre-existing lines when drawing the children - /// of a container, nothing more. So we remove it after we are done with - /// the children. - fn remove(&mut self, value: f32, increment: i64) { - let mut on_grid = (value / self.width).floor() as i64; - loop { - // TODO(clean): This fixes a panic, but I'm not sure how valid this is - let Some(next_cell) = on_grid.checked_add(increment) else { - return; - }; - if !self.lines.contains_key(&next_cell) { - self.lines.remove(&on_grid); - return; - } - on_grid = next_cell; - } - } - /// Add a line from the collection of drawn lines. - fn add(&mut self, value: f32, increment: i64) { - let mut on_grid = (value / self.width).floor() as i64; - loop { - let old_value = self.lines.insert(on_grid, increment.into()); - if old_value.is_none() { - return; - } - // TODO(clean): This fixes a panic, but I'm not sure how valid this is - let Some(added) = on_grid.checked_add(increment) else { - return; - }; - on_grid = added; - } - } -} - -#[derive(GizmoConfigGroup, Reflect, Default)] -pub struct UiGizmosDebug; - -pub(super) struct InsetGizmo<'w, 's> { - draw: Gizmos<'w, 's, UiGizmosDebug>, - cam: CameraQuery<'w, 's>, - known_y: DrawnLines, - known_x: DrawnLines, -} -impl<'w, 's> InsetGizmo<'w, 's> { - pub(super) fn new( - draw: Gizmos<'w, 's, UiGizmosDebug>, - cam: CameraQuery<'w, 's>, - line_width: f32, - ) -> Self { - InsetGizmo { - draw, - cam, - known_y: DrawnLines::new(line_width), - known_x: DrawnLines::new(line_width), - } - } - fn relative(&self, mut position: Vec2) -> Vec2 { - let zero = GlobalTransform::IDENTITY; - let Ok(cam) = self.cam.get_single() else { - return Vec2::ZERO; - }; - if let Ok(new_position) = cam.world_to_viewport(&zero, position.extend(0.)) { - position = new_position; - }; - position.xy() - } - fn line_2d(&mut self, mut start: Vec2, mut end: Vec2, color: Color) { - if approx_eq(start.x, end.x) { - start.x = self.known_x.inset(start.x); - end.x = start.x; - } else if approx_eq(start.y, end.y) { - start.y = self.known_y.inset(start.y); - end.y = start.y; - } - let (start, end) = (self.relative(start), self.relative(end)); - self.draw.line_2d(start, end, color); - } - pub(super) fn set_scope(&mut self, rect: LayoutRect) { - let (left, right, top, bottom) = rect_border_axis(rect); - self.known_x.add(left, 1); - self.known_x.add(right, -1); - self.known_y.add(top, 1); - self.known_y.add(bottom, -1); - } - pub(super) fn clear_scope(&mut self, rect: LayoutRect) { - let (left, right, top, bottom) = rect_border_axis(rect); - self.known_x.remove(left, 1); - self.known_x.remove(right, -1); - self.known_y.remove(top, 1); - self.known_y.remove(bottom, -1); - } - pub(super) fn rect_2d(&mut self, rect: LayoutRect, color: Color) { - let (left, right, top, bottom) = rect_border_axis(rect); - if approx_eq(left, right) { - self.line_2d(Vec2::new(left, top), Vec2::new(left, bottom), color); - } else if approx_eq(top, bottom) { - self.line_2d(Vec2::new(left, top), Vec2::new(right, top), color); - } else { - let inset_x = |v| self.known_x.inset(v); - let inset_y = |v| self.known_y.inset(v); - let (left, right) = (inset_x(left), inset_x(right)); - let (top, bottom) = (inset_y(top), inset_y(bottom)); - let strip = [ - Vec2::new(left, top), - Vec2::new(left, bottom), - Vec2::new(right, bottom), - Vec2::new(right, top), - Vec2::new(left, top), - ] - .map(|v| self.relative(v)); - self.draw.linestrip_2d(strip, color); - } - } -} diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs deleted file mode 100644 index 97726ce28972d..0000000000000 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs +++ /dev/null @@ -1,278 +0,0 @@ -//! A visual representation of UI node sizes. -use core::any::{Any, TypeId}; - -use bevy_app::{App, Plugin, PostUpdate}; -use bevy_color::Hsla; -use bevy_core::Name; -use bevy_core_pipeline::core_2d::Camera2d; -use bevy_ecs::{prelude::*, system::SystemParam}; -use bevy_gizmos::{config::GizmoConfigStore, prelude::Gizmos, AppGizmoBuilder}; -use bevy_hierarchy::{Children, Parent}; -use bevy_math::{Vec2, Vec3Swizzles}; -use bevy_render::{ - camera::RenderTarget, - prelude::*, - view::{RenderLayers, VisibilitySystems}, -}; -use bevy_transform::{prelude::GlobalTransform, TransformSystem}; -use bevy_ui::{ComputedNode, DefaultUiCamera, Display, Node, TargetCamera, UiScale}; -use bevy_utils::{default, warn_once}; -use bevy_window::{PrimaryWindow, Window, WindowRef}; - -use inset::InsetGizmo; - -use self::inset::UiGizmosDebug; - -mod inset; - -/// The [`Camera::order`] index used by the layout debug camera. -pub const LAYOUT_DEBUG_CAMERA_ORDER: isize = 255; -/// The [`RenderLayers`] used by the debug gizmos and the debug camera. -pub const LAYOUT_DEBUG_LAYERS: RenderLayers = RenderLayers::layer(16); - -#[derive(Clone, Copy)] -struct LayoutRect { - pos: Vec2, - size: Vec2, -} - -impl LayoutRect { - fn new(trans: &GlobalTransform, node: &ComputedNode, scale: f32) -> Self { - let mut this = Self { - pos: trans.translation().xy() * scale, - size: node.size() * scale, - }; - this.pos -= this.size / 2.; - this - } -} - -#[derive(Component, Debug, Clone, Default)] -struct DebugOverlayCamera; - -/// The debug overlay options. -#[derive(Resource, Clone, Default)] -pub struct UiDebugOptions { - /// Whether the overlay is enabled. - pub enabled: bool, - layout_gizmos_camera: Option, -} -impl UiDebugOptions { - /// This will toggle the enabled field, setting it to false if true and true if false. - pub fn toggle(&mut self) { - self.enabled = !self.enabled; - } -} - -/// The system responsible to change the [`Camera`] config based on changes in [`UiDebugOptions`] and [`GizmoConfig`](bevy_gizmos::prelude::GizmoConfig). -fn update_debug_camera( - mut gizmo_config: ResMut, - mut options: ResMut, - mut cmds: Commands, - mut debug_cams: Query<&mut Camera, With>, -) { - if !options.is_changed() && !gizmo_config.is_changed() { - return; - } - if !options.enabled { - let Some(cam) = options.layout_gizmos_camera else { - return; - }; - let Ok(mut cam) = debug_cams.get_mut(cam) else { - return; - }; - cam.is_active = false; - if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { - config.enabled = false; - } - } else { - let spawn_cam = || { - cmds.spawn(( - Camera2d, - OrthographicProjection { - far: 1000.0, - viewport_origin: Vec2::new(0.0, 0.0), - ..OrthographicProjection::default_3d() - }, - Camera { - order: LAYOUT_DEBUG_CAMERA_ORDER, - clear_color: ClearColorConfig::None, - ..default() - }, - LAYOUT_DEBUG_LAYERS.clone(), - DebugOverlayCamera, - Name::new("Layout Debug Camera"), - )) - .id() - }; - if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { - config.enabled = true; - config.render_layers = LAYOUT_DEBUG_LAYERS.clone(); - } - let cam = *options.layout_gizmos_camera.get_or_insert_with(spawn_cam); - let Ok(mut cam) = debug_cams.get_mut(cam) else { - return; - }; - cam.is_active = true; - } -} - -/// The function that goes over every children of given [`Entity`], skipping the not visible ones and drawing the gizmos outlines. -fn outline_nodes(outline: &OutlineParam, draw: &mut InsetGizmo, this_entity: Entity, scale: f32) { - let Ok(to_iter) = outline.children.get(this_entity) else { - return; - }; - - for (entity, trans, node, computed_node, children) in outline.nodes.iter_many(to_iter) { - if matches!(node.display, Display::None) { - continue; - } - - if let Ok(view_visibility) = outline.view_visibility.get(entity) { - if !view_visibility.get() { - continue; - } - } - let rect = LayoutRect::new(trans, computed_node, scale); - outline_node(entity, rect, draw); - if children.is_some() { - outline_nodes(outline, draw, entity, scale); - } - draw.clear_scope(rect); - } -} - -type NodesQuery = ( - Entity, - &'static GlobalTransform, - &'static Node, - &'static ComputedNode, - Option<&'static Children>, -); - -#[derive(SystemParam)] -struct OutlineParam<'w, 's> { - gizmo_config: Res<'w, GizmoConfigStore>, - children: Query<'w, 's, &'static Children>, - nodes: Query<'w, 's, NodesQuery>, - view_visibility: Query<'w, 's, &'static ViewVisibility>, - ui_scale: Res<'w, UiScale>, -} - -type CameraQuery<'w, 's> = Query<'w, 's, &'static Camera, With>; - -#[derive(SystemParam)] -struct CameraParam<'w, 's> { - debug_camera: Query<'w, 's, &'static Camera, With>, - cameras: Query<'w, 's, &'static Camera, Without>, - primary_window: Query<'w, 's, &'static Window, With>, - default_ui_camera: DefaultUiCamera<'w, 's>, -} - -/// system responsible for drawing the gizmos lines around all the node roots, iterating recursively through all visible children. -fn outline_roots( - outline: OutlineParam, - draw: Gizmos, - cam: CameraParam, - roots: Query< - ( - Entity, - &GlobalTransform, - &ComputedNode, - Option<&ViewVisibility>, - Option<&TargetCamera>, - ), - Without, - >, - window: Query<&Window, With>, - nonprimary_windows: Query<&Window, Without>, - options: Res, -) { - if !options.enabled { - return; - } - if !nonprimary_windows.is_empty() { - warn_once!( - "The layout debug view only uses the primary window scale, \ - you might notice gaps between container lines" - ); - } - let window_scale = window.get_single().map_or(1., Window::scale_factor); - let scale_factor = outline.ui_scale.0; - - // We let the line be defined by the window scale alone - let line_width = outline - .gizmo_config - .get_config_dyn(&UiGizmosDebug.type_id()) - .map_or(2., |(config, _)| config.line.width) - / window_scale; - let mut draw = InsetGizmo::new(draw, cam.debug_camera, line_width); - for (entity, trans, node, view_visibility, maybe_target_camera) in &roots { - if let Some(view_visibility) = view_visibility { - // If the entity isn't visible, we will not draw any lines. - if !view_visibility.get() { - continue; - } - } - // We skip ui in other windows that are not the primary one - if let Some(camera_entity) = maybe_target_camera - .map(|target| target.0) - .or(cam.default_ui_camera.get()) - { - let Ok(camera) = cam.cameras.get(camera_entity) else { - // The camera wasn't found. Either the Camera don't exist or the Camera is the debug Camera, that we want to skip and warn - warn_once!("Camera {:?} wasn't found for debug overlay", camera_entity); - continue; - }; - match camera.target { - RenderTarget::Window(window_ref) => { - if let WindowRef::Entity(window_entity) = window_ref { - if cam.primary_window.get(window_entity).is_err() { - // This window isn't the primary, so we skip this root. - continue; - } - } - } - // Hard to know the results of this, better skip this target. - _ => continue, - } - } - - let rect = LayoutRect::new(trans, node, scale_factor); - outline_node(entity, rect, &mut draw); - outline_nodes(&outline, &mut draw, entity, scale_factor); - } -} - -/// Function responsible for drawing the gizmos lines around the given Entity -fn outline_node(entity: Entity, rect: LayoutRect, draw: &mut InsetGizmo) { - let color = Hsla::sequential_dispersed(entity.index()); - - draw.rect_2d(rect, color.into()); - draw.set_scope(rect); -} - -/// The debug overlay plugin. -/// -/// This spawns a new camera with a low order, and draws gizmo. -/// -/// Note that due to limitation with [`bevy_gizmos`], multiple windows with this feature -/// enabled isn't supported and the lines are only drawn in the [`PrimaryWindow`] -pub struct DebugUiPlugin; -impl Plugin for DebugUiPlugin { - fn build(&self, app: &mut App) { - app.init_resource::() - .init_gizmo_group::() - .add_systems( - PostUpdate, - ( - update_debug_camera, - outline_roots - .after(TransformSystem::TransformPropagate) - // This needs to run before VisibilityPropagate so it can relies on ViewVisibility - .before(VisibilitySystems::VisibilityPropagate), - ) - .chain(), - ); - } -} diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index c6f3fed2d9eed..3acc57d16cbf0 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -235,6 +235,9 @@ bevy_sprite_picking_backend = [ # Provides a UI picking backend bevy_ui_picking_backend = ["bevy_picking", "bevy_ui/bevy_ui_picking_backend"] +# Provides a UI debug overlay +bevy_ui_debug = ["bevy_ui?/bevy_ui_debug"] + # Enable support for the ios_simulator by downgrading some rendering capabilities ios_simulator = ["bevy_pbr?/ios_simulator", "bevy_render?/ios_simulator"] diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index 70478d8fe7fb2..2ea7216f29b95 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -43,8 +43,10 @@ smallvec = "1.11" accesskit = "0.17" [features] +default = [] serialize = ["serde", "smallvec/serde", "bevy_math/serialize"] bevy_ui_picking_backend = ["bevy_picking"] +bevy_ui_debug = [] # Experimental features ghost_nodes = [] diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 20e7c55b776ad..4039930b7654c 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -40,12 +40,16 @@ pub use measurement::*; pub use render::*; pub use ui_material::*; pub use ui_node::*; + use widget::{ImageNode, ImageNodeSize}; /// The UI prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. pub mod prelude { + #[doc(hidden)] + #[cfg(feature = "bevy_ui_debug")] + pub use crate::render::UiDebugOptions; #[allow(deprecated)] #[doc(hidden)] pub use crate::widget::TextBundle; @@ -230,6 +234,9 @@ impl Plugin for UiPlugin { return; } + #[cfg(feature = "bevy_ui_debug")] + app.init_resource::(); + build_ui_render(app); } diff --git a/crates/bevy_ui/src/render/debug_overlay.rs b/crates/bevy_ui/src/render/debug_overlay.rs new file mode 100644 index 0000000000000..4d716bde8815e --- /dev/null +++ b/crates/bevy_ui/src/render/debug_overlay.rs @@ -0,0 +1,109 @@ +use bevy_asset::AssetId; +use bevy_color::Hsla; +use bevy_ecs::entity::Entity; +use bevy_ecs::system::Commands; +use bevy_ecs::system::Query; +use bevy_ecs::system::Res; +use bevy_ecs::system::ResMut; +use bevy_ecs::system::Resource; +use bevy_math::Rect; +use bevy_math::Vec2; +use bevy_render::sync_world::RenderEntity; +use bevy_render::sync_world::TemporaryRenderEntity; +use bevy_render::Extract; +use bevy_sprite::BorderRect; +use bevy_transform::components::GlobalTransform; + +use crate::ComputedNode; +use crate::DefaultUiCamera; +use crate::TargetCamera; + +use super::ExtractedUiItem; +use super::ExtractedUiNode; +use super::ExtractedUiNodes; +use super::NodeType; + +/// 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, +} + +impl UiDebugOptions { + pub fn toggle(&mut self) { + self.enabled = !self.enabled; + } +} + +impl Default for UiDebugOptions { + fn default() -> Self { + Self { + enabled: false, + line_width: 1., + } + } +} + +#[allow(clippy::too_many_arguments)] +pub fn extract_debug_overlay( + mut commands: Commands, + debug_options: Extract>, + mut extracted_uinodes: ResMut, + default_ui_camera: Extract, + uinode_query: Extract< + Query<( + Entity, + &ComputedNode, + &GlobalTransform, + Option<&TargetCamera>, + )>, + >, + mapping: Extract>, +) { + if !debug_options.enabled { + return; + } + + for (entity, uinode, transform, camera) in &uinode_query { + let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) + else { + continue; + }; + + let Ok(render_camera_entity) = mapping.get(camera_entity) else { + continue; + }; + + // Extract a border box to display an outline for every UI Node in the layout + extracted_uinodes.uinodes.insert( + commands.spawn(TemporaryRenderEntity).id(), + ExtractedUiNode { + // Add a large number to the UI node's stack index so that the overlay is always drawn on top + stack_index: uinode.stack_index + u32::MAX / 2, + color: Hsla::sequential_dispersed(entity.index()).into(), + rect: Rect { + min: Vec2::ZERO, + max: uinode.size, + }, + clip: None, + image: AssetId::default(), + camera_entity: render_camera_entity, + item: ExtractedUiItem::Node { + atlas_scaling: None, + transform: transform.compute_matrix(), + flip_x: false, + flip_y: false, + border: BorderRect::square( + debug_options.line_width / uinode.inverse_scale_factor(), + ), + border_radius: uinode.border_radius(), + node_type: NodeType::Border, + }, + main_entity: entity.into(), + }, + ); + } +} diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index a77d5cf144f2f..b7801584feb05 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -4,6 +4,9 @@ mod render_pass; mod ui_material_pipeline; pub mod ui_texture_slice_pipeline; +#[cfg(feature = "bevy_ui_debug")] +mod debug_overlay; + use crate::widget::ImageNode; use crate::{ experimental::UiChildren, BackgroundColor, BorderColor, BoxShadowSamples, CalculatedClip, @@ -41,6 +44,8 @@ use bevy_render::{ }; use bevy_sprite::TextureAtlasLayout; use bevy_sprite::{BorderRect, SpriteAssetEvents}; +#[cfg(feature = "bevy_ui_debug")] +pub use debug_overlay::UiDebugOptions; use crate::{Display, Node}; use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo}; @@ -98,6 +103,7 @@ pub enum RenderUiSystem { ExtractTextureSlice, ExtractBorders, ExtractText, + ExtractDebug, } pub fn build_ui_render(app: &mut App) { @@ -125,6 +131,7 @@ pub fn build_ui_render(app: &mut App) { RenderUiSystem::ExtractTextureSlice, RenderUiSystem::ExtractBorders, RenderUiSystem::ExtractText, + RenderUiSystem::ExtractDebug, ) .chain(), ) @@ -136,6 +143,8 @@ pub fn build_ui_render(app: &mut App) { extract_uinode_images.in_set(RenderUiSystem::ExtractImages), extract_uinode_borders.in_set(RenderUiSystem::ExtractBorders), extract_text_sections.in_set(RenderUiSystem::ExtractText), + #[cfg(feature = "bevy_ui_debug")] + debug_overlay::extract_debug_overlay.in_set(RenderUiSystem::ExtractDebug), ), ) .add_systems( diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 1cc83b9e1102f..c07158ef2e1d4 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -62,6 +62,7 @@ The default feature set enables most of the expected features of a game engine, |bevy_debug_stepping|Enable stepping-based debugging of Bevy systems| |bevy_dev_tools|Provides a collection of developer tools| |bevy_remote|Enable the Bevy Remote Protocol| +|bevy_ui_debug|Provides a debug overlay for bevy UI| |bmp|BMP image format support| |dds|DDS compressed texture support| |debug_glam_assert|Enable assertions in debug builds to check the validity of parameters passed to glam| diff --git a/examples/testbed/ui.rs b/examples/testbed/ui.rs index d33013d19a88e..677576cf12bf2 100644 --- a/examples/testbed/ui.rs +++ b/examples/testbed/ui.rs @@ -18,11 +18,8 @@ fn main() { .add_systems(Startup, setup) .add_systems(Update, update_scroll_position); - #[cfg(feature = "bevy_dev_tools")] - { - app.add_plugins(bevy::dev_tools::ui_debug_overlay::DebugUiPlugin) - .add_systems(Update, toggle_overlay); - } + #[cfg(feature = "bevy_ui_debug")] + app.add_systems(Update, toggle_debug_overlay); app.run(); } @@ -79,10 +76,10 @@ fn setup(mut commands: Commands, asset_server: Res) { Label, )); - #[cfg(feature = "bevy_dev_tools")] + #[cfg(feature = "bevy_ui_debug")] // Debug overlay text parent.spawn(( - Text::new("Press Space to enable debug outlines."), + Text::new("Press Space to toggle debug outlines."), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf"), ..default() @@ -90,9 +87,9 @@ fn setup(mut commands: Commands, asset_server: Res) { Label, )); - #[cfg(not(feature = "bevy_dev_tools"))] + #[cfg(not(feature = "bevy_ui_debug"))] parent.spawn(( - Text::new("Try enabling feature \"bevy_dev_tools\"."), + Text::new("Try enabling feature \"bevy_ui_debug\"."), TextFont { font: asset_server.load("fonts/FiraSans-Bold.ttf"), ..default() @@ -347,12 +344,9 @@ fn setup(mut commands: Commands, asset_server: Res) { }); } -#[cfg(feature = "bevy_dev_tools")] +#[cfg(feature = "bevy_ui_debug")] // The system that will enable/disable the debug outlines around the nodes -fn toggle_overlay( - input: Res>, - mut options: ResMut, -) { +fn toggle_debug_overlay(input: Res>, mut options: ResMut) { info_once!("The debug outlines are enabled, press Space to turn them on/off"); if input.just_pressed(KeyCode::Space) { // The toggle method will enable the debug_overlay if disabled and disable if enabled