From 3a79a32f3d0a93173d64e0fd1213f3da4edaa89f Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Thu, 27 Mar 2025 20:43:28 +0100 Subject: [PATCH] Add nice Id popup --- crates/egui/src/context.rs | 12 +- crates/egui/src/id.rs | 150 +++++++++++++++++++++++- crates/egui/src/interaction.rs | 2 +- examples/hello_world_simple/src/main.rs | 13 ++ 4 files changed, 167 insertions(+), 10 deletions(-) diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 44af99feb..8a33ad1b6 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -3185,10 +3185,16 @@ impl Context { if !is_visible { continue; } - let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),); // TODO(emilk): `Sense::hover_highlight()` - let response = - ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click())); + let response = ui + .horizontal(|ui| { + ui.style_mut().interaction.selectable_labels = false; + ui.label(layer_id.order.short_debug_format()); + layer_id.id.ui(ui); + ui.monospace(area.rect().to_string()); + }) + .response + .interact(Sense::click()); if response.hovered() && is_visible { ui.ctx() .debug_painter() diff --git a/crates/egui/src/id.rs b/crates/egui/src/id.rs index 454ceeeab..1268aaa95 100644 --- a/crates/egui/src/id.rs +++ b/crates/egui/src/id.rs @@ -1,5 +1,8 @@ // TODO(emilk): have separate types `PositionId` and `UniqueId`. ? +use crate::id::id_source::IdSource; +use crate::CollapsingHeader; +use epaint::Color32; use std::num::NonZeroU64; /// egui tracks widgets frame-to-frame using [`Id`]s. @@ -97,12 +100,133 @@ impl Id { self.value().into() } - // TODO: Nice debug ui - // pub fn ui(self, ui: &mut crate::Ui) -> crate::Response { - // ui.code(self.short_debug_format()).on_hover_ui(|ui| { - // let data = self.info(); - // }) - // } + fn source_ui(ui: &mut crate::Ui, source: IdSource) { + match source { + IdSource::Id(id) => { + Self::parent_ui(ui, id); + } + IdSource::Other(other) => { + ui.code(other); + } + } + } + + fn parent_ui(ui: &mut crate::Ui, id: Id) { + let data = id.info(); + if let Some(data) = data { + if let Some(parent) = data.parent { + Self::parent_ui(ui, parent); + ui.horizontal(|ui| { + ui.code(".with("); + Self::source_ui(ui, data.source); + ui.code(format!(" /* {} */", id.short_debug_format())); + ui.code(")"); + }); + } else { + ui.horizontal(|ui| { + ui.code("Id::new("); + Self::source_ui(ui, data.source); + ui.code(format!(" /* {} */", id.short_debug_format())); + ui.code(")"); + }); + } + } else { + ui.code(format!("Id::from_hash({})", id.short_debug_format())); + } + } + + fn group_ui(ui: &mut crate::Ui, id: Id) { + ui.group(|ui| { + let info = id.info(); + if let Some(info) = info { + ui.horizontal(|ui| { + ui.label("Id("); + ui.code(format!("{:04X}", id.value() as u16)); + ui.label(")"); + + ui.label("Source:"); + match info.source { + IdSource::Id(id) => { + Self::group_ui(ui, id); + } + IdSource::Other(other) => { + ui.code(other); + } + } + }); + if let Some(parent) = info.parent { + ui.label("^ with"); + Self::group_ui(ui, parent); + } + } else { + } + }); + } + + fn tree_ui(ui: &mut crate::Ui, id: Id, prefix: &str, depth: usize) { + let info = id.info(); + if let Some(info) = info { + let response = + CollapsingHeader::new(format!("{}Id({})", prefix, id.short_debug_format())) + .default_open(depth < 4) + .show(ui, |ui| { + match info.source { + IdSource::Id(id_source) => { + Self::tree_ui(ui, id_source, "Source: ", depth + 1); + } + IdSource::Other(other) => { + ui.horizontal(|ui| { + ui.add_space(ui.spacing().indent); + ui.label("Source:"); + ui.code(other); + }); + } + } + + if let Some(parent) = info.parent { + Self::tree_ui(ui, parent, "Parent: ", depth + 1); + } + }); + + if response.header_response.hovered() { + id.try_highlight(ui.ctx()); + } + } + } + + pub fn try_highlight(self, ctx: &crate::Context) { + let response = ctx.read_response(self); + if let Some(response) = response { + ctx.debug_painter().debug_rect( + response.rect, + Color32::GREEN, + self.short_debug_format(), + ); + } + + if let Some(area_rect) = ctx.memory(|mem| mem.area_rect(self)) { + ctx.debug_painter() + .debug_rect(area_rect, Color32::RED, self.short_debug_format()); + } + } + + pub fn ui(self, ui: &mut crate::Ui) -> crate::Response { + let data = self.info(); + let label = if let Some(data) = &data { + format!("{} ({})", self.short_debug_format(), data.source) + } else { + self.short_debug_format() + }; + let response = ui.code(label).on_hover_ui(|ui| { + Self::tree_ui(ui, self, "", 0); + }); + + if response.hovered() { + self.try_highlight(ui.ctx()); + } + + response + } } #[cfg(debug_assertions)] @@ -110,6 +234,7 @@ mod id_source { use crate::{AsId, Id}; use ahash::HashMap; use epaint::mutex::RwLock; + use std::fmt::{Display, Formatter}; use std::hash::Hasher; use std::sync::LazyLock; @@ -127,6 +252,19 @@ mod id_source { Other(String), } + impl Display for IdSource { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + IdSource::Id(id) => { + write!(f, "{}", id.short_debug_format()) + } + IdSource::Other(other) => { + write!(f, "{}", other) + } + } + } + } + static ID_MAP: LazyLock>> = LazyLock::new(|| { let mut map = HashMap::default(); map.insert( diff --git a/crates/egui/src/interaction.rs b/crates/egui/src/interaction.rs index 64a789bd7..4571fe3e6 100644 --- a/crates/egui/src/interaction.rs +++ b/crates/egui/src/interaction.rs @@ -70,7 +70,7 @@ impl InteractionSnapshot { fn id_ui<'a>(ui: &mut crate::Ui, widgets: impl IntoIterator) { for id in widgets { - ui.label(format!("{:?}", id)); + id.ui(ui); } } diff --git a/examples/hello_world_simple/src/main.rs b/examples/hello_world_simple/src/main.rs index 4fe49a89d..13f94e167 100644 --- a/examples/hello_world_simple/src/main.rs +++ b/examples/hello_world_simple/src/main.rs @@ -2,6 +2,7 @@ #![allow(rustdoc::missing_crate_level_docs)] // it's an example use eframe::egui; +use eframe::egui::{Id, Ui}; fn main() -> eframe::Result { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). @@ -28,6 +29,18 @@ fn main() -> eframe::Result { age += 1; } ui.label(format!("Hello '{name}', age {age}")); + + Id::new(Id::new("Hi").with((123, "456"))) + .with(Id::new("lol")) + .ui(ui); + + ui.id().ui(ui); + Id::ui(Ui::id(ui), ui); + + ui.unique_id().ui(ui); + + let some_button = ui.button("Some button"); + some_button.id.ui(ui); }); }) }