From 3fd1e8992d04257752fc7bd115c6ee5668ffe9fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Sep 2024 19:35:26 +0200 Subject: [PATCH] WIP V3 - simple and fast version of detecting duplicate id --- examples/example_win32_directx11/main.cpp | 12 ++++++++++++ imgui.cpp | 14 ++++++++++++++ imgui.h | 1 + imgui_demo.cpp | 2 ++ imgui_internal.h | 4 ++++ imgui_widgets.cpp | 3 +++ 6 files changed, 36 insertions(+) diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 5285df102..7206468a1 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -131,6 +131,8 @@ int main(int, char**) { static float f = 0.0f; static int counter = 0; + static int counter2 = 0; + static int counter3 = 0; ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. @@ -146,6 +148,16 @@ int main(int, char**) ImGui::SameLine(); ImGui::Text("counter = %d", counter); + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter2++; + ImGui::SameLine(); + ImGui::Text("counter2 = %d", counter2); + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter3++; + ImGui::SameLine(); + ImGui::Text("counter3 = %d", counter3); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::End(); } diff --git a/imgui.cpp b/imgui.cpp index 1d9d8b899..2e8de6811 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4290,6 +4290,18 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + + // Detect ID conflicts +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) + g.HoveredIdPreviousFrameItemCount++; + if (id != 0 && g.DebugDuplicateId == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) + { + SetTooltip("Duplicate ID detected!"); + GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); + } +#endif + if (g.HoveredWindow != window) return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) @@ -4834,6 +4846,7 @@ void ImGui::NewFrame() KeepAliveID(g.DragDropPayload.SourceId); // Update HoveredId data + g.DebugDuplicateId = (g.HoveredIdPreviousFrameItemCount > 1) ? g.HoveredIdPreviousFrame : 0; if (!g.HoveredIdPreviousFrame) g.HoveredIdTimer = 0.0f; if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) @@ -4843,6 +4856,7 @@ void ImGui::NewFrame() if (g.HoveredId && g.ActiveId != g.HoveredId) g.HoveredIdNotActiveTimer += g.IO.DeltaTime; g.HoveredIdPreviousFrame = g.HoveredId; + g.HoveredIdPreviousFrameItemCount = 0; g.HoveredId = 0; g.HoveredIdAllowOverlap = false; g.HoveredIdIsDisabled = false; diff --git a/imgui.h b/imgui.h index 18efaff42..a45946d6f 100644 --- a/imgui.h +++ b/imgui.h @@ -1135,6 +1135,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. + ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip. }; // Flags for ImGui::InputText() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index aabb220d6..258bab61d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7148,12 +7148,14 @@ static void ShowDemoWindowColumns() { if (h_borders && ImGui::GetColumnIndex() == 0) ImGui::Separator(); + ImGui::PushID(i); ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); ImGui::Text("Long text that is likely to clip"); ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); + ImGui::PopID(); ImGui::NextColumn(); } ImGui::Columns(1); diff --git a/imgui_internal.h b/imgui_internal.h index 1aa94ff3b..f14f44fb7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2042,9 +2042,11 @@ struct ImGuiContext ImVec2 WheelingAxisAvg; // Item/widgets state and tracking information + ImGuiID DebugDuplicateId; // Set when we detect multiple items with the same identifier ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; + int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id float HoveredIdTimer; // Measure contiguous hovering time float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active bool HoveredIdAllowOverlap; @@ -2365,8 +2367,10 @@ struct ImGuiContext WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; WheelingWindowReleaseTimer = 0.0f; + DebugDuplicateId = 0; DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; + HoveredIdPreviousFrameItemCount = 0; HoveredIdAllowOverlap = false; HoveredIdIsDisabled = false; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 10cdf3428..33af0686e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3551,6 +3551,8 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) // Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets) // FIXME: Facilitate using this in variety of other situations. +// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but +// the expected relationship between TempInputXXX functions and LastItemData is a little fishy. bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) { // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. @@ -3561,6 +3563,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ClearActiveID(); g.CurrentWindow->DC.CursorPos = bb.Min; + g.LastItemData.InFlags |= ImGuiItemFlags_AllowDuplicateId; bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); if (init) { -- 2.42.0.windows.2