From dcb6335bfed5934d6b1e377820e19d3e08be689b Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Apr 2023 20:25:05 +0200 Subject: [PATCH] Viewports: Setting focus from Platform/OS sets corresponding focus at Dear ImGui level. (#6299) --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 45 ++++++++++++++++++++++++++++----------------- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c9bb114ee..82c5d40f3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -153,6 +153,8 @@ Other changes: Docking+Viewports Branch: +- Viewports: Setting focus from Platform/OS (e.g. via decoration, or Alt-Tab) sets corresponding + focus at Dear ImGui level (generally last focused window in the viewport). (#6299) - Docking: Fixed using GetItemXXX() or IsItemXXX() functions after a DockSpace(). (#6217) - Backends: GLFW: Fixed key modifiers handling on secondary viewports. (#6248, #6034) [@aiekick] - Backends: GLFW: Fixed Emscripten erroneously enabling multi-viewport support, leading to assert. (#5683) @@ -423,7 +425,7 @@ Other changes: Docking+Viewports Branch: -- Viewport: Fixed collapsed windows setting ImGuiViewportFlags_NoRendererClear without +- Viewports: Fixed collapsed windows setting ImGuiViewportFlags_NoRendererClear without making title bar color opaque, leading to potential texture/fb garbage being visible. Right now as we don't fully support transparent viewports (#2766), so we turn that 'TitleBgCollapsed' color opaque just lke we do for 'WindowBG' on uncollapsed windows. diff --git a/imgui.cpp b/imgui.cpp index 8b604b008..d47c4bee1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4799,7 +4799,7 @@ void ImGui::NewFrame() // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) - FocusTopMostWindowUnderOne(NULL, NULL); + FocusTopMostWindowUnderOne(NULL, NULL, 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. @@ -7495,7 +7495,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) BringWindowToDisplayFront(display_front_window); } -void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) +void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport) { ImGuiContext& g = *GImGui; int start_idx = g.WindowsFocusOrder.Size - 1; @@ -7515,17 +7515,20 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; IM_ASSERT(window == window->RootWindow); - if (window != ignore_window && window->WasActive) - if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) - { - // FIXME-DOCK: This is failing (lagging by one frame) for docked windows. - // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. - // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) - // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); - FocusWindow(focus_window); - return; - } + if (window == ignore_window || !window->WasActive) + continue; + if (filter_viewport != NULL && window->Viewport != filter_viewport) + continue; + if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) + { + // FIXME-DOCK: This is failing (lagging by one frame) for docked windows. + // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. + // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) + // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? + ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); + FocusWindow(focus_window); + return; + } } FocusWindow(NULL); } @@ -10895,7 +10898,7 @@ void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_ if (focus_window && !focus_window->WasActive && popup_window) { // Fallback - FocusTopMostWindowUnderOne(popup_window, NULL); + FocusTopMostWindowUnderOne(popup_window, NULL, NULL); } else { @@ -12596,6 +12599,8 @@ static void ImGui::NavUpdateWindowing() // Apply final focus if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { + // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow() + // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow() ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; ClearActiveID(); NavRestoreHighlightAfterMove(); @@ -14490,6 +14495,10 @@ void ImGui::UpdatePlatformWindows() if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; g.PlatformLastFocusedViewportId = focused_viewport->ID; + if (focused_viewport->Window != NULL) + FocusWindow(NavRestoreLastChildNavWindow(focused_viewport->Window)); + else + FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport); } } } @@ -19151,11 +19160,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes()); if (viewports.Size > 1) ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByFrontMostStampCount); - for (int i = 0; i < viewports.Size; i++) - BulletText("Viewport #%d, ID: 0x%08X, FrontMostStampCount = %08d, Window: \"%s\"", viewports[i]->Idx, viewports[i]->ID, viewports[i]->LastFrontMostStampCount, viewports[i]->Window ? viewports[i]->Window->Name : "N/A"); + for (ImGuiViewportP* viewport : viewports) + BulletText("Viewport #%d, ID: 0x%08X, FrontMostStampCount = %08d, PlatformFocused = %s, Window: \"%s\"", + viewport->Idx, viewport->ID, viewport->LastFrontMostStampCount, + (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? "1" : "0") : "N/A", + viewport->Window ? viewport->Window->Name : "N/A"); TreePop(); } - for (int i = 0; i < g.Viewports.Size; i++) DebugNodeViewport(g.Viewports[i]); TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index 3dcc9f756..3c05886a9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2976,7 +2976,7 @@ namespace ImGui // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window); + IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cb57a59fa..c6da09fbf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7088,7 +7088,7 @@ void ImGui::EndMainMenuBar() // FIXME: With this strategy we won't be able to restore a NULL focus. ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) - FocusTopMostWindowUnderOne(g.NavWindow, NULL); + FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL); End(); }