From bf9a906464dc65e4f7f8dbe8cf48c4d71eceb460 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 17 Apr 2026 22:35:16 +0800 Subject: [PATCH] new_editor: filter closing windows from interaction --- .../XCUI_NewEditor收口重构计划_2026-04-17.md | 2 + .../Win32/WindowManager/Lifecycle.cpp | 12 +++-- .../Platform/Win32/WindowManager/TabDrag.cpp | 48 +++++++++++++++---- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/docs/plan/XCUI_NewEditor收口重构计划_2026-04-17.md b/docs/plan/XCUI_NewEditor收口重构计划_2026-04-17.md index ddabd402..4d5ce73e 100644 --- a/docs/plan/XCUI_NewEditor收口重构计划_2026-04-17.md +++ b/docs/plan/XCUI_NewEditor收口重构计划_2026-04-17.md @@ -57,6 +57,7 @@ - 已补 live window-set 构造时对 destroyed/no-`HWND` 窗口的过滤,避免 `primary/activeWindowId` 指向不存在的运行时窗口 - 已补 window-sync 失败时的已有窗口/新建窗口回滚,避免同步中途失败留下半更新窗口集 - 已补 closing-window 过滤,避免已发出 `WM_CLOSE` 但尚未销毁的 detached window 继续参与 live window-set 与命中测试 + - 已补 closing-window 对 render/transfer/global-tab-drag 的排除,并在主窗口级联关闭 detached windows 前先标记 `closing` 9. integration 测试构建模板已继续收口: - 已把 `tests/UI/Editor/integration` 叶子目标收敛到 shared helper @@ -556,6 +557,7 @@ - 已完成 detached window 起始 global-tab-drag 的源请求校验 - 已完成 live window-set 对 destroyed/no-`HWND` 窗口的 primary/active 过滤 - 已完成 window-sync 失败回滚与 closing-window 过滤 +- 已完成 closing-window 在 render/transfer/global-tab-drag 路径上的排除 - `transfer/session/state` 主链路已基本收口,剩余更多属于后续演进项而非当前主线阻塞 ### 涉及范围 diff --git a/new_editor/app/Platform/Win32/WindowManager/Lifecycle.cpp b/new_editor/app/Platform/Win32/WindowManager/Lifecycle.cpp index a08d4855..f5329839 100644 --- a/new_editor/app/Platform/Win32/WindowManager/Lifecycle.cpp +++ b/new_editor/app/Platform/Win32/WindowManager/Lifecycle.cpp @@ -276,7 +276,9 @@ void EditorWindowHostRuntime::RenderAllWindows( transferBatches.reserve(m_windows.size()); for (const std::unique_ptr& window : m_windows) { - if (window == nullptr || window->GetHwnd() == nullptr) { + if (window == nullptr || + window->GetHwnd() == nullptr || + window->IsClosing()) { continue; } @@ -293,7 +295,9 @@ void EditorWindowHostRuntime::RenderAllWindows( } for (WindowFrameTransferBatch& batch : transferBatches) { - if (batch.sourceWindow == nullptr || batch.sourceWindow->GetHwnd() == nullptr) { + if (batch.sourceWindow == nullptr || + batch.sourceWindow->GetHwnd() == nullptr || + batch.sourceWindow->IsClosing()) { continue; } @@ -310,7 +314,9 @@ void EditorWindowHostRuntime::HandleDestroyedWindow(HWND hwnd) { for (const std::unique_ptr& otherWindow : m_windows) { if (otherWindow != nullptr && otherWindow.get() != window && - otherWindow->GetHwnd() != nullptr) { + otherWindow->GetHwnd() != nullptr && + !otherWindow->IsClosing()) { + otherWindow->MarkClosing(); PostMessageW(otherWindow->GetHwnd(), WM_CLOSE, 0, 0); } } diff --git a/new_editor/app/Platform/Win32/WindowManager/TabDrag.cpp b/new_editor/app/Platform/Win32/WindowManager/TabDrag.cpp index 67a147ce..c68b2b0f 100644 --- a/new_editor/app/Platform/Win32/WindowManager/TabDrag.cpp +++ b/new_editor/app/Platform/Win32/WindowManager/TabDrag.cpp @@ -68,6 +68,12 @@ bool CanStartGlobalTabDragFromWindow( extractedPanel); } +bool IsLiveInteractiveWindow(const EditorWindow* window) { + return window != nullptr && + window->GetHwnd() != nullptr && + !window->IsClosing(); +} + std::size_t ResolveDropInsertionIndex( const Widgets::UIEditorDockHostTabStackLayout& tabStack, const UIPoint& point) { @@ -240,7 +246,7 @@ void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragOwnerWindowPosition() } EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId); - if (ownerWindow == nullptr || ownerWindow->GetHwnd() == nullptr) { + if (!IsLiveInteractiveWindow(ownerWindow)) { return; } @@ -273,7 +279,7 @@ void EditorWindowWorkspaceCoordinator::ClearGlobalTabDragDropPreview() { } if (EditorWindow* previewWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.previewWindowId); - previewWindow != nullptr) { + IsLiveInteractiveWindow(previewWindow)) { previewWindow->ClearExternalDockHostDropPreview(); previewWindow->InvalidateHostWindow(); } @@ -288,7 +294,7 @@ void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragDropPreview() { EditorWindow* targetWindow = FindTopmostWindowAtScreenPoint( m_globalTabDragSession.screenPoint, m_globalTabDragSession.panelWindowId); - if (targetWindow == nullptr || targetWindow->GetHwnd() == nullptr) { + if (!IsLiveInteractiveWindow(targetWindow)) { ClearGlobalTabDragDropPreview(); return; } @@ -333,7 +339,9 @@ void EditorWindowWorkspaceCoordinator::EndGlobalTabDragSession() { if (GetCapture() == ownerWindow->GetHwnd()) { ReleaseCapture(); } - ownerWindow->ResetInteractionState(); + if (!ownerWindow->IsClosing()) { + ownerWindow->ResetInteractionState(); + } } m_globalTabDragSession = {}; @@ -345,7 +353,11 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerMove(HWND hwnd) } const EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId); - if (ownerWindow == nullptr || ownerWindow->GetHwnd() != hwnd) { + if (!IsLiveInteractiveWindow(ownerWindow)) { + EndGlobalTabDragSession(); + return false; + } + if (ownerWindow->GetHwnd() != hwnd) { return false; } @@ -364,7 +376,11 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h } const EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId); - if (ownerWindow == nullptr || ownerWindow->GetHwnd() != hwnd) { + if (!IsLiveInteractiveWindow(ownerWindow)) { + EndGlobalTabDragSession(); + return false; + } + if (ownerWindow->GetHwnd() != hwnd) { return false; } @@ -377,7 +393,7 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h EndGlobalTabDragSession(); EditorWindow* targetWindow = FindTopmostWindowAtScreenPoint(screenPoint, panelWindowId); - if (targetWindow == nullptr || targetWindow->GetHwnd() == nullptr) { + if (!IsLiveInteractiveWindow(targetWindow)) { return true; } @@ -423,7 +439,8 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h if (EditorWindow* updatedTargetWindow = m_hostRuntime.FindWindow(targetWindow->GetWindowId()); updatedTargetWindow != nullptr && - updatedTargetWindow->GetHwnd() != nullptr) { + updatedTargetWindow->GetHwnd() != nullptr && + !updatedTargetWindow->IsClosing()) { SetForegroundWindow(updatedTargetWindow->GetHwnd()); } LogRuntimeTrace( @@ -436,6 +453,11 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( EditorWindow& sourceWindow, const EditorWindowPanelTransferRequest& request) { + if (sourceWindow.IsClosing()) { + LogRuntimeTrace("drag", "failed to start global tab drag: source window is closing"); + return false; + } + POINT dragHotspot = BuildFallbackGlobalTabDragHotspot(); TryResolveGlobalTabDragHotspot( sourceWindow, @@ -468,7 +490,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( } EditorWindow* detachedWindow = m_hostRuntime.FindWindow(result.targetWindowId); - if (detachedWindow == nullptr || detachedWindow->GetHwnd() == nullptr) { + if (!IsLiveInteractiveWindow(detachedWindow)) { LogRuntimeTrace("drag", "detached drag window was not created."); return false; } @@ -518,6 +540,11 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest( EditorWindow& sourceWindow, const EditorWindowPanelTransferRequest& request) { + if (sourceWindow.IsClosing()) { + LogRuntimeTrace("detach", "detach request rejected: source window is closing"); + return false; + } + const std::string sourceWindowId(sourceWindow.GetWindowId()); UIEditorWindowWorkspaceController windowWorkspaceController = BuildLiveWindowWorkspaceController(sourceWindowId); @@ -541,7 +568,8 @@ bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest( if (EditorWindow* detachedWindow = m_hostRuntime.FindWindow(result.targetWindowId); detachedWindow != nullptr && - detachedWindow->GetHwnd() != nullptr) { + detachedWindow->GetHwnd() != nullptr && + !detachedWindow->IsClosing()) { SetForegroundWindow(detachedWindow->GetHwnd()); }