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
This commit is contained in:
ocornut 2023-06-23 15:55:14 +02:00
parent 099e8533e2
commit 35b41949fb

View File

@ -4407,14 +4407,14 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
// When a window stop being submitted while being dragged, it may will its viewport until next Begin() // 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) if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared)
{ {
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
{ {
SetWindowPos(moving_window, pos, ImGuiCond_Always); 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->Pos = pos;
moving_window->Viewport->UpdateWorkRect(); moving_window->Viewport->UpdateWorkRect();
@ -4432,11 +4432,12 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); 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. // 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; g.MouseViewport = moving_window->Viewport;
// Clear the NoInput window flag set by the Viewport system // 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; g.MovingWindow = NULL;
@ -4971,6 +4972,7 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiViewportP* viewport = window->Viewport; ImGuiViewportP* viewport = window->Viewport;
IM_ASSERT(viewport != NULL);
g.IO.MetricsRenderWindows++; g.IO.MetricsRenderWindows++;
if (window->Flags & ImGuiWindowFlags_DockNodeHost) if (window->Flags & ImGuiWindowFlags_DockNodeHost)
window->DrawList->ChannelsMerge(); window->DrawList->ChannelsMerge();
@ -15890,6 +15892,16 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
window->ParentWindow->DC.ChildWindows.find_erase(window); window->ParentWindow->DC.ChildWindows.find_erase(window);
UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately 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 // Remove window
bool erased = false; bool erased = false;
for (int n = 0; n < node->Windows.Size; n++) for (int n = 0; n < node->Windows.Size; n++)