From 95b62ce144a28cbb50f10a83f071cc26c8011e6e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 31 Mar 2024 20:32:05 +0200 Subject: [PATCH] Show `WidgetInfo` for each widget if `debug.show_interactive_widgets` This was useful during debugging --- crates/egui/src/context.rs | 48 +++++++++++++++++++++++++++++----- crates/egui/src/response.rs | 8 ++++++ crates/egui/src/widget_rect.rs | 36 ++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 74a71da2..aa984952 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -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 = 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); } diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index 6946e157..18598f81 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -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)); } diff --git a/crates/egui/src/widget_rect.rs b/crates/egui/src/widget_rect.rs index bf393fe6..7c502f13 100644 --- a/crates/egui/src/widget_rect.rs +++ b/crates/egui/src/widget_rect.rs @@ -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>, /// 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, +} + +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) + } }