From d5692bff00a4643f91794fe8b6056aa3b210f9aa Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 20:30:14 +0200 Subject: [PATCH] Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus properly after the main menu bar or last focused window is deactivated. --- docs/CHANGELOG.txt | 3 ++ imgui.cpp | 74 +++++++++++++++++++++++++++++----------------- imgui_demo.cpp | 21 +++++++------ imgui_internal.h | 10 ++++--- imgui_widgets.cpp | 2 +- 5 files changed, 69 insertions(+), 41 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b59a1bc45..f0c5d6498 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,9 @@ HOW TO UPDATE? 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) diff --git a/imgui.cpp b/imgui.cpp index 47662c0be..bf933a1df 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -918,7 +918,7 @@ static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); // Settings static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); @@ -3385,6 +3385,7 @@ void ImGui::NewFrame() 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]; @@ -3395,9 +3396,8 @@ void ImGui::NewFrame() } // 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) - 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. @@ -3475,6 +3475,7 @@ void ImGui::Shutdown(ImGuiContext* context) 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(); @@ -3524,7 +3525,7 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); } -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) { out_sorted_windows->push_back(window); if (window->Active) @@ -3536,7 +3537,7 @@ static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, { ImGuiWindow* child = window->DC.ChildWindows[i]; 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]; 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 @@ -4503,8 +4504,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl 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; @@ -5621,7 +5623,21 @@ void ImGui::End() 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; 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 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) @@ -5681,20 +5697,24 @@ void ImGui::FocusWindow(ImGuiWindow* window) 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) @@ -8639,11 +8659,11 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) 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; } @@ -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) { 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; } @@ -8664,10 +8684,10 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) 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; @@ -8699,7 +8719,7 @@ static void ImGui::NavUpdateWindowing() 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; @@ -8843,9 +8863,9 @@ void ImGui::NavUpdateWindowingList() SetNextWindowPos(viewport->Pos + viewport->Size * 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 da89d978c..04884436c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -215,18 +215,20 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_collapse = false; static bool no_close = false; static bool no_nav = false; + static bool no_bring_to_front = false; static bool no_docking = 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_docking) window_flags |= ImGuiWindowFlags_NoDocking; - 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_docking) window_flags |= ImGuiWindowFlags_NoDocking; + 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. ImVec2 main_viewport_pos = ImGui::GetMainViewport()->Pos; @@ -389,6 +391,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("No collapse", &no_collapse); ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); 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); } diff --git a/imgui_internal.h b/imgui_internal.h index 84fcb7526..d0505ffdc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -799,7 +799,8 @@ struct ImGuiContext int FrameCountEnded; int FrameCountPlatformEnded; int FrameCountRendered; - ImVector Windows; + ImVector Windows; // Windows, sorted in display order, back to front + ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front ImVector WindowsSortBuffer; ImVector CurrentWindowStack; ImGuiStorage WindowsById; @@ -1362,9 +1363,10 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus() - 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 a622a034b..c949e4526 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -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 ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusFrontMostActiveWindowIgnoringOne(g.NavWindow); + FocusPreviousWindowIgnoringOne(g.NavWindow); End(); }