From 774ddb58bd5c4f2c5ce93b1ec447bcc2df6d3bec Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 13 Aug 2025 17:06:05 +0200 Subject: [PATCH] Fixed IsItemHovered() failing on disabled items and items that have no identifier. Made holding on disabled items not leak IsItemDisabled() between disabled items when window has _NoMove. (#8877, #8883) See amends to "widgets_status_common", "widgets_disabled_2" tests. --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 22 +++++++++++++++++++--- imgui.h | 2 +- imgui_internal.h | 1 + 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d1b421594..01fa71f57 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,11 @@ Breaking Changes: Other Changes: +- Fixed IsItemHovered() failing on disabled items and items that have no + identifier (e.g. Text() calls) when holding mouse button. (#8877, #8883) + [Regression in 1.92.2]. +- Made IsItemHovered() on holding mouse button down on disabled items not + leak between items when the window cannot be moved. - Backends: Allegro5: Fixed texture format setup which didn't work on all setups/drivers. (#8770, #8465) - Backends: Allegro5: Added ImGui_ImplAllegro5_SetDisplay() function to diff --git a/imgui.cpp b/imgui.cpp index 2e3086582..2705d8b70 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4063,6 +4063,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) ActiveIdClickOffset = ImVec2(-1, -1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; + ActiveIdDisabledId = 0; ActiveIdMouseButton = -1; ActiveIdPreviousFrame = 0; memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData)); @@ -4555,6 +4556,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdWindow = window; g.ActiveIdHasBeenEditedThisFrame = false; g.ActiveIdFromShortcut = false; + g.ActiveIdDisabledId = 0; if (id) { g.ActiveIdIsAlive = id; @@ -4656,6 +4658,7 @@ static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, } // This is roughly matching the behavior of internal-facing ItemHoverable() +// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { @@ -4697,7 +4700,17 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - return false; + { + // When ActiveId == MoveId it means that either: + // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag) + // - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). // FIXME: May be fixed. + // - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId. + bool cancel_is_hovered = true; + if (g.ActiveId == window->MoveId && (id == 0 || g.ActiveIdDisabledId == id)) + cancel_is_hovered = false; + if (cancel_is_hovered) + return false; + } // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. @@ -4778,7 +4791,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag if (!g.ActiveIdFromShortcut) return false; - // Done with rectangle culling so we can perform heavier checks now. + // We are done with rectangle culling so we can perform heavier checks now. if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdIsDisabled = true; @@ -5171,9 +5184,12 @@ void ImGui::UpdateMouseMovingWindowEndFrame() g.MovingWindow = NULL; // Cancel moving if clicked over an item which was disabled or inhibited by popups - // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)0 already) + // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item) if (g.HoveredIdIsDisabled) + { g.MovingWindow = NULL; + g.ActiveIdDisabledId = g.HoveredId; + } } else if (root_window == NULL && g.NavWindow != NULL) { diff --git a/imgui.h b/imgui.h index 08927acd6..6069dbdd6 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.3 WIP" -#define IMGUI_VERSION_NUM 19221 +#define IMGUI_VERSION_NUM 19222 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_internal.h b/imgui_internal.h index ec6be29d8..d7d17cd45 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2212,6 +2212,7 @@ struct ImGuiContext bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. bool ActiveIdHasBeenEditedThisFrame; bool ActiveIdFromShortcut; + ImGuiID ActiveIdDisabledId; // When clicking a disabled item we set ActiveId=window->MoveId to avoid interference with widget code. Actual item ID is stored here. int ActiveIdMouseButton : 8; ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow;