diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9b3032332..b8419caa4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,6 +50,8 @@ Other Changes: (It does not provide the docking/splitting/merging of windows available in the Docking branch) - Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, as a convenience to avoid using the ### operator. +- Window, Focus, Popup: Fixed an issue where closing a popup by clicking another window with the _NoMove flag would refocus + the parent window of the popup instead of the newly clicked window. - Window: Contents size is preserved while a window collapsed. Fix auto-resizing window losing their size for one frame when uncollapsed. - Window: Contents size is preserved while a window contents is hidden (unless it is hidden for resizing purpose). - Window: Resizing windows from edge is now enabled by default (io.ConfigWindowsResizeFromEdges=true). Note that diff --git a/imgui.cpp b/imgui.cpp index 637bc5cd0..caa852650 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6545,19 +6545,31 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below { //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); - ClosePopupToLevel(popup_count_to_keep); + ClosePopupToLevel(popup_count_to_keep, false); } } -void ImGui::ClosePopupToLevel(int remaining) +void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) { IM_ASSERT(remaining >= 0); ImGuiContext& g = *GImGui; ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; - if (g.NavLayer == 0) - focus_window = NavRestoreLastChildNavWindow(focus_window); - FocusWindow(focus_window); g.OpenPopupStack.resize(remaining); + + // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely. + // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing. + // The current code will set focus to the parent of the popup window which is incorrect. + // It rarely manifested until now because UpdateMouseMovingWindow() would call FocusWindow() again on the clicked window, + // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again. + // However if the clicked window has the _NoMove flag set we would be left with B focused. + // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway, + // but we should inspect and fix this properly. + if (apply_focus_to_window_under) + { + if (g.NavLayer == 0) + focus_window = NavRestoreLastChildNavWindow(focus_window); + FocusWindow(focus_window); + } } // Close the popup we have begin-ed into. @@ -6569,7 +6581,7 @@ void ImGui::CloseCurrentPopup() return; while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) popup_idx--; - ClosePopupToLevel(popup_idx); + ClosePopupToLevel(popup_idx, true); // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. @@ -6636,7 +6648,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla { EndPopup(); if (is_open) - ClosePopupToLevel(g.BeginPopupStack.Size); + ClosePopupToLevel(g.BeginPopupStack.Size, true); return false; } return is_open; diff --git a/imgui_internal.h b/imgui_internal.h index 53408f12f..60704564f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1297,7 +1297,7 @@ namespace ImGui // Popups, Modals, Tooltips IMGUI_API void OpenPopupEx(ImGuiID id); - IMGUI_API void ClosePopupToLevel(int remaining); + IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack! IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 367c4df9f..367b9bb61 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5657,7 +5657,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' want_close = true; if (want_close && IsPopupOpen(id)) - ClosePopupToLevel(g.BeginPopupStack.Size); + ClosePopupToLevel(g.BeginPopupStack.Size, true); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); @@ -5694,7 +5694,7 @@ void ImGui::EndMenu() ImGuiWindow* window = g.CurrentWindow; if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) { - ClosePopupToLevel(g.BeginPopupStack.Size); + ClosePopupToLevel(g.BeginPopupStack.Size, true); NavMoveRequestCancel(); }