Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus properly after the main menu bar or last focused window is deactivated.

This commit is contained in:
omar 2018-09-10 20:30:14 +02:00
parent dcef0c0237
commit d5692bff00
5 changed files with 69 additions and 41 deletions

View File

@ -54,6 +54,9 @@ HOW TO UPDATE?
VERSION 1.66 (In Progress) VERSION 1.66 (In Progress)
----------------------------------------------------------------------- -----------------------------------------------------------------------
- Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus
properly after the main menu bar or last focused window is deactivated.
----------------------------------------------------------------------- -----------------------------------------------------------------------
VERSION 1.65 (Released 2018-09-06) VERSION 1.65 (Released 2018-09-06)

View File

@ -918,7 +918,7 @@ static void CheckStacksSize(ImGuiWindow* window, bool write);
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list); static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window); static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
// Settings // Settings
static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
@ -3385,6 +3385,7 @@ void ImGui::NewFrame()
g.NavIdTabCounter = INT_MAX; g.NavIdTabCounter = INT_MAX;
// Mark all windows as not visible // Mark all windows as not visible
IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
for (int i = 0; i != g.Windows.Size; i++) for (int i = 0; i != g.Windows.Size; i++)
{ {
ImGuiWindow* window = g.Windows[i]; ImGuiWindow* window = g.Windows[i];
@ -3395,9 +3396,8 @@ void ImGui::NewFrame()
} }
// Closing the focused window restore focus to the first active root window in descending z-order // Closing the focused window restore focus to the first active root window in descending z-order
// FIXME: Because z-order and nav-order are correlated, this will focus the wrong window if we are part of a hierarchy with NoBringToFrontOnFocus flag.
if (g.NavWindow && !g.NavWindow->WasActive) if (g.NavWindow && !g.NavWindow->WasActive)
FocusFrontMostActiveWindowIgnoringOne(NULL); FocusPreviousWindowIgnoringOne(NULL);
// No window should be open at the beginning of the frame. // 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. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
@ -3475,6 +3475,7 @@ void ImGui::Shutdown(ImGuiContext* context)
for (int i = 0; i < g.Windows.Size; i++) for (int i = 0; i < g.Windows.Size; i++)
IM_DELETE(g.Windows[i]); IM_DELETE(g.Windows[i]);
g.Windows.clear(); g.Windows.clear();
g.WindowsFocusOrder.clear();
g.WindowsSortBuffer.clear(); g.WindowsSortBuffer.clear();
g.CurrentWindow = NULL; g.CurrentWindow = NULL;
g.CurrentWindowStack.clear(); g.CurrentWindowStack.clear();
@ -3524,7 +3525,7 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); 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); out_sorted_windows->push_back(window);
if (window->Active) if (window->Active)
@ -3536,7 +3537,7 @@ static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows,
{ {
ImGuiWindow* child = window->DC.ChildWindows[i]; ImGuiWindow* child = window->DC.ChildWindows[i];
if (child->Active) if (child->Active)
AddWindowToSortedBuffer(out_sorted_windows, child); AddWindowToSortBuffer(out_sorted_windows, child);
} }
} }
} }
@ -3807,7 +3808,7 @@ void ImGui::EndFrame()
ImGuiWindow* window = g.Windows[i]; ImGuiWindow* window = g.Windows[i];
if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
continue; continue;
AddWindowToSortedBuffer(&g.WindowsSortBuffer, window); AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
} }
IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
@ -4503,8 +4504,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
} }
g.WindowsFocusOrder.push_back(window);
if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) 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 else
g.Windows.push_back(window); g.Windows.push_back(window);
return window; return window;
@ -5621,7 +5623,21 @@ void ImGui::End()
SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
} }
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; ImGuiContext& g = *GImGui;
ImGuiWindow* current_front_window = g.Windows.back(); ImGuiWindow* current_front_window = g.Windows.back();
@ -5630,13 +5646,13 @@ void ImGui::BringWindowToFront(ImGuiWindow* window)
for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
if (g.Windows[i] == window) if (g.Windows[i] == window)
{ {
g.Windows.erase(g.Windows.Data + i); memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
g.Windows.push_back(window); g.Windows[g.Windows.Size - 1] = window;
break; break;
} }
} }
void ImGui::BringWindowToBack(ImGuiWindow* window) void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.Windows[0] == window) if (g.Windows[0] == window)
@ -5681,20 +5697,24 @@ void ImGui::FocusWindow(ImGuiWindow* window)
ClearActiveID(); ClearActiveID();
// Bring to front // Bring to front
BringWindowToFocusFront(window);
if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
BringWindowToFront(window); BringWindowToDisplayFront(window);
} }
void ImGui::FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window) void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
for (int i = g.Windows.Size - 1; i >= 0; i--) for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
{ {
ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); ImGuiWindow* window = g.WindowsFocusOrder[i];
if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
{
ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
FocusWindow(focus_window); FocusWindow(focus_window);
return; return;
} }
}
} }
void ImGui::PushItemWidth(float item_width) void ImGui::PushItemWidth(float item_width)
@ -8639,11 +8659,11 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
return 0.0f; return 0.0f;
} }
static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
for (int i = g.Windows.Size-1; i >= 0; i--) for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
if (g.Windows[i] == window) if (g.WindowsFocusOrder[i] == window)
return i; return i;
return -1; return -1;
} }
@ -8651,9 +8671,9 @@ static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N)
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
if (ImGui::IsWindowNavFocusable(g.Windows[i])) if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
return g.Windows[i]; return g.WindowsFocusOrder[i];
return NULL; return NULL;
} }
@ -8664,10 +8684,10 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
return; 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); ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
if (!window_target) 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 if (window_target) // Don't reset windowing target if there's a single window in the list
g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
g.NavWindowingToggleLayer = false; g.NavWindowingToggleLayer = false;
@ -8699,7 +8719,7 @@ static void ImGui::NavUpdateWindowing()
bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); 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); 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 (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.NavWindowingTarget = g.NavWindowingTargetAnim = window;
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
@ -8843,9 +8863,9 @@ void ImGui::NavUpdateWindowingList()
SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); 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); 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)) if (!IsWindowNavFocusable(window))
continue; continue;
const char* label = window->Name; const char* label = window->Name;

