From 8365a3735fd11b35902dad2c14a87421b0d75483 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Sun, 13 Oct 2024 15:36:40 +0200 Subject: [PATCH] debug_ui: Improve layout debug boxes Displaying all at once would be a mess, so this patch adds a way of enabling and disabling specific types of layout debug boxes. --- core/src/debug_ui/display_object.rs | 27 +++++-- core/src/display_object.rs | 2 + core/src/display_object/edit_text.rs | 114 ++++++++++++++++++--------- 3 files changed, 99 insertions(+), 44 deletions(-) diff --git a/core/src/debug_ui/display_object.rs b/core/src/debug_ui/display_object.rs index 456458f27d44..d86435576fd4 100644 --- a/core/src/debug_ui/display_object.rs +++ b/core/src/debug_ui/display_object.rs @@ -10,8 +10,8 @@ use crate::debug_ui::handle::{AVM1ObjectHandle, AVM2ObjectHandle, DisplayObjectH use crate::debug_ui::movie::open_movie_button; use crate::debug_ui::Message; use crate::display_object::{ - AutoSizeMode, Bitmap, DisplayObject, EditText, InteractiveObject, MovieClip, Stage, - TDisplayObject, TDisplayObjectContainer, TInteractiveObject, + AutoSizeMode, Bitmap, DisplayObject, EditText, InteractiveObject, LayoutDebugBoxesFlag, + MovieClip, Stage, TDisplayObject, TDisplayObjectContainer, TInteractiveObject, }; use crate::focus_tracker::Highlight; use egui::collapsing_header::CollapsingState; @@ -464,12 +464,23 @@ impl DisplayObjectWindow { } ui.end_row(); - ui.label("Draw Layout Boxes"); - ui.horizontal(|ui| { - let mut draw_layout_boxes = object.draw_layout_boxes(); - ui.checkbox(&mut draw_layout_boxes, "Enabled"); - if draw_layout_boxes != object.draw_layout_boxes() { - object.set_draw_layout_boxes(context, draw_layout_boxes); + ui.label("Layout Debug Boxes"); + ui.vertical(|ui| { + for (name, flag) in [ + ("Text Exterior", LayoutDebugBoxesFlag::TEXT_EXTERIOR), + ("Text", LayoutDebugBoxesFlag::TEXT), + ("Line", LayoutDebugBoxesFlag::LINE), + ("Line Interior", LayoutDebugBoxesFlag::LINE_INTERIOR), + ("Box", LayoutDebugBoxesFlag::BOX), + ("Box Interior", LayoutDebugBoxesFlag::BOX_INTERIOR), + ("Character", LayoutDebugBoxesFlag::CHAR), + ] { + let old_value = object.layout_debug_boxes_flag(flag); + let mut value = old_value; + ui.checkbox(&mut value, name); + if value != old_value { + object.set_layout_debug_boxes_flag(context, flag, value); + } } }); ui.end_row(); diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 9941124173b5..80e4d30c3820 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -48,6 +48,8 @@ pub use crate::display_object::container::{ pub use avm1_button::{Avm1Button, ButtonState, ButtonTracking}; pub use avm2_button::Avm2Button; pub use bitmap::{Bitmap, BitmapClass}; +#[allow(unused)] +pub use edit_text::LayoutDebugBoxesFlag; pub use edit_text::{AutoSizeMode, EditText, TextSelection}; pub use graphic::Graphic; pub use interactive::{Avm2MousePick, InteractiveObject, TInteractiveObject}; diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 82b3331133cd..71193e7b3c96 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -162,6 +162,10 @@ pub struct EditTextData<'gc> { #[collect(require_static)] flags: EditTextFlag, + /// Flags specifying how layout debug boxes should be drawn. + #[collect(require_static)] + layout_debug_boxes_flags: LayoutDebugBoxesFlag, + /// Whether this EditText represents an AVM2 TextLine. is_tlf: bool, @@ -304,6 +308,7 @@ impl<'gc> EditText<'gc> { is_tlf: false, restrict: EditTextRestrict::allow_all(), last_click: None, + layout_debug_boxes_flags: LayoutDebugBoxesFlag::empty(), }, )); @@ -625,18 +630,20 @@ impl<'gc> EditText<'gc> { self.0.write(gc_context).is_tlf = is_tlf; } - pub fn draw_layout_boxes(self) -> bool { - self.0 - .read() - .flags - .contains(EditTextFlag::DRAW_LAYOUT_BOXES) + pub fn layout_debug_boxes_flag(self, flag: LayoutDebugBoxesFlag) -> bool { + self.0.read().layout_debug_boxes_flags.contains(flag) } - pub fn set_draw_layout_boxes(self, context: &mut UpdateContext<'gc>, value: bool) { + pub fn set_layout_debug_boxes_flag( + self, + context: &mut UpdateContext<'gc>, + flag: LayoutDebugBoxesFlag, + value: bool, + ) { self.0 .write(context.gc()) - .flags - .set(EditTextFlag::DRAW_LAYOUT_BOXES, value); + .layout_debug_boxes_flags + .set(flag, value); } pub fn replace_text( @@ -895,6 +902,48 @@ impl<'gc> EditText<'gc> { } } + fn render_debug_boxes( + self, + context: &mut RenderContext<'_, 'gc>, + flags: LayoutDebugBoxesFlag, + layout: &Layout<'gc>, + ) { + if flags.contains(LayoutDebugBoxesFlag::CHAR) { + let text = &self.text(); + for i in 0..text.len() { + if let Some(bounds) = layout.char_bounds(i, text) { + context.draw_rect_outline(Color::MAGENTA, bounds, Twips::ONE); + } + } + } + if flags.contains(LayoutDebugBoxesFlag::BOX_INTERIOR) { + for lbox in layout.boxes_iter() { + context.draw_rect_outline(Color::RED, lbox.interior_bounds().into(), Twips::ONE); + } + } + if flags.contains(LayoutDebugBoxesFlag::BOX) { + for lbox in layout.boxes_iter() { + context.draw_rect_outline(Color::RED, lbox.bounds().into(), Twips::ONE); + } + } + if flags.contains(LayoutDebugBoxesFlag::LINE_INTERIOR) { + for line in layout.lines() { + context.draw_rect_outline(Color::BLUE, line.interior_bounds().into(), Twips::ONE); + } + } + if flags.contains(LayoutDebugBoxesFlag::LINE) { + for line in layout.lines() { + context.draw_rect_outline(Color::BLUE, line.bounds().into(), Twips::ONE); + } + } + if flags.contains(LayoutDebugBoxesFlag::TEXT) { + context.draw_rect_outline(Color::GREEN, layout.bounds().into(), Twips::ONE); + } + if flags.contains(LayoutDebugBoxesFlag::TEXT_EXTERIOR) { + context.draw_rect_outline(Color::GREEN, layout.exterior_bounds().into(), Twips::ONE); + } + } + /// Render a layout box, plus its children. fn render_layout_box(self, context: &mut RenderContext<'_, 'gc>, lbox: &LayoutBox<'gc>) { let origin = lbox.interior_bounds().origin(); @@ -2328,35 +2377,16 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { ..Default::default() }); - { - let draw_boxes = edit_text.flags.contains(EditTextFlag::DRAW_LAYOUT_BOXES); - if draw_boxes { - context.draw_rect_outline( - Color::GREEN, - edit_text.layout.exterior_bounds().into(), - Twips::ONE, - ); - - let text = &self.text(); - for i in 0..text.len() { - if let Some(bounds) = edit_text.layout.char_bounds(i, text) { - context.draw_rect_outline(Color::MAGENTA, bounds, Twips::ONE); - } - } - } - - for layout_box in edit_text.layout.boxes_iter() { - if draw_boxes { - context.draw_rect_outline( - Color::RED, - layout_box.interior_bounds().into(), - Twips::ONE, - ); - } - self.render_layout_box(context, layout_box); - } + for layout_box in edit_text.layout.boxes_iter() { + self.render_layout_box(context, layout_box); } + self.render_debug_boxes( + context, + edit_text.layout_debug_boxes_flags, + &edit_text.layout, + ); + context.transform_stack.pop(); context.commands.deactivate_mask(); @@ -2734,7 +2764,6 @@ bitflags::bitflags! { struct EditTextFlag: u16 { const FIRING_VARIABLE_BINDING = 1 << 0; const HAS_BACKGROUND = 1 << 1; - const DRAW_LAYOUT_BOXES = 1 << 2; const CONDENSE_WHITE = 1 << 13; const ALWAYS_SHOW_SELECTION = 1 << 14; @@ -2753,6 +2782,19 @@ bitflags::bitflags! { } } +bitflags::bitflags! { + #[derive(Clone, Copy)] + pub struct LayoutDebugBoxesFlag: u8 { + const TEXT_EXTERIOR = 1 << 1; + const TEXT = 1 << 2; + const LINE = 1 << 3; + const LINE_INTERIOR = 1 << 4; + const BOX = 1 << 5; + const BOX_INTERIOR = 1 << 6; + const CHAR = 1 << 7; + } +} + /// Static data shared between all instances of a text object. #[derive(Debug, Clone, Collect)] #[collect(require_static)]