Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus properly after the main menu bar or last focused window is deactivated.
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 184a4b9..1d7d3e4 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -39,6 +39,8 @@
of the supplied context pointer. (#2066)
- Fixed calling SetNextWindowSize()/SetWindowSize() with non-integer values leading to
accidental alteration of window position. We now round the provided size. (#2067)
+- Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus
+ properly after the main menu bar or last focused window is deactivated.
-----------------------------------------------------------------------
diff --git a/imgui.cpp b/imgui.cpp
index a2dbc4d..ffe5b40 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -906,7 +906,7 @@
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
-static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
+static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
static ImRect GetViewportRect();
@@ -3225,6 +3225,7 @@
g.NavIdTabCounter = INT_MAX;
// Mark all windows as not visible
+ IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
for (int i = 0; i != g.Windows.Size; i++)
{
ImGuiWindow* window = g.Windows[i];
@@ -3235,7 +3236,7 @@
// Closing the focused window restore focus to the first active root window in descending z-order
if (g.NavWindow && !g.NavWindow->WasActive)
- FocusFrontMostActiveWindowIgnoringOne(NULL);
+ FocusPreviousWindowIgnoringOne(NULL);
// No window should be open at the beginning of the frame.
// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
@@ -3292,6 +3293,7 @@
for (int i = 0; i < g.Windows.Size; i++)
IM_DELETE(g.Windows[i]);
g.Windows.clear();
+ g.WindowsFocusOrder.clear();
g.WindowsSortBuffer.clear();
g.CurrentWindow = NULL;
g.CurrentWindowStack.clear();
@@ -3340,7 +3342,7 @@
return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
}
-static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
+static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
{
out_sorted_windows->push_back(window);
if (window->Active)
@@ -3352,7 +3354,7 @@
{
ImGuiWindow* child = window->DC.ChildWindows[i];
if (child->Active)
- AddWindowToSortedBuffer(out_sorted_windows, child);
+ AddWindowToSortBuffer(out_sorted_windows, child);
}
}
}
@@ -3550,7 +3552,7 @@
ImGuiWindow* window = g.Windows[i];
if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
continue;
- AddWindowToSortedBuffer(&g.WindowsSortBuffer, window);
+ AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
}
IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
@@ -4204,8 +4206,9 @@
window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
}
+ g.WindowsFocusOrder.push_back(window);
if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
- g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
+ g.Windows.push_front(window); // Quite slow but rare and only once
else
g.Windows.push_back(window);
return window;
@@ -5125,7 +5128,21 @@
SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
}
-void ImGui::BringWindowToFront(ImGuiWindow* window)
+void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ if (g.WindowsFocusOrder.back() == window)
+ return;
+ for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
+ if (g.WindowsFocusOrder[i] == window)
+ {
+ memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
+ g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
+ break;
+ }
+}
+
+void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* current_front_window = g.Windows.back();
@@ -5134,13 +5151,13 @@
for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
if (g.Windows[i] == window)
{
- g.Windows.erase(g.Windows.Data + i);
- g.Windows.push_back(window);
+ memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
+ g.Windows[g.Windows.Size - 1] = window;
break;
}
}
-void ImGui::BringWindowToBack(ImGuiWindow* window)
+void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
if (g.Windows[0] == window)
@@ -5185,20 +5202,24 @@
ClearActiveID();
// Bring to front
+ BringWindowToFocusFront(window);
if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
- BringWindowToFront(window);
+ BringWindowToDisplayFront(window);
}
-void ImGui::FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window)
+void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
{
ImGuiContext& g = *GImGui;
- for (int i = g.Windows.Size - 1; i >= 0; i--)
- if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
+ for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
+ {
+ ImGuiWindow* window = g.WindowsFocusOrder[i];
+ if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
{
- ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]);
+ ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
FocusWindow(focus_window);
return;
}
+ }
}
void ImGui::PushItemWidth(float item_width)
@@ -7436,11 +7457,11 @@
return 0.0f;
}
-static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N)
+static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
{
ImGuiContext& g = *GImGui;
- for (int i = g.Windows.Size-1; i >= 0; i--)
- if (g.Windows[i] == window)
+ for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
+ if (g.WindowsFocusOrder[i] == window)
return i;
return -1;
}
@@ -7448,9 +7469,9 @@
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
{
ImGuiContext& g = *GImGui;
- for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir)
- if (ImGui::IsWindowNavFocusable(g.Windows[i]))
- return g.Windows[i];
+ for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
+ if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
+ return g.WindowsFocusOrder[i];
return NULL;
}
@@ -7461,10 +7482,10 @@
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
return;
- const int i_current = FindWindowIndex(g.NavWindowingTarget);
+ const int i_current = FindWindowFocusIndex(g.NavWindowingTarget);
ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
if (!window_target)
- window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
+ window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
if (window_target) // Don't reset windowing target if there's a single window in the list
g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
g.NavWindowingToggleLayer = false;
@@ -7496,7 +7517,7 @@
bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
- if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1))
+ if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
{
g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
@@ -7628,9 +7649,9 @@
SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
- for (int n = g.Windows.Size - 1; n >= 0; n--)
+ for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
{
- ImGuiWindow* window = g.Windows[n];
+ ImGuiWindow* window = g.WindowsFocusOrder[n];
if (!IsWindowNavFocusable(window))
continue;
const char* label = window->Name;
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 2cce0dd..c5f0061 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -207,16 +207,18 @@
static bool no_collapse = false;
static bool no_close = false;
static bool no_nav = false;
-
+ static bool no_bring_to_front = false;
+
ImGuiWindowFlags window_flags = 0;
- if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar;
- if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar;
- if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar;
- if (no_move) window_flags |= ImGuiWindowFlags_NoMove;
- if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
- if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
- if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
- if (no_close) p_open = NULL; // Don't pass our bool* to Begin
+ if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar;
+ if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar;
+ if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar;
+ if (no_move) window_flags |= ImGuiWindowFlags_NoMove;
+ if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
+ if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
+ if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
+ if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
+ if (no_close) p_open = NULL; // Don't pass our bool* to Begin
// We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming.
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver);
@@ -359,7 +361,8 @@
ImGui::Checkbox("No resize", &no_resize); ImGui::SameLine(300);
ImGui::Checkbox("No collapse", &no_collapse);
ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150);
- ImGui::Checkbox("No nav", &no_nav);
+ ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300);
+ ImGui::Checkbox("No bring to front", &no_bring_to_front);
}
if (ImGui::CollapsingHeader("Widgets"))
diff --git a/imgui_internal.h b/imgui_internal.h
index cedf1c3..70c6bbd 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -649,7 +649,8 @@
int FrameCount;
int FrameCountEnded;
int FrameCountRendered;
- ImVector<ImGuiWindow*> Windows;
+ ImVector<ImGuiWindow*> Windows; // Windows, sorted in display order, back to front
+ ImVector<ImGuiWindow*> WindowsFocusOrder; // Windows, sorted in focus order, back to front
ImVector<ImGuiWindow*> WindowsSortBuffer;
ImVector<ImGuiWindow*> CurrentWindowStack;
ImGuiStorage WindowsById;
@@ -1113,9 +1114,10 @@
inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void FocusWindow(ImGuiWindow* window);
- IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window);
- IMGUI_API void BringWindowToFront(ImGuiWindow* window);
- IMGUI_API void BringWindowToBack(ImGuiWindow* window);
+ IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window);
+ IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
+ IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window);
+ IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window);
IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index f264396..7fe4c16 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -5448,7 +5448,7 @@
// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
ImGuiContext& g = *GImGui;
if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
- FocusFrontMostActiveWindowIgnoringOne(g.NavWindow);
+ FocusPreviousWindowIgnoringOne(g.NavWindow);
End();
}