diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 46474277ae25..a3176ed50fc7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -40,7 +40,12 @@ Breaking changes: As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialisation, we are exceptionally accepting the breakage. Majority of users should not even notice. -- Overlapping items: (#6512, #3909) +- Overlapping items: (#6512, #3909, #517) + - Added 'SetNextItemAllowOverlap()' (called before an item) as a replacement for using + 'SetItemAllowOverlap()' (called after an item). This is roughly equivalent to using the + legacy 'SetItemAllowOverlap()' call (public API) + ImGuiButtonFlags_AllowOverlap (internal). + - Obsoleted 'SetItemAllowOverlap()': it didn't and couldn't work reliably since 1.89 (2022-11-15), + and relied on ambiguously defined design. Use 'SetNextItemAllowOverlap()' before item instead. - Renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap'. - Renamed 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap' - Kept redirecting enums (will obsolete). diff --git a/imgui.cpp b/imgui.cpp index 66fd7e80766c..f18ddbe7c82f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -397,6 +397,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage. - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3. @@ -5284,17 +5285,28 @@ bool ImGui::IsItemEdited() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } +// Allow next item to be overlapped by subsequent items. +// This works by requiring HoveredId to match for two subsequent frames, +// so if a following items overwrite it our interactions will naturally be disabled. +void ImGui::SetNextItemAllowOverlap() +{ + ImGuiContext& g = *GImGui; + g.NextItemData.ItemFlags |= ImGuiItemflags_AllowOverlap; +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowOverlap flag are extremely confusing, need rework. +// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead. void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdAllowOverlap = true; - if (g.ActiveId == id) + if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id. g.ActiveIdAllowOverlap = true; } +#endif // FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function. void ImGui::SetActiveIdUsingAllKeyboardKeys() diff --git a/imgui.h b/imgui.h index 8b31175477f3..2af5a2a94913 100644 --- a/imgui.h +++ b/imgui.h @@ -23,7 +23,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.89.7 WIP" -#define IMGUI_VERSION_NUM 18966 +#define IMGUI_VERSION_NUM 18967 #define IMGUI_HAS_TABLE /* @@ -839,6 +839,9 @@ namespace ImGui IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. + // Overlapping mode + IMGUI_API void SetNextItemAllowOverlap(); // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this. + // Item/Widgets Utilities and Query Functions // - Most of the functions are referring to the previous Item that has been submitted. // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. @@ -859,7 +862,6 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item - IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -3072,6 +3074,8 @@ namespace ImGui #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.89.7 (from June 2023) + IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. // OBSOLETED in 1.89.4 (from March 2023) static inline void PushAllowKeyboardFocus(bool tab_stop) { PushTabStop(tab_stop); } static inline void PopAllowKeyboardFocus() { PopTabStop(); } diff --git a/imgui_internal.h b/imgui_internal.h index b895d983fa80..79aeff1cdbcc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -802,6 +802,7 @@ enum ImGuiItemFlags_ ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable() + ImGuiItemflags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. @@ -861,7 +862,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping - ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() + ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable. ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine @@ -1179,7 +1180,7 @@ enum ImGuiNextItemDataFlags_ struct ImGuiNextItemData { ImGuiNextItemDataFlags Flags; - ImGuiItemFlags ItemFlags; + ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemflags_AllowOverlap. float Width; // Set by SetNextItemWidth() ImGuiID FocusScopeId; // Set by SetNextItemMultiSelectData() (!= 0 signify value has been set, so it's an alternate version of HasSelectionData, we don't use Flags for this because they are cleared too early. This is mostly used for debugging) ImGuiCond OpenCond; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index ec149b3ad8fd..e4c5269c632f 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2962,8 +2962,6 @@ void ImGui::TableHeader(const char* label) // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); if (held || hovered || selected) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0eca30153c3c..ba5d89830b48 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -496,6 +496,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); if (item_flags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + if (item_flags & ImGuiItemflags_AllowOverlap) + flags |= ImGuiButtonFlags_AllowOverlap; ImGuiWindow* backup_hovered_window = g.HoveredWindow; const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; @@ -529,8 +531,13 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.HoveredWindow = backup_hovered_window; // 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. - if (hovered && (flags & ImGuiButtonFlags_AllowOverlap) && g.HoveredIdPreviousFrame != id) - hovered = false; + if (flags & ImGuiButtonFlags_AllowOverlap) + { + if (hovered && g.HoveredIdPreviousFrame != id) + hovered = false; + if (g.HoveredId == id) // FIXME: Added this to match legacy SetItemAllowOverlap(). Investigate precise side-effects of using (hovered==true) instead? + g.HoveredIdAllowOverlap = true; + } // Mouse handling const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; @@ -1546,14 +1553,20 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) return false; + // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is + // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item. + // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item. + ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + button_flags |= ImGuiButtonFlags_AllowOverlap; +#endif + bool hovered, held; ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowOverlap); + ButtonBehavior(bb_interact, id, &hovered, &held, button_flags); if (hovered) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb - if (g.ActiveId != id) // Because: we don't want to hover other while Active - SetItemAllowOverlap(); if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); @@ -6158,7 +6171,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l } ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; - if (flags & ImGuiTreeNodeFlags_AllowOverlap) + if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemflags_AllowOverlap)) button_flags |= ImGuiButtonFlags_AllowOverlap; if (!is_leaf) button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; @@ -6232,8 +6245,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } - if ((flags & ImGuiTreeNodeFlags_AllowOverlap) && g.ActiveId != id) // Because: we don't want to hover other while Active - SetItemAllowOverlap(); // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 @@ -6490,7 +6501,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } - if (flags & ImGuiSelectableFlags_AllowOverlap) { button_flags |= ImGuiButtonFlags_AllowOverlap; } + if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemflags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } const bool was_selected = selected; bool hovered, held; @@ -6519,9 +6530,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (pressed) MarkItemEdited(id); - if ((flags & ImGuiSelectableFlags_AllowOverlap) && g.ActiveId != id) // Because: we don't want to hover other while Active - SetItemAllowOverlap(); - // In this branch, Selectable() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; @@ -8390,6 +8398,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Click to Select a tab + // Allow the close button to overlap ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); if (g.DragDropActive) button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; @@ -8398,10 +8407,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (pressed && !is_tab_button) TabBarQueueFocus(tab_bar, tab); - // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs or collapse/close button to be hovered) - if (g.ActiveId != id) // Because: we don't want to hover other items while dragging active) - SetItemAllowOverlap(); - // Drag and drop: re-order tabs if (held && !tab_appearing && IsMouseDragging(0)) {