diff --git a/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDrop.cpp b/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDrop.cpp index db260f42..57596274 100644 --- a/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDrop.cpp +++ b/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDrop.cpp @@ -1,166 +1,14 @@ #include "EditorWindowManager.h" +#include "Platform/Win32/EditorWindowManagerCrossWindowDropSupport.h" #include "State/EditorContext.h" #include "EditorWindow.h" -#include - namespace XCEngine::UI::Editor::App { -namespace { - +using EditorWindowManagerCrossWindowDropSupport::CrossWindowDockDropTarget; +using EditorWindowManagerCrossWindowDropSupport::TryResolveCrossWindowDockDropTarget; using ::XCEngine::UI::UIPoint; -using ::XCEngine::UI::UIRect; - -struct CrossWindowDockDropTarget { - bool valid = false; - std::string nodeId = {}; - UIEditorWorkspaceDockPlacement placement = UIEditorWorkspaceDockPlacement::Center; - std::size_t insertionIndex = Widgets::UIEditorTabStripInvalidIndex; -}; - -bool IsPointInsideRect(const UIRect& rect, const UIPoint& point) { - return point.x >= rect.x && - point.x <= rect.x + rect.width && - point.y >= rect.y && - point.y <= rect.y + rect.height; -} - -std::size_t ResolveCrossWindowDropInsertionIndex( - const Widgets::UIEditorDockHostTabStackLayout& tabStack, - const UIPoint& point) { - if (!IsPointInsideRect(tabStack.tabStripLayout.headerRect, point)) { - return Widgets::UIEditorTabStripInvalidIndex; - } - - std::size_t insertionIndex = 0u; - for (const UIRect& rect : tabStack.tabStripLayout.tabHeaderRects) { - const float midpoint = rect.x + rect.width * 0.5f; - if (point.x > midpoint) { - ++insertionIndex; - } - } - return insertionIndex; -} - -UIEditorWorkspaceDockPlacement ResolveCrossWindowDockPlacement( - const Widgets::UIEditorDockHostTabStackLayout& tabStack, - const UIPoint& point) { - if (IsPointInsideRect(tabStack.tabStripLayout.headerRect, point)) { - return UIEditorWorkspaceDockPlacement::Center; - } - - const float leftDistance = point.x - tabStack.bounds.x; - const float rightDistance = tabStack.bounds.x + tabStack.bounds.width - point.x; - const float topDistance = point.y - tabStack.bounds.y; - const float bottomDistance = tabStack.bounds.y + tabStack.bounds.height - point.y; - const float minHorizontalThreshold = tabStack.bounds.width * 0.25f; - const float minVerticalThreshold = tabStack.bounds.height * 0.25f; - const float nearestEdge = - (std::min)((std::min)(leftDistance, rightDistance), (std::min)(topDistance, bottomDistance)); - - if (nearestEdge == leftDistance && leftDistance <= minHorizontalThreshold) { - return UIEditorWorkspaceDockPlacement::Left; - } - if (nearestEdge == rightDistance && rightDistance <= minHorizontalThreshold) { - return UIEditorWorkspaceDockPlacement::Right; - } - if (nearestEdge == topDistance && topDistance <= minVerticalThreshold) { - return UIEditorWorkspaceDockPlacement::Top; - } - if (nearestEdge == bottomDistance && bottomDistance <= minVerticalThreshold) { - return UIEditorWorkspaceDockPlacement::Bottom; - } - - return UIEditorWorkspaceDockPlacement::Center; -} - -bool TryResolveCrossWindowDockDropTarget( - const Widgets::UIEditorDockHostLayout& layout, - const UIPoint& point, - CrossWindowDockDropTarget& outTarget) { - outTarget = {}; - if (!IsPointInsideRect(layout.bounds, point)) { - return false; - } - - const Widgets::UIEditorDockHostHitTarget hitTarget = - Widgets::HitTestUIEditorDockHost(layout, point); - for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { - if ((!hitTarget.nodeId.empty() && tabStack.nodeId != hitTarget.nodeId) || - !IsPointInsideRect(tabStack.bounds, point)) { - continue; - } - - outTarget.valid = true; - outTarget.nodeId = tabStack.nodeId; - outTarget.placement = ResolveCrossWindowDockPlacement(tabStack, point); - if (outTarget.placement == UIEditorWorkspaceDockPlacement::Center) { - outTarget.insertionIndex = ResolveCrossWindowDropInsertionIndex(tabStack, point); - if (outTarget.insertionIndex == Widgets::UIEditorTabStripInvalidIndex) { - outTarget.insertionIndex = tabStack.items.size(); - } - } - return true; - } - - for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { - if (IsPointInsideRect(tabStack.bounds, point)) { - outTarget.valid = true; - outTarget.nodeId = tabStack.nodeId; - outTarget.placement = ResolveCrossWindowDockPlacement(tabStack, point); - if (outTarget.placement == UIEditorWorkspaceDockPlacement::Center) { - outTarget.insertionIndex = tabStack.items.size(); - } - return true; - } - } - - return false; -} - -} // namespace - -EditorWindow* EditorWindowManager::FindTopmostWindowAtScreenPoint( - const POINT& screenPoint, - std::string_view excludedWindowId) { - if (const HWND hitWindow = WindowFromPoint(screenPoint); hitWindow != nullptr) { - const HWND rootWindow = GetAncestor(hitWindow, GA_ROOT); - if (EditorWindow* window = FindWindow(rootWindow); - window != nullptr && - window->GetWindowId() != excludedWindowId) { - return window; - } - } - - for (auto it = m_windows.rbegin(); it != m_windows.rend(); ++it) { - EditorWindow* const window = it->get(); - if (window == nullptr || - window->GetHwnd() == nullptr || - window->GetWindowId() == excludedWindowId) { - continue; - } - - RECT windowRect = {}; - if (GetWindowRect(window->GetHwnd(), &windowRect) && - screenPoint.x >= windowRect.left && - screenPoint.x < windowRect.right && - screenPoint.y >= windowRect.top && - screenPoint.y < windowRect.bottom) { - return window; - } - } - - return nullptr; -} - -const EditorWindow* EditorWindowManager::FindTopmostWindowAtScreenPoint( - const POINT& screenPoint, - std::string_view excludedWindowId) const { - return const_cast(this)->FindTopmostWindowAtScreenPoint( - screenPoint, - excludedWindowId); -} bool EditorWindowManager::HandleGlobalTabDragPointerButtonUp(HWND hwnd) { if (!m_globalTabDragSession.active) { diff --git a/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDropSupport.h b/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDropSupport.h new file mode 100644 index 00000000..1e607f8e --- /dev/null +++ b/new_editor/app/Platform/Win32/EditorWindowManagerCrossWindowDropSupport.h @@ -0,0 +1,122 @@ +#pragma once + +#include + +#include +#include +#include + +namespace XCEngine::UI { +struct UIPoint; +} + +namespace XCEngine::UI::Editor::App::EditorWindowManagerCrossWindowDropSupport { + +struct CrossWindowDockDropTarget { + bool valid = false; + std::string nodeId = {}; + UIEditorWorkspaceDockPlacement placement = UIEditorWorkspaceDockPlacement::Center; + std::size_t insertionIndex = Widgets::UIEditorTabStripInvalidIndex; +}; + +bool TryResolveCrossWindowDockDropTarget( + const Widgets::UIEditorDockHostLayout& layout, + const ::XCEngine::UI::UIPoint& point, + CrossWindowDockDropTarget& outTarget) { + using ::XCEngine::UI::UIPoint; + using ::XCEngine::UI::UIRect; + + const auto isPointInsideRect = [](const UIRect& rect, const UIPoint& targetPoint) { + return targetPoint.x >= rect.x && + targetPoint.x <= rect.x + rect.width && + targetPoint.y >= rect.y && + targetPoint.y <= rect.y + rect.height; + }; + + const auto resolveInsertionIndex = [&](const Widgets::UIEditorDockHostTabStackLayout& tabStack) { + if (!isPointInsideRect(tabStack.tabStripLayout.headerRect, point)) { + return Widgets::UIEditorTabStripInvalidIndex; + } + + std::size_t insertionIndex = 0u; + for (const UIRect& rect : tabStack.tabStripLayout.tabHeaderRects) { + const float midpoint = rect.x + rect.width * 0.5f; + if (point.x > midpoint) { + ++insertionIndex; + } + } + return insertionIndex; + }; + + const auto resolvePlacement = [&](const Widgets::UIEditorDockHostTabStackLayout& tabStack) { + if (isPointInsideRect(tabStack.tabStripLayout.headerRect, point)) { + return UIEditorWorkspaceDockPlacement::Center; + } + + const float leftDistance = point.x - tabStack.bounds.x; + const float rightDistance = tabStack.bounds.x + tabStack.bounds.width - point.x; + const float topDistance = point.y - tabStack.bounds.y; + const float bottomDistance = tabStack.bounds.y + tabStack.bounds.height - point.y; + const float minHorizontalThreshold = tabStack.bounds.width * 0.25f; + const float minVerticalThreshold = tabStack.bounds.height * 0.25f; + const float nearestEdge = (std::min)( + (std::min)(leftDistance, rightDistance), + (std::min)(topDistance, bottomDistance)); + + if (nearestEdge == leftDistance && leftDistance <= minHorizontalThreshold) { + return UIEditorWorkspaceDockPlacement::Left; + } + if (nearestEdge == rightDistance && rightDistance <= minHorizontalThreshold) { + return UIEditorWorkspaceDockPlacement::Right; + } + if (nearestEdge == topDistance && topDistance <= minVerticalThreshold) { + return UIEditorWorkspaceDockPlacement::Top; + } + if (nearestEdge == bottomDistance && bottomDistance <= minVerticalThreshold) { + return UIEditorWorkspaceDockPlacement::Bottom; + } + + return UIEditorWorkspaceDockPlacement::Center; + }; + + outTarget = {}; + if (!isPointInsideRect(layout.bounds, point)) { + return false; + } + + const Widgets::UIEditorDockHostHitTarget hitTarget = + Widgets::HitTestUIEditorDockHost(layout, point); + for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { + if ((!hitTarget.nodeId.empty() && tabStack.nodeId != hitTarget.nodeId) || + !isPointInsideRect(tabStack.bounds, point)) { + continue; + } + + outTarget.valid = true; + outTarget.nodeId = tabStack.nodeId; + outTarget.placement = resolvePlacement(tabStack); + if (outTarget.placement == UIEditorWorkspaceDockPlacement::Center) { + outTarget.insertionIndex = resolveInsertionIndex(tabStack); + if (outTarget.insertionIndex == Widgets::UIEditorTabStripInvalidIndex) { + outTarget.insertionIndex = tabStack.items.size(); + } + } + return true; + } + + for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { + if (isPointInsideRect(tabStack.bounds, point)) { + outTarget.valid = true; + outTarget.nodeId = tabStack.nodeId; + outTarget.placement = resolvePlacement(tabStack); + if (outTarget.placement == UIEditorWorkspaceDockPlacement::Center) { + outTarget.insertionIndex = tabStack.items.size(); + } + return true; + } + } + + return false; +} + +} // namespace XCEngine::UI::Editor::App::EditorWindowManagerCrossWindowDropSupport diff --git a/new_editor/app/Platform/Win32/EditorWindowManagerLookup.cpp b/new_editor/app/Platform/Win32/EditorWindowManagerLookup.cpp index e7748429..a5c0d673 100644 --- a/new_editor/app/Platform/Win32/EditorWindowManagerLookup.cpp +++ b/new_editor/app/Platform/Win32/EditorWindowManagerLookup.cpp @@ -54,4 +54,45 @@ const EditorWindow* EditorWindowManager::FindPrimaryWindow() const { return const_cast(this)->FindPrimaryWindow(); } +EditorWindow* EditorWindowManager::FindTopmostWindowAtScreenPoint( + const POINT& screenPoint, + std::string_view excludedWindowId) { + if (const HWND hitWindow = WindowFromPoint(screenPoint); hitWindow != nullptr) { + const HWND rootWindow = GetAncestor(hitWindow, GA_ROOT); + if (EditorWindow* window = FindWindow(rootWindow); + window != nullptr && + window->GetWindowId() != excludedWindowId) { + return window; + } + } + + for (auto it = m_windows.rbegin(); it != m_windows.rend(); ++it) { + EditorWindow* const window = it->get(); + if (window == nullptr || + window->GetHwnd() == nullptr || + window->GetWindowId() == excludedWindowId) { + continue; + } + + RECT windowRect = {}; + if (GetWindowRect(window->GetHwnd(), &windowRect) && + screenPoint.x >= windowRect.left && + screenPoint.x < windowRect.right && + screenPoint.y >= windowRect.top && + screenPoint.y < windowRect.bottom) { + return window; + } + } + + return nullptr; +} + +const EditorWindow* EditorWindowManager::FindTopmostWindowAtScreenPoint( + const POINT& screenPoint, + std::string_view excludedWindowId) const { + return const_cast(this)->FindTopmostWindowAtScreenPoint( + screenPoint, + excludedWindowId); +} + } // namespace XCEngine::UI::Editor::App