From 71e99b0d33dc898abc02a44a757cbf94983b02f6 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 27 Jul 2021 16:47:18 +0300 Subject: [PATCH] Popups: Fix popups being closed by newly appearing windows. (#4317) * Popups/modals now remain open when new windows are created from within popup/modal begin stack. * Modals are not closed when new window appears behind active modal. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 25 ++++++++++++++++++++++--- imgui_internal.h | 2 ++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 089d8e84a9e40..425b57672cbe7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -165,6 +165,7 @@ Other Changes: - Backends: OSX: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. (#4557, #4563) [@lfnoise] - Backends: All renderers: Normalize clipping rect handling across backends. (#4464) - Examples: Added SDL + SDL_Renderer example in "examples/example_sdl_sdlrenderer/" folder. (#3926) [@1bsyl] +- Popups: Windows created from within a popup/modal will no longer close that popup/modal. (#4317) [@rokups] ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 8b6613e058544..f95420cb3e835 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3896,7 +3896,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window, true)) + if (modal_window && g.HoveredWindow && !IsWindowChildOfBeginStack(g.HoveredWindow->RootWindow, modal_window)) clear_hovered_windows = true; // Disabled mouse? @@ -5874,7 +5874,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update ->RootWindow and others pointers (before any possible call to FocusWindow) if (first_begin_of_the_frame) + { UpdateWindowParentAndRootLinks(window, flags, parent_window); + window->ParentWindowInBeginStack = parent_window_in_stack; + } // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components @@ -6125,7 +6128,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) bool want_focus = false; if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) { - if (flags & ImGuiWindowFlags_Popup) + ImGuiWindow* modal = GetTopMostPopupModal(); + if (modal != NULL && !IsWindowChildOfBeginStack(window, modal)) + want_focus = false; + else if (flags & ImGuiWindowFlags_Popup) want_focus = true; else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; @@ -6764,6 +6770,19 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, return false; } +bool ImGui::IsWindowChildOfBeginStack(ImGuiWindow* window, ImGuiWindow* potential_parent) +{ + if (window->RootWindow == potential_parent) + return true; + while (window != NULL) + { + if (window == potential_parent) + return true; + window = window->ParentWindowInBeginStack; + } + return false; +} + bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; @@ -8436,7 +8455,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) - if (popup_window->RootWindow == ref_window->RootWindow) + if (IsWindowChildOfBeginStack(ref_window, popup_window)) { ref_window_is_descendent_of_popup = true; break; diff --git a/imgui_internal.h b/imgui_internal.h index 7b430dc4496e4..80ec86edf46e3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2026,6 +2026,7 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* ParentWindowInBeginStack; ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. @@ -2414,6 +2415,7 @@ namespace ImGui IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowChildOfBeginStack(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);