From cf4fcc473550c8c024a84f480aebe5d4e6aafedc Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 Mar 2019 13:10:41 +0100 Subject: [PATCH] Viewports: Fixed delayed window pos->viewport pos sync leading to monitor not being updated at the time of clamping window position in Begin. (#2415, #1542) --- imgui.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bf571aa8..d457f726 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1061,6 +1061,7 @@ static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); +static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport); } @@ -5581,13 +5582,31 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); } + bool viewport_rect_changed = false; if (window->ViewportOwned) { + // Synchronize window --> viewport in most situations // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM if (window->Viewport->PlatformRequestMove) window->Pos = window->Viewport->Pos; + else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Pos = window->Pos; + } + if (window->Viewport->PlatformRequestResize) window->Size = window->SizeFull = window->Viewport->Size; + else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Size = window->Size; + } + + // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() + // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. + if (viewport_rect_changed) + UpdateViewportPlatformMonitor(window->Viewport); // Update common viewport flags ImGuiViewportFlags viewport_flags = (window->Viewport->Flags) & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); @@ -5669,7 +5688,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); window->ResizeBorderHeld = (signed char)border_held; - // Synchronize window --> viewport + // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either) if (window->ViewportOwned) { if (!window->Viewport->PlatformRequestMove) @@ -10115,8 +10134,7 @@ static void ImGui::UpdateViewportsNewFrame() viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); } - // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + UpdateViewportPlatformMonitor(viewport); } // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. @@ -10261,7 +10279,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Idx = g.Viewports.Size; viewport->Pos = viewport->LastPos = pos; viewport->Size = size; - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + UpdateViewportPlatformMonitor(viewport); g.Viewports.push_back(viewport); //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); @@ -10569,11 +10587,12 @@ static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) // Search for the monitor with the largest intersection area with the given rectangle // We generally try to avoid searching loops but the monitor count should be very small here +// FIXME-OPT: We could test the last monitor used for that viewport first.. static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) { ImGuiContext& g = *GImGui; - // Use a minimum threshold of 1.0f so a zero-sized rect will still find its monitor given its position. + // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position. // This is necessary for tooltips which always resize down to zero at first. const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); int best_monitor_n = -1; @@ -10596,6 +10615,12 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) return best_monitor_n; } +// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor) +static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) +{ + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); +} + void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui;