From f953ebf9ca15bbe1477d4cef499009faa8110814 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 May 2024 18:13:25 +0200 Subject: [PATCH] Disabled: nested tooltips or other non-child window within a BeginDisabled() block disable the disabled state. (#211, #7640) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- imgui.h | 2 +- imgui_internal.h | 3 +++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 154ae8ff8..3b4ff8238 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,8 @@ Other changes: grab scrolls by one page, holding mouse button repeats scrolling. (#7328, #150) - Combo: simplified Combo() API uses a list clipper (due to its api it wasn't previously trivial before we added clipper.IncludeItemByIndex() function). +- Disabled: nested tooltips or other non-child window within a BeginDisabled() + block disable the disabled state. (#211, #7640) - Misc: made ImGuiDir and ImGuiSortDirection stronger-typed enums. diff --git a/imgui.cpp b/imgui.cpp index a917148e8..cbcf8278a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6648,6 +6648,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // [EXPERIMENTAL] Skip Refresh mode UpdateWindowSkipRefresh(window); + // Nested root windows (typically tooltips) override disabled state + if (window->RootWindow == window) + if ((window->DC.BackupItemDisabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0)) + BeginDisabledOverrideReenable(); + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindow = NULL; @@ -7259,6 +7264,8 @@ void ImGui::End() if (!window->SkipRefresh) PopClipRect(); // Inner window clip rectangle PopFocusScope(); + if (window->RootWindow == window && window->DC.BackupItemDisabled) + EndDisabledOverrideReenable(); if (window->SkipRefresh) { @@ -7523,7 +7530,7 @@ void ImGui::BeginDisabled(bool disabled) } if (was_disabled || disabled) g.CurrentItemFlags |= ImGuiItemFlags_Disabled; - g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize? g.DisabledStackSize++; } @@ -7540,6 +7547,29 @@ void ImGui::EndDisabled() g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } +// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name. +// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal. +// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled(). +void ImGui::BeginDisabledOverrideReenable() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled); + g.Style.Alpha = g.DisabledAlphaBackup; + g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled; + g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; +} + +void ImGui::EndDisabledOverrideReenable() +{ + ImGuiContext& g = *GImGui; + g.DisabledStackSize--; + IM_ASSERT(g.DisabledStackSize > 0); + g.ItemFlagsStack.pop_back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); + g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha; +} + void ImGui::PushTabStop(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); @@ -9995,7 +10025,13 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); - EndDisabled(); + if (g.CurrentItemFlags & ImGuiItemFlags_Disabled) + EndDisabled(); + else + { + EndDisabledOverrideReenable(); + window->DC.BackupItemDisabled = false; + } } while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) { diff --git a/imgui.h b/imgui.h index aef2bf22c..689604ba4 100644 --- a/imgui.h +++ b/imgui.h @@ -28,7 +28,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.90.8 WIP" -#define IMGUI_VERSION_NUM 19072 +#define IMGUI_VERSION_NUM 19073 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index 2a31473c5..228a0f7e2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2472,6 +2472,7 @@ struct IMGUI_API ImGuiWindowTempData bool NavWindowHasScrollY; // Set per window when scrolling can be used (== ScrollMax.y > 0.0f) // Miscellaneous + bool BackupItemDisabled; // Non-child window override disabled flag bool MenuBarAppending; // FIXME: Remove this ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items measurement @@ -3127,6 +3128,8 @@ namespace ImGui IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx); + IMGUI_API void BeginDisabledOverrideReenable(); + IMGUI_API void EndDisabledOverrideReenable(); // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.