View File

@ -215,6 +215,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
static bool no_collapse = false; static bool no_collapse = false;
static bool no_close = false; static bool no_close = false;
static bool no_nav = false; static bool no_nav = false;
static bool no_bring_to_front = false;
static bool no_docking = false; static bool no_docking = false;
ImGuiWindowFlags window_flags = 0; ImGuiWindowFlags window_flags = 0;
@ -225,6 +226,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; if (no_resize) window_flags |= ImGuiWindowFlags_NoResize;
if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse;
if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; if (no_nav) window_flags |= ImGuiWindowFlags_NoNav;
if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking;
if (no_close) p_open = NULL; // Don't pass our bool* to Begin if (no_close) p_open = NULL; // Don't pass our bool* to Begin
@ -389,6 +391,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Checkbox("No collapse", &no_collapse); ImGui::Checkbox("No collapse", &no_collapse);
ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150);
ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300); ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300);
ImGui::Checkbox("No bring to front", &no_bring_to_front);
ImGui::Checkbox("No docking", &no_docking); ImGui::Checkbox("No docking", &no_docking);
} }

View File

@ -799,7 +799,8 @@ struct ImGuiContext
int FrameCountEnded; int FrameCountEnded;
int FrameCountPlatformEnded; int FrameCountPlatformEnded;
int FrameCountRendered; 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*> WindowsSortBuffer;
ImVector<ImGuiWindow*> CurrentWindowStack; ImVector<ImGuiWindow*> CurrentWindowStack;
ImGuiStorage WindowsById; ImGuiStorage WindowsById;
@ -1362,9 +1363,10 @@ namespace ImGui
IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id);
IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus() IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus()
IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window); IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window);
IMGUI_API void BringWindowToFront(ImGuiWindow* window); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window);
IMGUI_API void BringWindowToBack(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 void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);

View File

@ -5457,7 +5457,7 @@ void ImGui::EndMainMenuBar()
// When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window // 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; ImGuiContext& g = *GImGui;
if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0)
FocusFrontMostActiveWindowIgnoringOne(g.NavWindow); FocusPreviousWindowIgnoringOne(g.NavWindow);
End(); End();
} }