From f862be953ea3f2b534a6f5b9e5e0d10d7975d92c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Sep 2024 19:35:26 +0200 Subject: [PATCH 1/2] WIP simple version of detecting duplicate id --- examples/example_win32_directx11/main.cpp | 12 ++++++++++++ imgui.cpp | 13 +++++++++++++ imgui.h | 1 + imgui_internal.h | 2 ++ imgui_widgets.cpp | 1 + 5 files changed, 29 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..78eefbd3e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4113,6 +4113,7 @@ void ImGui::SetHoveredID(ImGuiID id) ImGuiContext& g = *GImGui; g.HoveredId = id; g.HoveredIdAllowOverlap = false; + g.HoveredIdRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); if (id != 0 && g.HoveredIdPreviousFrame != id) g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; } @@ -4290,6 +4291,15 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + + // Detect ID conflicts + if (g.HoveredId == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) + { + SetTooltip("Duplicate ID detected!"); + GetForegroundDrawList()->AddRect(g.HoveredIdRect.Min, g.HoveredIdRect.Max, IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); + GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); + } + if (g.HoveredWindow != window) return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) @@ -4317,6 +4327,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag return false; SetHoveredID(id); + g.HoveredIdRect = bb; // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test. @@ -4843,8 +4854,10 @@ void ImGui::NewFrame() if (g.HoveredId && g.ActiveId != g.HoveredId) g.HoveredIdNotActiveTimer += g.IO.DeltaTime; g.HoveredIdPreviousFrame = g.HoveredId; + g.HoveredIdPreviousFrameRect = g.HoveredIdRect; g.HoveredId = 0; g.HoveredIdAllowOverlap = false; + g.HoveredIdRect = ImRect(); g.HoveredIdIsDisabled = false; // Clear ActiveID if the item is not alive anymore. 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_internal.h b/imgui_internal.h index 1aa94ff3b..7a60d4d7f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2049,6 +2049,8 @@ struct ImGuiContext float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active bool HoveredIdAllowOverlap; bool HoveredIdIsDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. + ImRect HoveredIdRect; + ImRect HoveredIdPreviousFrameRect; bool ItemUnclipByLog; // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled ImGuiID ActiveId; // Active widget ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 10cdf3428..29fecc602 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -526,6 +526,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { hovered = true; SetHoveredID(id); + g.HoveredIdRect = bb; if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) { pressed = true; -- 2.42.0.windows.2 From 5edfa38f93d074e96afe5ca4502918df2fcf9910 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Sep 2024 19:50:00 +0200 Subject: [PATCH 2/2] WIP amends/fixes --- imgui.cpp | 2 +- imgui_demo.cpp | 2 ++ imgui_widgets.cpp | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 78eefbd3e..9dd4f85c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4293,7 +4293,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag ImGuiWindow* window = g.CurrentWindow; // Detect ID conflicts - if (g.HoveredId == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) + if (g.HoveredId == id && id != 0 && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0) { SetTooltip("Duplicate ID detected!"); GetForegroundDrawList()->AddRect(g.HoveredIdRect.Min, g.HoveredIdRect.Max, IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); 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_widgets.cpp b/imgui_widgets.cpp index 29fecc602..6812393ec 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3552,6 +3552,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. @@ -3562,6 +3564,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