fix(new_editor/tab-drag): keep floating window under cursor

This commit is contained in:
2026-04-15 13:03:44 +08:00
parent ea2e44976a
commit 2605608685
2 changed files with 134 additions and 4 deletions

View File

@@ -3,8 +3,34 @@
#include "State/EditorContext.h"
#include "EditorWindow.h"
#include <algorithm>
#include <cmath>
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<float>(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<LONG>(std::lround(localOffsetXDips * dpiScale));
outDragHotspot.y = static_cast<LONG>(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());
}