diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index aab81ab2..5a868dd7 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1198,6 +1198,12 @@ impl Context { #[allow(clippy::let_and_return, clippy::allow_attributes)] let res = self.get_response(w); + #[cfg(debug_assertions)] + if res.contains_pointer() { + let plugins = self.read(|ctx| ctx.plugins.ordered_plugins()); + plugins.on_widget_under_pointer(self, &w); + } + #[cfg(feature = "accesskit")] if allow_focus && w.sense.is_focusable() { // Make sure anything that can receive focus has an AccessKit node. diff --git a/crates/egui/src/plugin.rs b/crates/egui/src/plugin.rs index bebcf892..3e078d21 100644 --- a/crates/egui/src/plugin.rs +++ b/crates/egui/src/plugin.rs @@ -34,14 +34,21 @@ pub trait Plugin: Send + Sync + std::any::Any + 'static { /// Called just before the input is processed. /// /// Useful to inspect or modify the input. - /// Since this is called outside a pass, don't show ui here. + /// Since this is called outside a pass, don't show ui here. Using `Context::debug_painter` is fine though. fn input_hook(&mut self, input: &mut RawInput) {} /// Called just before the output is passed to the backend. /// /// Useful to inspect or modify the output. - /// Since this is called outside a pass, don't show ui here. + /// Since this is called outside a pass, don't show ui here. Using `Context::debug_painter` is fine though. fn output_hook(&mut self, output: &mut FullOutput) {} + + /// Called when a widget is created and is under the pointer. + /// + /// Useful for capturing a stack trace so that widgets can be mapped back to their source code. + /// Since this is called outside a pass, don't show ui here. Using `Context::debug_painter` is fine though. + #[cfg(debug_assertions)] + fn on_widget_under_pointer(&mut self, ctx: &Context, widget: &crate::WidgetRect) {} } pub(crate) struct PluginHandle { @@ -167,6 +174,14 @@ impl PluginsOrdered { plugin.output_hook(output); }); } + + #[cfg(debug_assertions)] + pub fn on_widget_under_pointer(&self, ctx: &Context, widget: &crate::WidgetRect) { + profiling::scope!("plugins", "on_widget_under_pointer"); + self.for_each_dyn(|plugin| { + plugin.on_widget_under_pointer(ctx, widget); + }); + } } impl Plugins {