Stack Tool: Added Stack Tool (ShowStackToolWindow() function and available from Demo and Metrics window). (#4631)
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index ef9f4f0..4fea7c3 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -42,6 +42,10 @@
Other Changes:
+- Debug: Stack Tool: Added "Stack Tool" available in "Demo->Examples->Stack Tool", "Metrics->Tools",
+ or by calling the ShowStackToolWindow() function. The tool run queries on hovered id to display
+ details about individual components that were hashed to create an ID. It helps understanding
+ the ID stack system and debugging potential ID collisions. (#4631) [@ocornut, @rokups]
- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493)
- IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups
from the tested windows when combined with _ChildWindows.
diff --git a/imconfig.h b/imconfig.h
index a0c86ca..7082c55 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -33,7 +33,7 @@
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
-//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty.
+//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
diff --git a/imgui.cpp b/imgui.cpp
index a15559e..eaa9696 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -82,6 +82,7 @@
// [SECTION] VIEWPORTS
// [SECTION] PLATFORM DEPENDENT HELPERS
// [SECTION] METRICS/DEBUGGER WINDOW
+// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
*/
@@ -924,16 +925,17 @@
static void NavRestoreLayer(ImGuiNavLayer layer);
static int FindWindowFocusIndex(ImGuiWindow* window);
-// Error Checking
+// Error Checking and Debug Tools
static void ErrorCheckNewFrameSanityChecks();
static void ErrorCheckEndFrameSanityChecks();
+static void UpdateDebugToolItemPicker();
+static void UpdateDebugToolStackQueries();
// Misc
static void UpdateSettings();
static void UpdateMouseInputs();
static void UpdateMouseWheel();
static void UpdateTabFocus();
-static void UpdateDebugToolItemPicker();
static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
static void RenderWindowOuterBorders(ImGuiWindow* window);
static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
@@ -2967,10 +2969,9 @@
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
return id;
}
@@ -2979,10 +2980,9 @@
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
return id;
}
@@ -2991,10 +2991,9 @@
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
return id;
}
@@ -3002,10 +3001,9 @@
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
return id;
}
@@ -3013,10 +3011,9 @@
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
return id;
}
@@ -3024,10 +3021,9 @@
{
ImGuiID seed = IDStack.back();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
-#endif
+ if (g.DebugHookIdInfo == id)
+ ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
return id;
}
@@ -4159,8 +4155,9 @@
g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
g.GroupStack.resize(0);
- // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
+ // [DEBUG] Update debug features
UpdateDebugToolItemPicker();
+ UpdateDebugToolStackQueries();
// Create implicit/fallback window - which we will only render it if the user has added something to it.
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
@@ -4173,31 +4170,6 @@
CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
}
-// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
-void ImGui::UpdateDebugToolItemPicker()
-{
- ImGuiContext& g = *GImGui;
- g.DebugItemPickerBreakId = 0;
- if (g.DebugItemPickerActive)
- {
- const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
- SetMouseCursor(ImGuiMouseCursor_Hand);
- if (IsKeyPressedMap(ImGuiKey_Escape))
- g.DebugItemPickerActive = false;
- if (IsMouseClicked(0) && hovered_id)
- {
- g.DebugItemPickerBreakId = hovered_id;
- g.DebugItemPickerActive = false;
- }
- SetNextWindowBgAlpha(0.60f);
- BeginTooltip();
- Text("HoveredId: 0x%08X", hovered_id);
- Text("Press ESC to abort picking.");
- TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
- EndTooltip();
- }
-}
-
void ImGui::Initialize(ImGuiContext* context)
{
ImGuiContext& g = *context;
@@ -7184,6 +7156,8 @@
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
+ if (g.DebugHookIdInfo == id)
+ DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
window->IDStack.push_back(id);
}
@@ -7193,11 +7167,10 @@
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
{
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
- ImGui::KeepAliveID(id);
-#ifdef IMGUI_ENABLE_TEST_ENGINE
+ KeepAliveID(id);
ImGuiContext& g = *GImGui;
- IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
-#endif
+ if (g.DebugHookIdInfo == id)
+ DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
return id;
}
@@ -11226,22 +11199,23 @@
void ImGui::ShowMetricsWindow(bool* p_open)
{
+ ImGuiContext& g = *GImGui;
+ ImGuiIO& io = g.IO;
+ ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
+ if (cfg->ShowStackTool)
+ ShowStackToolWindow(&cfg->ShowStackTool);
+
if (!Begin("Dear ImGui Metrics/Debugger", p_open))
{
End();
return;
}
- ImGuiContext& g = *GImGui;
- ImGuiIO& io = g.IO;
- ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
-
// Basic info
Text("Dear ImGui %s", GetVersion());
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
- Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
- Text("%d active allocations", io.MetricsActiveAllocations);
+ Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations);
//SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
Separator();
@@ -11295,11 +11269,10 @@
// Tools
if (TreeNode("Tools"))
{
- // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
- if (Button("Item Picker.."))
- DebugStartItemPicker();
+ // Stack Tool is your best friend!
+ Checkbox("Show stack tool", &cfg->ShowStackTool);
SameLine();
- MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
+ MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code.");
Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
@@ -11317,8 +11290,6 @@
}
Unindent();
}
- Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
- Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
SameLine();
@@ -11365,6 +11336,12 @@
}
}
+ // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
+ if (Button("Item Picker.."))
+ DebugStartItemPicker();
+ SameLine();
+ MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
+
TreePop();
}
@@ -11378,6 +11355,8 @@
drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
{
+ Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
+ Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
{
ImGuiViewportP* viewport = g.Viewports[viewport_i];
@@ -12002,6 +11981,186 @@
TreePop();
}
+//-----------------------------------------------------------------------------
+// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL)
+//-----------------------------------------------------------------------------
+
+// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
+void ImGui::UpdateDebugToolItemPicker()
+{
+ ImGuiContext& g = *GImGui;
+ g.DebugItemPickerBreakId = 0;
+ if (!g.DebugItemPickerActive)
+ return;
+
+ const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
+ SetMouseCursor(ImGuiMouseCursor_Hand);
+ if (IsKeyPressedMap(ImGuiKey_Escape))
+ g.DebugItemPickerActive = false;
+ if (IsMouseClicked(0) && hovered_id)
+ {
+ g.DebugItemPickerBreakId = hovered_id;
+ g.DebugItemPickerActive = false;
+ }
+ SetNextWindowBgAlpha(0.60f);
+ BeginTooltip();
+ Text("HoveredId: 0x%08X", hovered_id);
+ Text("Press ESC to abort picking.");
+ TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
+ EndTooltip();
+}
+
+// [DEBUG] Stack Tool: update queries. Called by NewFrame()
+void ImGui::UpdateDebugToolStackQueries()
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiStackTool* tool = &g.DebugStackTool;
+
+ // Clear hook when stack tool is not visible
+ g.DebugHookIdInfo = 0;
+ if (g.FrameCount != tool->LastActiveFrame + 1)
+ return;
+
+ // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
+ // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
+ const ImGuiID query_id = g.ActiveId ? g.ActiveId : g.HoveredIdPreviousFrame;
+ if (tool->QueryId != query_id)
+ {
+ tool->QueryId = query_id;
+ tool->StackLevel = -1;
+ tool->Results.resize(0);
+ }
+ if (query_id == 0)
+ return;
+
+ // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
+ int stack_level = tool->StackLevel;
+ if (stack_level >= 0 && stack_level < tool->Results.Size)
+ if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
+ tool->StackLevel++;
+
+ // Update hook
+ stack_level = tool->StackLevel;
+ if (stack_level == -1)
+ g.DebugHookIdInfo = query_id;
+ if (stack_level >= 0 && stack_level < tool->Results.Size)
+ {
+ g.DebugHookIdInfo = tool->Results[stack_level].ID;
+ tool->Results[stack_level].QueryFrameCount++;
+ }
+}
+
+// [DEBUG] Stack tool: hooks called by GetID() family functions
+void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiStackTool* tool = &g.DebugStackTool;
+
+ // Step 0: stack query
+ // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget.
+ if (tool->StackLevel == -1)
+ {
+ tool->StackLevel++;
+ tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
+ for (int n = 0; n < window->IDStack.Size + 1; n++)
+ tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
+ return;
+ }
+
+ // Step 1+: query for individual level
+ IM_ASSERT(tool->StackLevel >= 0);
+ if (tool->StackLevel != window->IDStack.Size)
+ return;
+ ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
+ IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
+
+ int data_len;
+ switch (data_type)
+ {
+ case ImGuiDataType_S32:
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
+ break;
+ case ImGuiDataType_String:
+ data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id);
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id);
+ break;
+ case ImGuiDataType_Pointer:
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
+ break;
+ case ImGuiDataType_ID:
+ if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
+ ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
+ break;
+ default:
+ IM_ASSERT(0);
+ }
+ info->QuerySuccess = true;
+}
+
+// Stack Tool: Display UI
+void ImGui::ShowStackToolWindow(bool* p_open)
+{
+ if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
+ {
+ End();
+ return;
+ }
+
+ // Display hovered/active status
+ ImGuiContext& g = *GImGui;
+ const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
+ const ImGuiID active_id = g.ActiveId;
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : "");
+#else
+ Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id);
+#endif
+ SameLine();
+ MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
+
+ // Display decorated stack
+ ImGuiStackTool* tool = &g.DebugStackTool;
+ tool->LastActiveFrame = g.FrameCount;
+ if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
+ {
+ const float id_width = CalcTextSize("0xDDDDDDDD").x;
+ TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
+ TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
+ TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
+ TableHeadersRow();
+ for (int n = 0; n < tool->Results.Size; n++)
+ {
+ ImGuiStackLevelInfo* info = &tool->Results[n];
+ TableNextColumn();
+ Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
+
+ TableNextColumn();
+ ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL;
+ if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
+ Text("\"%s\" [window]", window->Name);
+ else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
+ TextUnformatted(info->Desc);
+ else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
+ {
+#ifdef IMGUI_ENABLE_TEST_ENGINE
+ if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
+ Text("??? \"%s\"", label);
+ else
+#endif
+ TextUnformatted("???");
+ }
+
+ TableNextColumn();
+ Text("0x%08X", info->ID);
+ if (n == tool->Results.Size - 1)
+ TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
+ }
+ EndTable();
+ }
+ End();
+}
+
#else
void ImGui::ShowMetricsWindow(bool*) {}
@@ -12017,7 +12176,12 @@
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
-#endif
+void ImGui::ShowStackToolWindow(bool*) {}
+void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
+void ImGui::UpdateDebugToolItemPicker() {}
+void ImGui::UpdateDebugToolStackQueries() {}
+
+#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW
//-----------------------------------------------------------------------------
diff --git a/imgui.h b/imgui.h
index c2fdad8..f66fd95 100644
--- a/imgui.h
+++ b/imgui.h
@@ -64,7 +64,7 @@
// Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.85 WIP"
-#define IMGUI_VERSION_NUM 18418
+#define IMGUI_VERSION_NUM 18419
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE
@@ -307,6 +307,7 @@
// Demo, Debug, Information
IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!
IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.
+ IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.
IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information.
IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)
IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles.
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index febca56..6977957 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -294,10 +294,12 @@
// Dear ImGui Apps (accessible from the "Tools" menu)
static bool show_app_metrics = false;
+ static bool show_app_stack_tool = false;
static bool show_app_style_editor = false;
static bool show_app_about = false;
if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); }
+ if (show_app_stack_tool) { ImGui::ShowStackToolWindow(&show_app_stack_tool); }
if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); }
if (show_app_style_editor)
{
@@ -382,7 +384,10 @@
//if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
if (ImGui::BeginMenu("Tools"))
{
+#ifndef IMGUI_DISABLE_METRICS_WINDOW
ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics);
+ ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool);
+#endif
ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor);
ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about);
ImGui::EndMenu();
diff --git a/imgui_internal.h b/imgui_internal.h
index 3f6c48c..1149da6 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -24,7 +24,7 @@
// [SECTION] Docking support
// [SECTION] Viewport support
// [SECTION] Settings support
-// [SECTION] Metrics, Debug
+// [SECTION] Metrics, Debug tools
// [SECTION] Generic context hooks
// [SECTION] ImGuiContext (main imgui context)
// [SECTION] ImGuiWindowTempData, ImGuiWindow
@@ -1382,11 +1382,12 @@
};
//-----------------------------------------------------------------------------
-// [SECTION] Metrics, Debug
+// [SECTION] Metrics, Debug Tools
//-----------------------------------------------------------------------------
struct ImGuiMetricsConfig
{
+ bool ShowStackTool;
bool ShowWindowsRects;
bool ShowWindowsBeginOrder;
bool ShowTablesRects;
@@ -1397,6 +1398,7 @@
ImGuiMetricsConfig()
{
+ ShowStackTool = false;
ShowWindowsRects = false;
ShowWindowsBeginOrder = false;
ShowTablesRects = false;
@@ -1407,6 +1409,27 @@
}
};
+struct ImGuiStackLevelInfo
+{
+ ImGuiID ID;
+ ImS8 QueryFrameCount; // >= 1: Query in progress
+ bool QuerySuccess; // Obtained result from DebugHookIdInfo()
+ char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?)
+
+ ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); }
+};
+
+// State for Stack tool queries
+struct ImGuiStackTool
+{
+ int LastActiveFrame;
+ int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level
+ ImGuiID QueryId; // ID to query details for
+ ImVector<ImGuiStackLevelInfo> Results;
+
+ ImGuiStackTool() { memset(this, 0, sizeof(*this)); }
+};
+
//-----------------------------------------------------------------------------
// [SECTION] Generic context hooks
//-----------------------------------------------------------------------------
@@ -1448,7 +1471,6 @@
bool WithinEndChild; // Set within EndChild()
bool GcCompactAll; // Request full GC
bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
- ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID()
void* TestEngine; // Test engine user data
// Windows state
@@ -1468,6 +1490,7 @@
float WheelingWindowTimer;
// Item/widgets state and tracking information
+ ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame;
bool HoveredIdAllowOverlap;
@@ -1671,8 +1694,9 @@
// Debug Tools
bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
- ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id
+ ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID
ImGuiMetricsConfig DebugMetricsConfig;
+ ImGuiStackTool DebugStackTool;
// Misc
float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds.
@@ -1697,7 +1721,6 @@
WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
GcCompactAll = false;
TestEngineHookItems = false;
- TestEngineHookIdInfo = 0;
TestEngine = NULL;
WindowsActiveCount = 0;
@@ -1708,6 +1731,7 @@
WheelingWindow = NULL;
WheelingWindowTimer = 0.0f;
+ DebugHookIdInfo = 0;
HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdAllowOverlap = false;
HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false;
@@ -2713,6 +2737,7 @@
inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
+ IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);
IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns);
IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label);
IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
@@ -2758,14 +2783,12 @@
#ifdef IMGUI_ENABLE_TEST_ENGINE
extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id);
extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
-extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id);
-extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end);
extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);
+extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);
+
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
#define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
-#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA));
-#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2));
#else
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0)
#endif
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index 945e739..f9ed5ce 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -564,6 +564,7 @@
// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables)
// + 1 (for table->RawData allocated below)
// + 1 (for table->ColumnsNames, if names are used)
+// Shared allocations per number of nested tables
// + 1 (for table->Splitter._Channels)
// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)
// Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details.
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 155ebcc..c4f1ceb 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1799,7 +1799,7 @@
bool value_changed = false;
for (int i = 0; i < items_count; i++)
{
- PushID((void*)(intptr_t)i);
+ PushID(i);
const bool item_selected = (i == *current_item);
const char* item_text;
if (!items_getter(data, i, &item_text))
@@ -6017,7 +6017,7 @@
ImGuiWindow* window = g.CurrentWindow;
Indent();
window->DC.TreeDepth++;
- window->IDStack.push_back(id);
+ PushOverrideID(id);
}
void ImGui::TreePop()