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.
Tested by "window_popup_interruptions"
This commit is contained in:
Rokas Kupstys 2021-07-27 16:47:18 +03:00 committed by ocornut
parent 657073a650
commit b38af0f522
4 changed files with 119 additions and 5 deletions

View File

@ -45,6 +45,10 @@ Other Changes:
(so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2,
but it allows testing for triple clicks and more). but it allows testing for triple clicks and more).
- Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527)
- Modals, Popups, Windows: changes how appearing windows are interrupting popups and modals. (#4317) [@rokups]
- appearing windows created from within the begin stack of a popup/modal will no longer close it.
- appearing windows created not within the begin stack of a modal will no longer close the modal,
and automatically appear behind it.
- Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676)
- Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard - Nav: Ctrl+tabbing to cycle through windows is now enabled regardless of using the _NavEnableKeyboard
configuration flag. This is part of an effort to generalize the use of keyboard inputs. (#4023, #787). configuration flag. This is part of an effort to generalize the use of keyboard inputs. (#4023, #787).

114
imgui.cpp
View File

@ -948,6 +948,7 @@ static void RenderWindowDecorations(ImGuiWindow* window, const ImRec
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
static void RenderDimmedBackgrounds(); static void RenderDimmedBackgrounds();
static ImGuiWindow* FindBlockingModal(ImGuiWindow* window);
// Viewports // Viewports
static void UpdateViewportsNewFrame(); static void UpdateViewportsNewFrame();
@ -3910,7 +3911,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// Modal windows prevents mouse from hovering behind them. // Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal(); ImGuiWindow* modal_window = GetTopMostPopupModal();
if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window, true)) if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))
clear_hovered_windows = true; clear_hovered_windows = true;
// Disabled mouse? // Disabled mouse?
@ -4466,6 +4467,23 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32
} }
} }
ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* bottom_most_visible_window = parent_window;
for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
{
ImGuiWindow* window = g.Windows[i];
if (window->Flags & ImGuiWindowFlags_ChildWindow)
continue;
if (!IsWindowWithinBeginStackOf(window, parent_window))
break;
if (IsWindowActiveAndVisible(window))
bottom_most_visible_window = window;
}
return bottom_most_visible_window;
}
static void ImGui::RenderDimmedBackgrounds() static void ImGui::RenderDimmedBackgrounds()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -4477,8 +4495,9 @@ static void ImGui::RenderDimmedBackgrounds()
if (dim_bg_for_modal) if (dim_bg_for_modal)
{ {
// Draw dimming behind modal // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
RenderDimmedBackgroundBehindWindow(modal_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio));
} }
else if (dim_bg_for_window_list) else if (dim_bg_for_window_list)
{ {
@ -5859,6 +5878,36 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags
} }
} }
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
// - Window // FindBlockingModal() returns Modal1
// - Window // .. returns Modal1
// - Modal1 // .. returns Modal2
// - Window // .. returns Modal2
// - Window // .. returns Modal2
// - Modal2 // .. returns Modal2
static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
if (g.OpenPopupStack.Size <= 0)
return NULL;
// Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)
{
ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window;
if (popup_window == NULL || !popup_window->WasActive || !(popup_window->Flags & ImGuiWindowFlags_Modal)) // Check WasActive, because this code may run before popup renders on current frame.
continue;
if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed.
break;
for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow)
if (IsWindowWithinBeginStackOf(window, parent))
return popup_window; // Place window above its begin stack parent.
}
return NULL;
}
// Push a new Dear ImGui window to add widgets to. // Push a new Dear ImGui window to add widgets to.
// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
// - Begin/End can be called multiple times during the frame with the same window name to append content. // - Begin/End can be called multiple times during the frame with the same window name to append content.
@ -6204,6 +6253,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
want_focus = true; want_focus = true;
else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
want_focus = true; want_focus = true;
ImGuiWindow* modal = GetTopMostPopupModal();
if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal))
{
// Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed.
// Since window is not focused it would reappear at the same display position like the last time it was visible.
// In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal.
// Position window behind a modal that is not a begin-parent of this window.
want_focus = false;
if (window == window->RootWindow)
{
ImGuiWindow* blocking_modal = FindBlockingModal(window);
IM_ASSERT(blocking_modal != NULL);
BringWindowToDisplayBehind(window, blocking_modal);
}
}
} }
// Handle manual resize: Resize Grips, Borders, Gamepad // Handle manual resize: Resize Grips, Borders, Gamepad
@ -6581,6 +6646,34 @@ void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
} }
} }
void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
{
IM_ASSERT(window != NULL && behind_window != NULL);
ImGuiContext& g = *GImGui;
window = window->RootWindow;
behind_window = behind_window->RootWindow;
int pos_wnd = FindWindowDisplayIndex(window);
int pos_beh = FindWindowDisplayIndex(behind_window);
if (pos_wnd < pos_beh)
{
size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
g.Windows[pos_beh - 1] = window;
}
else
{
size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
g.Windows[pos_beh] = window;
}
}
int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
return g.Windows.index_from_ptr(g.Windows.find(window));
}
// Moving window to front of display and set focus (which happens to be back of our sorted list) // Moving window to front of display and set focus (which happens to be back of our sorted list)
void ImGui::FocusWindow(ImGuiWindow* window) void ImGui::FocusWindow(ImGuiWindow* window)
{ {
@ -6810,6 +6903,19 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent,
return false; return false;
} }
bool ImGui::IsWindowWithinBeginStackOf(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) bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -8492,7 +8598,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to
bool ref_window_is_descendent_of_popup = false; bool ref_window_is_descendent_of_popup = false;
for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
if (popup_window->RootWindow == ref_window->RootWindow) if (IsWindowWithinBeginStackOf(ref_window, popup_window))
{ {
ref_window_is_descendent_of_popup = true; ref_window_is_descendent_of_popup = true;
break; break;

View File

@ -64,7 +64,7 @@ Index of this file:
// Version // Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.86 WIP" #define IMGUI_VERSION "1.86 WIP"
#define IMGUI_VERSION_NUM 18516 #define IMGUI_VERSION_NUM 18517
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE

View File

@ -2418,6 +2418,7 @@ namespace ImGui
IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy);
IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
@ -2433,6 +2434,9 @@ namespace ImGui
IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window);
IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window);
IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window);
IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window);
IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window);
// Fonts, drawing // Fonts, drawing
IMGUI_API void SetCurrentFont(ImFont* font); IMGUI_API void SetCurrentFont(ImFont* font);