Files
XCEngine/new_editor/app/Platform/Win32/EditorWindowManagerTabDrag.cpp

267 lines
8.4 KiB
C++
Raw Normal View History

#include "EditorWindowManager.h"
#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;
}
bool EditorWindowManager::OwnsActiveGlobalTabDrag(std::string_view windowId) const {
return m_globalTabDragSession.active &&
m_globalTabDragSession.panelWindowId == windowId;
}
void EditorWindowManager::BeginGlobalTabDragSession(
std::string_view panelWindowId,
std::string_view sourceNodeId,
std::string_view panelId,
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() {
if (!m_globalTabDragSession.active) {
return;
}
if (EditorWindow* ownerWindow = FindWindow(m_globalTabDragSession.panelWindowId);
ownerWindow != nullptr) {
if (GetCapture() == ownerWindow->GetHwnd()) {
ReleaseCapture();
}
ownerWindow->ResetInteractionState();
}
m_globalTabDragSession = {};
}
bool EditorWindowManager::HandleGlobalTabDragPointerMove(HWND hwnd) {
if (!m_globalTabDragSession.active) {
return false;
}
const EditorWindow* ownerWindow = FindWindow(m_globalTabDragSession.panelWindowId);
if (ownerWindow == nullptr || ownerWindow->GetHwnd() != hwnd) {
return false;
}
POINT screenPoint = {};
if (GetCursorPos(&screenPoint)) {
m_globalTabDragSession.screenPoint = screenPoint;
UpdateGlobalTabDragOwnerWindowPosition();
}
return true;
}
void EditorWindowManager::ProcessPendingGlobalTabDragStarts() {
if (m_globalTabDragSession.active) {
return;
}
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
if (window == nullptr || window->GetHwnd() == nullptr) {
continue;
}
if (TryStartGlobalTabDrag(*window)) {
return;
}
}
}
bool EditorWindowManager::TryStartGlobalTabDrag(EditorWindow& sourceWindow) {
const std::optional<EditorWindowPendingTabDragStart> pending =
sourceWindow.ConsumePendingTabDragStart();
if (!pending.has_value()) {
return false;
}
POINT dragHotspot = BuildFallbackGlobalTabDragHotspot();
TryResolveGlobalTabDragHotspot(
sourceWindow,
pending->nodeId,
pending->panelId,
pending->screenPoint,
dragHotspot);
if (sourceWindow.IsPrimary()) {
UIEditorWindowWorkspaceController windowWorkspaceController =
BuildLiveWindowWorkspaceController(sourceWindow.GetWindowId());
const UIEditorWindowWorkspaceOperationResult result =
windowWorkspaceController.DetachPanelToNewWindow(
sourceWindow.GetWindowId(),
pending->nodeId,
pending->panelId);
if (result.status != UIEditorWindowWorkspaceOperationStatus::Changed) {
LogRuntimeTrace(
"drag",
"failed to start global tab drag from primary window: " + result.message);
return false;
}
if (!SynchronizeWindowsFromController(
windowWorkspaceController,
result.targetWindowId,
pending->screenPoint)) {
LogRuntimeTrace("drag", "failed to synchronize detached drag window state");
return false;
}
EditorWindow* detachedWindow = FindWindow(result.targetWindowId);
if (detachedWindow == nullptr || detachedWindow->GetHwnd() == nullptr) {
LogRuntimeTrace("drag", "detached drag window was not created.");
return false;
}
BeginGlobalTabDragSession(
detachedWindow->GetWindowId(),
detachedWindow->GetWorkspaceController().GetWorkspace().root.nodeId,
pending->panelId,
pending->screenPoint,
dragHotspot);
UpdateGlobalTabDragOwnerWindowPosition();
SetCapture(detachedWindow->GetHwnd());
SetForegroundWindow(detachedWindow->GetHwnd());
LogRuntimeTrace(
"drag",
"started global tab drag by detaching panel '" + pending->panelId +
"' into window '" + std::string(detachedWindow->GetWindowId()) + "'");
return true;
}
sourceWindow.ResetInteractionState();
BeginGlobalTabDragSession(
sourceWindow.GetWindowId(),
pending->nodeId,
pending->panelId,
pending->screenPoint,
dragHotspot);
UpdateGlobalTabDragOwnerWindowPosition();
if (sourceWindow.GetHwnd() != nullptr) {
SetCapture(sourceWindow.GetHwnd());
}
LogRuntimeTrace(
"drag",
"started global tab drag from detached window '" +
std::string(sourceWindow.GetWindowId()) +
"' panel '" + pending->panelId + "'");
return true;
}
} // namespace XCEngine::UI::Editor::App