Popups: Fix an edge case where programatically closing a popup while clicking on its empty space would attempt to focus it and close other popups. (#2880)

This commit is contained in:
ocornut 2020-06-16 16:50:28 +02:00
parent 1dfd0634cb
commit d31fe97f74
3 changed files with 30 additions and 11 deletions

View File

@ -52,6 +52,8 @@ Other Changes:
Set to 0.0f (default) to always make a close button appear on hover (same as Chrome, VS). Set to 0.0f (default) to always make a close button appear on hover (same as Chrome, VS).
Set to FLT_MAX to only display a close button when selected (merely hovering is not enough). Set to FLT_MAX to only display a close button when selected (merely hovering is not enough).
Set to an intermediary value to toggle behavior based on width (same as Firefox). Set to an intermediary value to toggle behavior based on width (same as Firefox).
- Popups: Fix an edge case where programatically closing a popup while clicking on its empty space
would attempt to focus it and close other popups. (#2880)
- Fix GetGlyphRangesKorean() end-range to end at 0xD7A3 (instead of 0xD79D). (#348, #3217) [@marukrap] - Fix GetGlyphRangesKorean() end-range to end at 0xD7A3 (instead of 0xD79D). (#348, #3217) [@marukrap]
- Metrics: Added a "Settings" section with some details about persistent ini settings. - Metrics: Added a "Settings" section with some details about persistent ini settings.
- Nav, Menus: Fix vertical wrap-around in menus or popups created with multiple appending calls to - Nav, Menus: Fix vertical wrap-around in menus or popups created with multiple appending calls to

View File

@ -3449,17 +3449,22 @@ void ImGui::UpdateMouseMovingWindowEndFrame()
if (g.NavWindow && g.NavWindow->Appearing) if (g.NavWindow && g.NavWindow->Appearing)
return; return;
// Click to focus window and start moving (after we're done with all our widgets) // Click on empty space to focus window and start moving (after we're done with all our widgets)
if (g.IO.MouseClicked[0]) if (g.IO.MouseClicked[0])
{ {
if (g.HoveredRootWindow != NULL) // Handle the edge case of a popup being closed while clicking in its empty space.
// If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
ImGuiWindow* root_window = g.HoveredRootWindow;
const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpenAtAnyLevel(root_window->PopupId);
if (root_window != NULL && !is_closed_popup)
{ {
StartMouseMovingWindow(g.HoveredWindow); StartMouseMovingWindow(g.HoveredWindow);
if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
g.MovingWindow = NULL; g.MovingWindow = NULL;
} }
else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL) else if (root_window != NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
{ {
// Clicking on void disable focus // Clicking on void disable focus
FocusWindow(NULL); FocusWindow(NULL);
@ -3700,7 +3705,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
{ {
if (g.IO.MouseClicked[i]) if (g.IO.MouseClicked[i])
g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty()); g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
mouse_any_down |= g.IO.MouseDown[i]; mouse_any_down |= g.IO.MouseDown[i];
if (g.IO.MouseDown[i]) if (g.IO.MouseDown[i])
if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
@ -3718,7 +3723,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
if (g.WantCaptureMouseNextFrame != -1) if (g.WantCaptureMouseNextFrame != -1)
g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
else else
g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty()); g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
if (g.WantCaptureKeyboardNextFrame != -1) if (g.WantCaptureKeyboardNextFrame != -1)
@ -7610,6 +7615,15 @@ bool ImGui::IsPopupOpen(const char* str_id)
return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
} }
bool ImGui::IsPopupOpenAtAnyLevel(ImGuiID id)
{
ImGuiContext& g = *GImGui;
for (int n = 0; n < g.OpenPopupStack.Size; n++)
if (g.OpenPopupStack[n].PopupId == id)
return true;
return false;
}
ImGuiWindow* ImGui::GetTopMostPopupModal() ImGuiWindow* ImGui::GetTopMostPopupModal()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -7661,8 +7675,8 @@ void ImGui::OpenPopupEx(ImGuiID id)
else else
{ {
// Close child popups if any, then flag popup for open/reopen // Close child popups if any, then flag popup for open/reopen
g.OpenPopupStack.resize(current_stack_size + 1); ClosePopupToLevel(current_stack_size, false);
g.OpenPopupStack[current_stack_size] = popup_ref; g.OpenPopupStack.push_back(popup_ref);
} }
// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
@ -7675,7 +7689,7 @@ void ImGui::OpenPopupEx(ImGuiID id)
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.OpenPopupStack.empty()) if (g.OpenPopupStack.Size == 0)
return; return;
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
@ -7714,6 +7728,8 @@ void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
// Trim open popup stack
ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
g.OpenPopupStack.resize(remaining); g.OpenPopupStack.resize(remaining);

View File

@ -1581,7 +1581,7 @@ struct IMGUI_API ImGuiWindow
bool WantCollapseToggle; bool WantCollapseToggle;
bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed)
bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Appearing; // Set during the frame where the window is appearing (or re-appearing)
bool Hidden; // Do not display (== (HiddenFrames*** > 0)) bool Hidden; // Do not display (== HiddenFrames*** > 0)
bool IsFallbackWindow; // Set on the "Debug##Default" window. bool IsFallbackWindow; // Set on the "Debug##Default" window.
bool HasCloseButton; // Set when the window has a close button (p_open != NULL) bool HasCloseButton; // Set when the window has a close button (p_open != NULL)
signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3)
@ -1853,6 +1853,7 @@ namespace ImGui
IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id at current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack! IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id at current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack!
IMGUI_API bool IsPopupOpenAtAnyLevel(ImGuiID id);
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags);
IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImGuiWindow* GetTopMostPopupModal();