From 35b41949fb589a86604370ac40940e573f29e581 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Jun 2023 15:55:14 +0200 Subject: [PATCH] Docking+Viewports: Fixed extraneous viewport+platform-window recreation. Part 3. Part 3: DockNodeRemoveWindow() clears viewports so it doesn't get bounced back and forth. Note that in case of called from e.g. dock builder this can happen mid-frame. Clearing Viewport here isn't well exercised yet. If window doesn't get a Begin() in same-frame it'll be hidden. Refer to "viewport_owner_change_1" and "viewport_owner_change_2" in ImGuiTestSuite. Amend 6b77668171 --- imgui.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 38a83dae..30cba582 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4407,14 +4407,14 @@ void ImGui::UpdateMouseMovingWindowNewFrame() ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; // When a window stop being submitted while being dragged, it may will its viewport until next Begin() - const bool window_disappared = ((!moving_window->WasActive && !moving_window->Active) || moving_window->Viewport == NULL); + const bool window_disappared = (!moving_window->WasActive && !moving_window->Active); if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) { SetWindowPos(moving_window, pos, ImGuiCond_Always); - if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. + if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. { moving_window->Viewport->Pos = pos; moving_window->Viewport->UpdateWorkRect(); @@ -4432,11 +4432,12 @@ void ImGui::UpdateMouseMovingWindowNewFrame() UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. - if (!IsDragDropPayloadBeingAccepted()) + if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted()) g.MouseViewport = moving_window->Viewport; // Clear the NoInput window flag set by the Viewport system - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. + if (moving_window->Viewport) + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; } g.MovingWindow = NULL; @@ -4971,6 +4972,7 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = window->Viewport; + IM_ASSERT(viewport != NULL); g.IO.MetricsRenderWindows++; if (window->Flags & ImGuiWindowFlags_DockNodeHost) window->DrawList->ChannelsMerge(); @@ -15890,6 +15892,16 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window window->ParentWindow->DC.ChildWindows.find_erase(window); UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately + if (node->HostWindow && node->HostWindow->ViewportOwned) + { + // When undocking from a user interaction this will always run in NewFrame() and have not much effect. + // But mid-frame, if we clear viewport we need to mark window as hidden as well. + window->Viewport = NULL; + window->ViewportId = 0; + window->ViewportOwned = false; + window->Hidden = true; + } + // Remove window bool erased = false; for (int n = 0; n < node->Windows.Size; n++)