Show `WidgetInfo` for each widget if `debug.show_interactive_widgets`

This was useful during debugging
This commit is contained in:
Emil Ernerfeldt 2024-03-31 20:32:05 +02:00
parent aa2f87e0ff
commit 95b62ce144
3 changed files with 83 additions and 9 deletions

View File

@ -1202,6 +1202,19 @@ impl Context {
res
}
/// This is called by [`Response::widget_info`], but can also be called directly.
///
/// With some debug flags it will store the widget info in [`WidgetRects`] for later display.
#[inline]
pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
#[cfg(debug_assertions)]
self.write(|ctx| {
if ctx.memory.options.style.debug.show_interactive_widgets {
ctx.viewport().widgets_this_frame.set_info(id, make_info());
}
});
}
/// Get a full-screen painter for a new or existing layer
pub fn layer_painter(&self, layer_id: LayerId) -> Painter {
let screen_rect = self.screen_rect();
@ -1807,6 +1820,7 @@ impl Context {
self.write(|ctx| ctx.end_frame())
}
/// Called at the end of the frame.
#[cfg(debug_assertions)]
fn debug_painting(&self) {
let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {
@ -1839,8 +1853,8 @@ impl Context {
} else if rect.sense.drag {
(Color32::from_rgb(0, 0, 0x88), "drag")
} else {
continue;
// (Color32::from_rgb(0, 0, 0x88), "hover")
// unreachable since we only show interactive
(Color32::from_rgb(0, 0, 0x88), "hover")
};
painter.debug_rect(rect.interact_rect, color, text);
}
@ -1860,10 +1874,32 @@ impl Context {
hovered,
} = interact_widgets;
if false {
for widget in contains_pointer {
paint_widget_id(widget, "contains_pointer", Color32::BLUE);
if true {
for &id in &contains_pointer {
paint_widget_id(id, "contains_pointer", Color32::BLUE);
}
let widget_rects = self.write(|w| w.viewport().widgets_this_frame.clone());
let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
contains_pointer.sort_by_key(|&id| {
widget_rects
.order(id)
.map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))
});
let mut debug_text = "Widgets in order:\n".to_owned();
for id in contains_pointer {
let mut widget_text = format!("{id:?}");
if let Some(rect) = widget_rects.get(id) {
widget_text += &format!(" {:?} {:?}", rect.rect, rect.sense);
}
if let Some(info) = widget_rects.info(id) {
widget_text += &format!(" {info:?}");
}
debug_text += &format!("{widget_text}\n");
}
self.debug_text(debug_text);
}
if true {
for widget in hovered {
@ -1887,7 +1923,7 @@ impl Context {
drag,
} = hits;
if false {
if true {
for widget in &contains_pointer {
paint_widget(widget, "contains_pointer", Color32::BLUE);
}

View File

@ -745,6 +745,7 @@ impl Response {
/// Call after interacting and potential calls to [`Self::mark_changed`].
pub fn widget_info(&self, make_info: impl Fn() -> crate::WidgetInfo) {
use crate::output::OutputEvent;
let event = if self.clicked() {
Some(OutputEvent::Clicked(make_info()))
} else if self.double_clicked() {
@ -758,6 +759,7 @@ impl Response {
} else {
None
};
if let Some(event) = event {
self.output_event(event);
} else {
@ -765,6 +767,8 @@ impl Response {
self.ctx.accesskit_node_builder(self.id, |builder| {
self.fill_accesskit_node_from_widget_info(builder, make_info());
});
self.ctx.register_widget_info(self.id, make_info);
}
}
@ -773,6 +777,10 @@ impl Response {
self.ctx.accesskit_node_builder(self.id, |builder| {
self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone());
});
self.ctx
.register_widget_info(self.id, || event.widget_info().clone());
self.ctx.output_mut(|o| o.events.push(event));
}

View File

@ -46,13 +46,25 @@ pub struct WidgetRect {
///
/// All [`Ui`]s have a [`WidgetRects`], but whether or not their rects are correct
/// depends on if [`Ui::interact_bg`] was ever called.
#[derive(Default, Clone, PartialEq, Eq)]
#[derive(Default, Clone)]
pub struct WidgetRects {
/// All widgets, in painting order.
by_layer: HashMap<LayerId, Vec<WidgetRect>>,
/// All widgets, by id, and their order in their respective layer
by_id: IdMap<(usize, WidgetRect)>,
/// Info about some widgets.
///
/// Only filled in if the widget is interacted with,
/// or if this is a debug build.
infos: IdMap<WidgetInfo>,
}
impl PartialEq for WidgetRects {
fn eq(&self, other: &Self) -> bool {
self.by_layer == other.by_layer
}
}
impl WidgetRects {
@ -90,18 +102,28 @@ impl WidgetRects {
/// Clear the contents while retaining allocated memory.
pub fn clear(&mut self) {
let Self { by_layer, by_id } = self;
let Self {
by_layer,
by_id,
infos,
} = self;
for rects in by_layer.values_mut() {
rects.clear();
}
by_id.clear();
infos.clear();
}
/// Insert the given widget rect in the given layer.
pub fn insert(&mut self, layer_id: LayerId, widget_rect: WidgetRect) {
let Self { by_layer, by_id } = self;
let Self {
by_layer,
by_id,
infos: _,
} = self;
let layer_widgets = by_layer.entry(layer_id).or_default();
@ -134,4 +156,12 @@ impl WidgetRects {
}
}
}
pub fn set_info(&mut self, id: Id, info: WidgetInfo) {
self.infos.insert(id, info);
}
pub fn info(&self, id: Id) -> Option<&WidgetInfo> {
self.infos.get(&id)
}
}