From 80d78fad7c3301b58003e669d27cac5438465fd5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 30 Jul 2025 18:39:15 +0900 Subject: [PATCH] Windows, Viewport: fixed an issue where interrupting a viewport move with e.g. a ClearActiveID() call would leave the dragged viewport with the ImGuiViewportFlags_NoInputs flag. Amend 6b7766817, 36055213c5, #5324. Next would be good to amend the ImGuiViewportFlags_NoInputs clear to match the set logic. --- docs/CHANGELOG.txt | 7 +++++ imgui.cpp | 71 ++++++++++++++++++++++++++++------------------ imgui_internal.h | 1 + 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 547d46f56..7d2a66e4a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,13 @@ Other Changes: - Backends: Vulkan: Fixed texture update corruption introduced in 1.92.0, affecting some drivers/setups. (#8801, #8755, #8840) [@Retro52, @Miolith] +Docking+Viewports Branch: + +- Windows, Viewport: fixed an issue where interrupting a viewport move with + e.g. a ClearActiveID() call would leave the dragged viewport with the + normally temporary ImGuiViewportFlags_NoInputs flag, preventing further + interactions with the viewport. (#5324) (thanks @mdelaharpe) + ----------------------------------------------------------------------- VERSION 1.92.1 (Released 2025-07-09) diff --git a/imgui.cpp b/imgui.cpp index 4c3c42faf..02b60e814 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4606,15 +4606,6 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) // Clear previous active id if (g.ActiveId != 0) { - // While most behaved code would make an effort to not steal active id during window move/drag operations, - // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch - // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. - if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) - { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); - g.MovingWindow = NULL; - } - // Store deactivate data ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData; deactivated_data->ID = g.ActiveId; @@ -4627,6 +4618,15 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID() if (g.InputTextState.ID == g.ActiveId) InputTextDeactivateHook(g.ActiveId); + + // While most behaved code would make an effort to not steal active id during window move/drag operations, + // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch + // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. + if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) + { + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); + StopMouseMovingWindow(); + } } // Set active id @@ -5207,6 +5207,34 @@ void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* nod StartMouseMovingWindow(window); } +// This is not 100% symetric with StartMouseMovingWindow(). +// We do NOT clear ActiveID, because: +// - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired. +// - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item). +void ImGui::StopMouseMovingWindow() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.MovingWindow; + + // Ref commits 6b7766817, 36055213c for some partial history on checking if viewport != NULL. + if (window && window->Viewport) + { + // Try to merge the window back into the main viewport. + // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + UpdateTryMergeWindowIntoHostViewport(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()) + g.MouseViewport = window->Viewport; + + // Clear the NoInputs window flag set by the Viewport system in AddUpdateViewport() + window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; + } + + g.MovingWindow = NULL; +} + // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() // FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId. @@ -5224,8 +5252,8 @@ 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); - if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) + const bool window_disappeared = (!moving_window->WasActive && !moving_window->Active); + if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappeared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) @@ -5241,23 +5269,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - if (!window_disappared) - { - // Try to merge the window back into the main viewport. - // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) - 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 (moving_window->Viewport && !IsDragDropPayloadBeingAccepted()) - g.MouseViewport = moving_window->Viewport; - - // Clear the NoInput window flag set by the Viewport system - if (moving_window->Viewport) - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; - } - - g.MovingWindow = NULL; + StopMouseMovingWindow(); ClearActiveID(); } } @@ -5299,6 +5311,9 @@ void ImGui::UpdateMouseMovingWindowEndFrame() { StartMouseMovingWindow(g.HoveredWindow); //-V595 + // FIXME: In principal we might be able to call StopMouseMovingWindow() below. + // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symetrical, at the later doesn't clear ActiveId. + // Cancel moving if clicked outside of title bar if (g.IO.ConfigWindowsMoveFromTitleBarOnly) if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive) diff --git a/imgui_internal.h b/imgui_internal.h index aefe06545..b3c9ca409 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3382,6 +3382,7 @@ namespace ImGui IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock); + IMGUI_API void StopMouseMovingWindow(); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame();