From f1ae07e5321014deaa9920b89e72b1faf9a95700 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Apr 2018 11:52:38 +0200 Subject: [PATCH] Viewport, Platform: Using Platform_GetWindowFocus to provide a much stronger heuristic of platform z-order, in replacement for when the back-end cannot provide io.MouseHoveredViewport. The pressure for it to work well increased with the use of viewports by popups/menus. (#1542) --- TODO.txt | 1 - imgui.cpp | 29 +++++++++++++++++++++++------ imgui.h | 2 +- imgui_internal.h | 8 ++++++-- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/TODO.txt b/TODO.txt index 69f88df71..3fff67ed4 100644 --- a/TODO.txt +++ b/TODO.txt @@ -262,7 +262,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. - - viewport: fallback calculation of hovered window is very currently wrong without ImGuiBackendFlags_HasMouseHoveredViewport. typically affect half-overlapping viewported menus. - viewport: need to clarify how to use GetMousePos() from a user point of view. - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. diff --git a/imgui.cpp b/imgui.cpp index 9c0e0c95b..aade0c309 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3392,8 +3392,8 @@ static void ImGui::UpdateMovingWindow() } // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. -// This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -// FIXME-VIEWPORT: Need a proper notion of focus. At least use the equivalent of LastFrameAsRefViewport on a per-window basis. +// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +// B) It requires Platform_GetWindowFocus to be implemented by back-end. static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { ImGuiContext& g = *GImGui; @@ -3402,7 +3402,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m { ImGuiViewportP* viewport = g.Viewports[n]; if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && viewport->GetRect().Contains(mouse_platform_pos)) - if (best_candidate == NULL || best_candidate->LastFrameAsRefViewport < viewport->LastFrameAsRefViewport) + if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) best_candidate = viewport; } return best_candidate; @@ -3532,10 +3532,8 @@ static void ImGui::UpdateViewports() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { g.MouseRefViewport = g.MouseRefPrevViewport = main_viewport; - g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; return; } - g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. ImGuiViewportP* viewport_hovered = NULL; @@ -3584,6 +3582,21 @@ void ImGui::UpdatePlatformWindows() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; + // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. + if (g.PlatformIO.Platform_GetWindowFocus) + { + ImGuiViewportP* focused_viewport = NULL; + for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) + if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) + focused_viewport = g.Viewports[i]; + if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) + { + if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + g.PlatformLastFocusedViewport = focused_viewport->ID; + } + } + // Create/resize/destroy platform windows to match each active viewport. // Skip the main viewport (index 0), which is always fully handled by the application! for (int i = 1; i < g.Viewports.Size; i++) @@ -3656,7 +3669,7 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); viewport->LastAlpha = viewport->Alpha; - // Show window. On startup ensure platform window don't get focus. + // Show window. On startup ensure platform window don't get focus if (is_new_window) { if (g.FrameCount < 2) @@ -3664,6 +3677,10 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_ShowWindow(viewport); } + // Even without focus, we assume the window becomes front-most. This is used by our platform z-order heuristic when io.MouseHoveredViewport is not available. + if (is_new_window && viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + // Clear request flags viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; } diff --git a/imgui.h b/imgui.h index 17f674494..4aa47a74a 100644 --- a/imgui.h +++ b/imgui.h @@ -1073,7 +1073,7 @@ struct ImGuiIO float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. ImGuiID MousePosViewport; // (Optional) When using multiple viewports: viewport from which io.MousePos is based from (when dragging this is generally the captured/focused viewport, even though we can drag outside of it and then it's not hovered anymore). (0 == default viewport) - ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering. (0 == default viewport) _IGNORING_ viewports with the ImGuiBackendFlags_HasMouseHoveredViewport flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this information. + ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will use a decent heuristic instead. bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift diff --git a/imgui_internal.h b/imgui_internal.h index 854ebff2c..8372e8bd3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -511,8 +511,8 @@ struct ImGuiViewportP : public ImGuiViewport { int Idx; int LastFrameActive; // Last frame number this viewport was activated by a window - int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef int LastFrameOverlayDrawList; + int LastFrontMostStampCount; // Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order ImGuiID LastNameHash; ImVec2 LastPos; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) @@ -524,7 +524,7 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } @@ -610,6 +610,7 @@ struct ImGuiContext ImVector CurrentWindowStack; ImGuiStorage WindowsById; int WindowsActiveCount; + int WindowsFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) @@ -644,6 +645,7 @@ struct ImGuiContext ImGuiViewportP* MouseRefPrevViewport; ImGuiViewportP* MouseHoveredLastViewport; // Last viewport that was hovered by mouse (even if we are not hovering any viewport any more) ImGuiID MouseClickedPosViewportId[5]; // For rarely used fields we only compare to, store viewport ID only so we don't have to clean dangling pointers + ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -751,6 +753,7 @@ struct ImGuiContext FrameCount = 0; FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; WindowsActiveCount = 0; + WindowsFrontMostStampCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; @@ -776,6 +779,7 @@ struct ImGuiContext MouseRefViewport = NULL; MouseRefPrevViewport = MouseHoveredLastViewport = NULL; memset(MouseClickedPosViewportId, 0, sizeof(MouseClickedPosViewportId)); + PlatformLastFocusedViewport = 0; NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0;