diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e6cf69ebe..3167c5561 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -61,6 +61,8 @@ Other Changes: - Style: Added style.ColorButtonButton (left/right, defaults to ImGuiDir_Right) to move the color button of ColorEdit3/ColorEdit4 functions to either side of the inputs. - Misc: Added IMGUI_DISABLE_METRICS_WINDOW imconfig.h setting to explicitly compile out ShowMetricsWindow(). +- Debug, Metrics: Added "Tools->Item Picker" tool which allow clicking on a widget to break in the debugger + within the item code. The tool calls IM_DEBUG_BREAK() which can be redefined in imconfig.h if needed. - ImDrawList: Fixed CloneOutput() helper crashing. (#1860) [@gviot] - ImDrawList::ChannelsSplit(), ImDrawListSplitter: Fixed an issue with merging draw commands between channel 0 and 1. (#2624) diff --git a/imconfig.h b/imconfig.h index 5d9caecd0..16f8c8279 100644 --- a/imconfig.h +++ b/imconfig.h @@ -76,6 +76,11 @@ //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); //#define ImDrawCallback MyImDrawCallback +//---- Debug Tools +// Use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging. +//#define IM_DEBUG_BREAK IM_ASSERT(0) +//#define IM_DEBUG_BREAK __debugbreak() + //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. /* namespace ImGui diff --git a/imgui.cpp b/imgui.cpp index 47dc25df7..bde77d2db 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2862,6 +2862,16 @@ void ImGui::SetHoveredID(ImGuiID id) g.HoveredIdAllowOverlap = false; if (id != 0 && g.HoveredIdPreviousFrame != id) g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; + + // [DEBUG] Item Picker tool! + // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making + // the cost of this tool near-zero. We would get slightly better call-stack if we made the test in ItemAdd() + // but that would incur a slightly higher cost and may require us to hide this feature behind a define. + if (id != 0 && id == g.DebugBreakItemId) + { + IM_DEBUG_BREAK(); + g.DebugBreakItemId = 0; + } } ImGuiID ImGui::GetHoveredID() @@ -10180,6 +10190,28 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode("Tools")) { + static bool picking_enabled = false; + if (ImGui::Button("Item Picker..")) + picking_enabled = true; + if (picking_enabled) + { + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + picking_enabled = false; + if (ImGui::IsMouseClicked(0) && hovered_id) + { + g.DebugBreakItemId = hovered_id; + picking_enabled = false; + } + ImGui::SetNextWindowBgAlpha(0.5f); + ImGui::BeginTooltip(); + ImGui::Text("HoveredId: 0x%08X", hovered_id); + ImGui::Text("Press ESC to abort picking."); + ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + ImGui::EndTooltip(); + } + ImGui::Checkbox("Show windows begin order", &show_windows_begin_order); ImGui::Checkbox("Show windows rectangles", &show_windows_rects); ImGui::SameLine(); @@ -10225,10 +10257,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) } ImGui::End(); } + #else -void ImGui::ShowMetricsWindow(bool*) -{ -} + +void ImGui::ShowMetricsWindow(bool*) { } + #endif //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index cb5c40864..d615a9420 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -401,7 +401,7 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_Deactivated = 1 << 5 // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. #ifdef IMGUI_ENABLE_TEST_ENGINE - , // [imgui-test only] + , // [imgui_tests only] ImGuiItemStatusFlags_Openable = 1 << 10, // ImGuiItemStatusFlags_Opened = 1 << 11, // ImGuiItemStatusFlags_Checkable = 1 << 12, // @@ -1028,6 +1028,9 @@ struct ImGuiContext int LogDepthToExpand; int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. + // Debug Tools + ImGuiID DebugBreakItemId; + // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; @@ -1153,6 +1156,8 @@ struct ImGuiContext LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; + DebugBreakItemId = 0; + memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = 0; FramerateSecPerFrameAccum = 0.0f; @@ -1662,7 +1667,19 @@ IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); -// Test engine hooks (imgui-test) +// Debug Tools +// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item. +#ifndef IM_DEBUG_BREAK +#if defined(__clang__) +#define IM_DEBUG_BREAK() __builtin_debugtrap() +#elif defined (_MSC_VER) +#define IM_DEBUG_BREAK() __debugbreak() +#else +#define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! +#endif +#endif // #ifndef IM_DEBUG_BREAK + +// Test Engine Hooks (imgui_tests) //#define IMGUI_ENABLE_TEST_ENGINE #ifdef IMGUI_ENABLE_TEST_ENGINE extern void ImGuiTestEngineHook_PreNewFrame(ImGuiContext* ctx);