Skip to main content

inset_rendering/
debug.rs

1//! Flutter counterpart: `rendering/debug.dart`.
2
3use std::sync::atomic::{AtomicBool, Ordering};
4
5use inset_embedder::{Canvas, Color, Paint, Rect};
6use inset_painting::{Axis, FillRule, PathBuilder};
7
8use crate::box_::BoxConstraints;
9
10static DEBUG_PAINT_SIZE_ENABLED: AtomicBool = AtomicBool::new(false);
11static DEBUG_PAINT_BASELINES_ENABLED: AtomicBool = AtomicBool::new(false);
12static DEBUG_PAINT_TEXT_LAYOUT_BOXES: AtomicBool = AtomicBool::new(false);
13static DEBUG_PAINT_LAYER_BORDERS_ENABLED: AtomicBool = AtomicBool::new(false);
14static DEBUG_PAINT_POINTERS_ENABLED: AtomicBool = AtomicBool::new(false);
15static DEBUG_REPAINT_RAINBOW_ENABLED: AtomicBool = AtomicBool::new(false);
16static DEBUG_REPAINT_TEXT_RAINBOW_ENABLED: AtomicBool = AtomicBool::new(false);
17static DEBUG_PRINT_MARK_NEEDS_LAYOUT_STACKS: AtomicBool = AtomicBool::new(false);
18static DEBUG_PRINT_MARK_NEEDS_PAINT_STACKS: AtomicBool = AtomicBool::new(false);
19static DEBUG_PRINT_LAYOUTS: AtomicBool = AtomicBool::new(false);
20static DEBUG_CHECK_INTRINSIC_SIZES: AtomicBool = AtomicBool::new(false);
21static DEBUG_PROFILE_LAYOUTS_ENABLED: AtomicBool = AtomicBool::new(false);
22static DEBUG_PROFILE_PAINTS_ENABLED: AtomicBool = AtomicBool::new(false);
23static DEBUG_ENHANCE_LAYOUT_TIMELINE_ARGUMENTS: AtomicBool = AtomicBool::new(false);
24static DEBUG_ENHANCE_PAINT_TIMELINE_ARGUMENTS: AtomicBool = AtomicBool::new(false);
25static DEBUG_DISABLE_CLIP_LAYERS: AtomicBool = AtomicBool::new(false);
26static DEBUG_DISABLE_PHYSICAL_SHAPE_LAYERS: AtomicBool = AtomicBool::new(false);
27static DEBUG_DISABLE_OPACITY_LAYERS: AtomicBool = AtomicBool::new(false);
28
29macro_rules! debug_flag {
30    ($get:ident, $set:ident, $static:ident, $doc:expr) => {
31        #[doc = $doc]
32        pub fn $get() -> bool {
33            $static.load(Ordering::Relaxed)
34        }
35
36        #[doc = concat!("Sets [`", stringify!($get), "`].")]
37        pub fn $set(value: bool) {
38            $static.store(value, Ordering::Relaxed);
39        }
40    };
41}
42
43debug_flag!(
44    debug_paint_size_enabled,
45    set_debug_paint_size_enabled,
46    DEBUG_PAINT_SIZE_ENABLED,
47    "Causes each RenderBox to paint a box around its bounds."
48);
49debug_flag!(
50    debug_paint_baselines_enabled,
51    set_debug_paint_baselines_enabled,
52    DEBUG_PAINT_BASELINES_ENABLED,
53    "Causes each RenderBox to paint a line at each of its baselines."
54);
55debug_flag!(
56    debug_paint_text_layout_boxes,
57    set_debug_paint_text_layout_boxes,
58    DEBUG_PAINT_TEXT_LAYOUT_BOXES,
59    "Causes each RenderParagraph to paint the layout boxes of its text."
60);
61debug_flag!(
62    debug_paint_layer_borders_enabled,
63    set_debug_paint_layer_borders_enabled,
64    DEBUG_PAINT_LAYER_BORDERS_ENABLED,
65    "Causes each Layer to paint a box around its bounds."
66);
67debug_flag!(
68    debug_paint_pointers_enabled,
69    set_debug_paint_pointers_enabled,
70    DEBUG_PAINT_POINTERS_ENABLED,
71    "Causes objects like RenderPointerListener to flash while they are being tapped."
72);
73debug_flag!(
74    debug_repaint_rainbow_enabled,
75    set_debug_repaint_rainbow_enabled,
76    DEBUG_REPAINT_RAINBOW_ENABLED,
77    "Overlay a rotating set of colors when repainting layers in debug mode."
78);
79debug_flag!(
80    debug_repaint_text_rainbow_enabled,
81    set_debug_repaint_text_rainbow_enabled,
82    DEBUG_REPAINT_TEXT_RAINBOW_ENABLED,
83    "Overlay a rotating set of colors when repainting text in debug mode."
84);
85debug_flag!(
86    debug_print_mark_needs_layout_stacks,
87    set_debug_print_mark_needs_layout_stacks,
88    DEBUG_PRINT_MARK_NEEDS_LAYOUT_STACKS,
89    "Log the call stacks that mark render objects as needing layout."
90);
91debug_flag!(
92    debug_print_mark_needs_paint_stacks,
93    set_debug_print_mark_needs_paint_stacks,
94    DEBUG_PRINT_MARK_NEEDS_PAINT_STACKS,
95    "Log the call stacks that mark render objects as needing paint."
96);
97debug_flag!(
98    debug_print_layouts,
99    set_debug_print_layouts,
100    DEBUG_PRINT_LAYOUTS,
101    "Log the dirty render objects that are laid out each frame."
102);
103debug_flag!(
104    debug_check_intrinsic_sizes,
105    set_debug_check_intrinsic_sizes,
106    DEBUG_CHECK_INTRINSIC_SIZES,
107    "Check the intrinsic sizes of each RenderBox during layout."
108);
109debug_flag!(
110    debug_profile_layouts_enabled,
111    set_debug_profile_layouts_enabled,
112    DEBUG_PROFILE_LAYOUTS_ENABLED,
113    "Adds Timeline events for every RenderObject layout."
114);
115debug_flag!(
116    debug_profile_paints_enabled,
117    set_debug_profile_paints_enabled,
118    DEBUG_PROFILE_PAINTS_ENABLED,
119    "Adds Timeline events for every RenderObject painted."
120);
121debug_flag!(
122    debug_enhance_layout_timeline_arguments,
123    set_debug_enhance_layout_timeline_arguments,
124    DEBUG_ENHANCE_LAYOUT_TIMELINE_ARGUMENTS,
125    "Adds debugging information to Timeline events related to RenderObject layouts."
126);
127debug_flag!(
128    debug_enhance_paint_timeline_arguments,
129    set_debug_enhance_paint_timeline_arguments,
130    DEBUG_ENHANCE_PAINT_TIMELINE_ARGUMENTS,
131    "Adds debugging information to Timeline events related to RenderObject paints."
132);
133debug_flag!(
134    debug_disable_clip_layers,
135    set_debug_disable_clip_layers,
136    DEBUG_DISABLE_CLIP_LAYERS,
137    "Setting to true will cause all clipping effects from the layer tree to be ignored."
138);
139debug_flag!(
140    debug_disable_physical_shape_layers,
141    set_debug_disable_physical_shape_layers,
142    DEBUG_DISABLE_PHYSICAL_SHAPE_LAYERS,
143    "Setting to true will cause all physical modeling effects from the layer tree to be ignored."
144);
145debug_flag!(
146    debug_disable_opacity_layers,
147    set_debug_disable_opacity_layers,
148    DEBUG_DISABLE_OPACITY_LAYERS,
149    "Setting to true will cause all opacity effects from the layer tree to be ignored."
150);
151
152fn debug_draw_double_rect(canvas: &mut Canvas, outer_rect: Rect, inner_rect: Rect, color: Color) {
153    let mut path = PathBuilder::new();
154    path.rect(outer_rect.into());
155    path.rect(inner_rect.into());
156    let paint = Paint {
157        color: color.into(),
158        ..Paint::default()
159    };
160    canvas.draw_path(&path.build(), FillRule::EvenOdd, &paint);
161}
162
163/// Paint a diagram showing the given area as padding.
164pub fn debug_paint_padding(
165    canvas: &mut Canvas,
166    outer_rect: Rect,
167    inner_rect: Option<Rect>,
168    outline_width: f64,
169) {
170    if cfg!(debug_assertions) {
171        if let Some(inner_rect) = inner_rect.filter(|rect| !rect.is_empty()) {
172            debug_draw_double_rect(
173                canvas,
174                outer_rect,
175                inner_rect,
176                Color::from_argb(0x90, 0x00, 0x90, 0xFF),
177            );
178            debug_draw_double_rect(
179                canvas,
180                inner_rect.inflate(outline_width).intersect(outer_rect),
181                inner_rect,
182                Color::from_argb(0xFF, 0x00, 0x90, 0xFF),
183            );
184        } else {
185            let paint = Paint {
186                color: Color::from_argb(0x90, 0x90, 0x90, 0x90).into(),
187                ..Paint::default()
188            };
189            canvas.draw_rect(outer_rect, &paint);
190        }
191    }
192}
193
194/// Returns true if none of the rendering library debug variables have been changed.
195pub fn debug_assert_all_render_vars_unset(
196    reason: &str,
197    debug_check_intrinsic_sizes_override: bool,
198) -> bool {
199    debug_assert!(
200        !(debug_paint_size_enabled()
201            || debug_paint_baselines_enabled()
202            || debug_paint_layer_borders_enabled()
203            || debug_paint_text_layout_boxes()
204            || debug_paint_pointers_enabled()
205            || debug_repaint_rainbow_enabled()
206            || debug_repaint_text_rainbow_enabled()
207            || debug_print_mark_needs_layout_stacks()
208            || debug_print_mark_needs_paint_stacks()
209            || debug_print_layouts()
210            || debug_check_intrinsic_sizes() != debug_check_intrinsic_sizes_override
211            || debug_profile_layouts_enabled()
212            || debug_profile_paints_enabled()
213            || debug_disable_clip_layers()
214            || debug_disable_physical_shape_layers()
215            || debug_disable_opacity_layers()),
216        "{reason}"
217    );
218    true
219}
220
221/// Returns true if the given [`Axis`] is bounded within the given
222/// [`BoxConstraints`] in both the main and cross axis.
223pub fn debug_check_has_bounded_axis(axis: Axis, constraints: BoxConstraints) -> bool {
224    debug_assert!(
225        constraints.has_bounded_height() && constraints.has_bounded_width(),
226        "{axis:?} viewport was given unbounded constraints: {constraints}"
227    );
228    true
229}