diff --git a/new_editor/app/Platform/Win32/EditorWindowManager.h b/new_editor/app/Platform/Win32/EditorWindowManager.h index 5e29ece7..6b34b8d9 100644 --- a/new_editor/app/Platform/Win32/EditorWindowManager.h +++ b/new_editor/app/Platform/Win32/EditorWindowManager.h @@ -88,6 +88,7 @@ private: std::string sourceNodeId = {}; std::string panelId = {}; POINT screenPoint = {}; + POINT dragHotspot = {}; }; void DestroyEditorWindow(EditorWindow& window); @@ -112,7 +113,15 @@ private: std::string_view panelWindowId, std::string_view sourceNodeId, std::string_view panelId, - const POINT& screenPoint); + const POINT& screenPoint, + const POINT& dragHotspot); + bool TryResolveGlobalTabDragHotspot( + const EditorWindow& sourceWindow, + std::string_view nodeId, + std::string_view panelId, + const POINT& screenPoint, + POINT& outDragHotspot) const; + void UpdateGlobalTabDragOwnerWindowPosition(); EditorWindow* FindTopmostWindowAtScreenPoint( const POINT& screenPoint, std::string_view excludedWindowId = {}); diff --git a/new_editor/app/Platform/Win32/EditorWindowManagerTabDrag.cpp b/new_editor/app/Platform/Win32/EditorWindowManagerTabDrag.cpp index bd8f9b42..3765ba08 100644 --- a/new_editor/app/Platform/Win32/EditorWindowManagerTabDrag.cpp +++ b/new_editor/app/Platform/Win32/EditorWindowManagerTabDrag.cpp @@ -3,8 +3,34 @@ #include "State/EditorContext.h" #include "EditorWindow.h" +#include +#include + namespace XCEngine::UI::Editor::App { +namespace { + +constexpr LONG kFallbackDragHotspotX = 40; +constexpr LONG kFallbackDragHotspotY = 12; + +POINT BuildFallbackGlobalTabDragHotspot() { + POINT hotspot = {}; + hotspot.x = kFallbackDragHotspotX; + hotspot.y = kFallbackDragHotspotY; + return hotspot; +} + +float ResolveWindowDpiScale(HWND hwnd) { + if (hwnd == nullptr) { + return 1.0f; + } + + const UINT dpi = GetDpiForWindow(hwnd); + return dpi == 0u ? 1.0f : static_cast(dpi) / 96.0f; +} + +} // namespace + bool EditorWindowManager::IsGlobalTabDragActive() const { return m_globalTabDragSession.active; } @@ -18,12 +44,94 @@ void EditorWindowManager::BeginGlobalTabDragSession( std::string_view panelWindowId, std::string_view sourceNodeId, std::string_view panelId, - const POINT& screenPoint) { + const POINT& screenPoint, + const POINT& dragHotspot) { m_globalTabDragSession.active = true; m_globalTabDragSession.panelWindowId = std::string(panelWindowId); m_globalTabDragSession.sourceNodeId = std::string(sourceNodeId); m_globalTabDragSession.panelId = std::string(panelId); m_globalTabDragSession.screenPoint = screenPoint; + m_globalTabDragSession.dragHotspot = dragHotspot; +} + +bool EditorWindowManager::TryResolveGlobalTabDragHotspot( + const EditorWindow& sourceWindow, + std::string_view nodeId, + std::string_view panelId, + const POINT& screenPoint, + POINT& outDragHotspot) const { + const HWND hwnd = sourceWindow.GetHwnd(); + if (hwnd == nullptr) { + return false; + } + + const auto& layout = + sourceWindow.GetShellFrame().workspaceInteractionFrame.dockHostFrame.layout; + for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { + if (tabStack.nodeId != nodeId) { + continue; + } + + const std::size_t tabCount = + (std::min)(tabStack.items.size(), tabStack.tabStripLayout.tabHeaderRects.size()); + for (std::size_t index = 0u; index < tabCount; ++index) { + if (tabStack.items[index].panelId != panelId) { + continue; + } + + const ::XCEngine::UI::UIPoint clientPointDips = + sourceWindow.ConvertScreenPixelsToClientDips(screenPoint); + const ::XCEngine::UI::UIRect& tabRect = + tabStack.tabStripLayout.tabHeaderRects[index]; + const float dpiScale = ResolveWindowDpiScale(hwnd); + const float localOffsetXDips = (std::clamp)( + clientPointDips.x - tabRect.x, + 0.0f, + (std::max)(tabRect.width, 0.0f)); + const float localOffsetYDips = (std::clamp)( + clientPointDips.y - tabRect.y, + 0.0f, + (std::max)(tabRect.height, 0.0f)); + outDragHotspot.x = static_cast(std::lround(localOffsetXDips * dpiScale)); + outDragHotspot.y = static_cast(std::lround(localOffsetYDips * dpiScale)); + return true; + } + } + + return false; +} + +void EditorWindowManager::UpdateGlobalTabDragOwnerWindowPosition() { + if (!m_globalTabDragSession.active) { + return; + } + + EditorWindow* ownerWindow = FindWindow(m_globalTabDragSession.panelWindowId); + if (ownerWindow == nullptr || ownerWindow->GetHwnd() == nullptr) { + return; + } + + RECT windowRect = {}; + if (!GetWindowRect(ownerWindow->GetHwnd(), &windowRect)) { + return; + } + + const LONG targetLeft = + m_globalTabDragSession.screenPoint.x - m_globalTabDragSession.dragHotspot.x; + const LONG targetTop = + m_globalTabDragSession.screenPoint.y - m_globalTabDragSession.dragHotspot.y; + if (windowRect.left == targetLeft && windowRect.top == targetTop) { + return; + } + + SetWindowPos( + ownerWindow->GetHwnd(), + nullptr, + targetLeft, + targetTop, + 0, + 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } void EditorWindowManager::EndGlobalTabDragSession() { @@ -55,6 +163,7 @@ bool EditorWindowManager::HandleGlobalTabDragPointerMove(HWND hwnd) { POINT screenPoint = {}; if (GetCursorPos(&screenPoint)) { m_globalTabDragSession.screenPoint = screenPoint; + UpdateGlobalTabDragOwnerWindowPosition(); } return true; } @@ -82,6 +191,14 @@ bool EditorWindowManager::TryStartGlobalTabDrag(EditorWindow& sourceWindow) { return false; } + POINT dragHotspot = BuildFallbackGlobalTabDragHotspot(); + TryResolveGlobalTabDragHotspot( + sourceWindow, + pending->nodeId, + pending->panelId, + pending->screenPoint, + dragHotspot); + if (sourceWindow.IsPrimary()) { UIEditorWindowWorkspaceController windowWorkspaceController = BuildLiveWindowWorkspaceController(sourceWindow.GetWindowId()); @@ -115,7 +232,9 @@ bool EditorWindowManager::TryStartGlobalTabDrag(EditorWindow& sourceWindow) { detachedWindow->GetWindowId(), detachedWindow->GetWorkspaceController().GetWorkspace().root.nodeId, pending->panelId, - pending->screenPoint); + pending->screenPoint, + dragHotspot); + UpdateGlobalTabDragOwnerWindowPosition(); SetCapture(detachedWindow->GetHwnd()); SetForegroundWindow(detachedWindow->GetHwnd()); LogRuntimeTrace( @@ -130,7 +249,9 @@ bool EditorWindowManager::TryStartGlobalTabDrag(EditorWindow& sourceWindow) { sourceWindow.GetWindowId(), pending->nodeId, pending->panelId, - pending->screenPoint); + pending->screenPoint, + dragHotspot); + UpdateGlobalTabDragOwnerWindowPosition(); if (sourceWindow.GetHwnd() != nullptr) { SetCapture(sourceWindow.GetHwnd()); }