#include "Platform/Win32/EditorWindow.h" #include "Platform/Win32/EditorWindowChromeController.h" #include "Platform/Win32/EditorWindowInputController.h" #include "Platform/Win32/EditorWindowInternalState.h" #include "Platform/Win32/EditorWindowRuntimeController.h" #include #include #include namespace XCEngine::UI::Editor::App { using ::XCEngine::UI::UIInputEvent; using ::XCEngine::UI::UIInputEventType; using ::XCEngine::UI::UIPointerButton; namespace { bool IsScreenPointOverWindow(HWND hwnd, const POINT& screenPoint) { if (hwnd == nullptr || !IsWindow(hwnd)) { return false; } const HWND hitWindow = WindowFromPoint(screenPoint); if (hitWindow == nullptr || GetAncestor(hitWindow, GA_ROOT) != hwnd) { return false; } RECT windowRect = {}; if (!GetWindowRect(hwnd, &windowRect)) { return false; } return screenPoint.x >= windowRect.left && screenPoint.x < windowRect.right && screenPoint.y >= windowRect.top && screenPoint.y < windowRect.bottom; } } // namespace bool EditorWindow::ApplyCurrentCursor() const { if (!HasInteractiveCaptureState() && !IsPointerInsideClientArea()) { return false; } const HCURSOR cursor = LoadCursorW(nullptr, ResolveCurrentCursorResource()); if (cursor == nullptr) { return false; } SetCursor(cursor); return true; } bool EditorWindow::HasInteractiveCaptureState() const { return m_runtime->GetShellRuntime().HasInteractiveCapture() || m_chromeController->IsBorderlessWindowDragRestoreArmed() || m_chromeController->IsBorderlessResizeActive() || m_inputController->HasPointerCaptureOwner(); } EditorWindowPointerCaptureOwner EditorWindow::GetPointerCaptureOwner() const { return m_inputController->GetPointerCaptureOwner(); } bool EditorWindow::OwnsPointerCapture(EditorWindowPointerCaptureOwner owner) const { return m_inputController->OwnsPointerCapture(owner); } void EditorWindow::AcquirePointerCapture(EditorWindowPointerCaptureOwner owner) { m_inputController->AcquirePointerCapture(m_state->window.hwnd, owner); } void EditorWindow::ReleasePointerCapture(EditorWindowPointerCaptureOwner owner) { m_inputController->ReleasePointerCapture(m_state->window.hwnd, owner); } void EditorWindow::ForceReleasePointerCapture() { m_inputController->ForceReleasePointerCapture(m_state->window.hwnd); } void EditorWindow::ClearPointerCaptureOwner() { m_inputController->ClearPointerCaptureOwner(); } void EditorWindow::TryStartImmediateShellPointerCapture(LPARAM lParam) { if (m_state->window.hwnd == nullptr || !IsWindow(m_state->window.hwnd) || GetCapture() == m_state->window.hwnd) { return; } const ::XCEngine::UI::UIPoint clientPoint = ConvertClientPixelsToDips( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); if (!ShouldStartImmediateUIEditorShellPointerCapture( m_runtime->GetShellFrame(), clientPoint)) { return; } AcquirePointerCapture(EditorWindowPointerCaptureOwner::Shell); } void EditorWindow::QueuePointerEvent( UIInputEventType type, UIPointerButton button, WPARAM wParam, LPARAM lParam) { UIInputEvent event = {}; m_inputController->QueuePointerEvent( type, button, ConvertClientPixelsToDips(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), wParam); } void EditorWindow::QueueSyntheticPointerStateSyncEvent( const ::XCEngine::UI::UIInputModifiers& modifiers) { if (m_state->window.hwnd == nullptr || !IsWindow(m_state->window.hwnd)) { return; } POINT screenPoint = {}; if (!GetCursorPos(&screenPoint)) { return; } if (!ScreenToClient(m_state->window.hwnd, &screenPoint)) { return; } m_inputController->QueueSyntheticPointerStateSyncEvent( ConvertClientPixelsToDips(screenPoint.x, screenPoint.y), modifiers); } void EditorWindow::QueuePointerLeaveEvent() { ::XCEngine::UI::UIPoint position = {}; if (m_state->window.hwnd != nullptr) { POINT clientPoint = {}; GetCursorPos(&clientPoint); ScreenToClient(m_state->window.hwnd, &clientPoint); position = ConvertClientPixelsToDips(clientPoint.x, clientPoint.y); } m_inputController->QueuePointerLeaveEvent(position); } void EditorWindow::QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM lParam) { if (m_state->window.hwnd == nullptr) { return; } POINT screenPoint = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; ScreenToClient(m_state->window.hwnd, &screenPoint); m_inputController->QueuePointerWheelEvent( ConvertClientPixelsToDips(screenPoint.x, screenPoint.y), wheelDelta, wParam); } void EditorWindow::QueueKeyEvent(UIInputEventType type, WPARAM wParam, LPARAM lParam) { m_inputController->QueueKeyEvent(type, wParam, lParam); } void EditorWindow::QueueCharacterEvent(WPARAM wParam, LPARAM) { m_inputController->QueueCharacterEvent(wParam); } void EditorWindow::QueueWindowFocusEvent(UIInputEventType type) { m_inputController->QueueWindowFocusEvent(type); } void EditorWindow::SyncInputModifiersFromSystemState() { m_inputController->SyncInputModifiersFromSystemState(); } void EditorWindow::ResetInputModifiers() { m_inputController->ResetInputModifiers(); } void EditorWindow::RequestManualScreenshot() { m_runtime->RequestManualScreenshot("manual_f12"); } bool EditorWindow::IsPointerInsideClientArea() const { if (m_state->window.hwnd == nullptr || !IsWindow(m_state->window.hwnd)) { return false; } POINT screenPoint = {}; if (!GetCursorPos(&screenPoint)) { return false; } if (!IsScreenPointOverWindow(m_state->window.hwnd, screenPoint)) { return false; } const LPARAM pointParam = MAKELPARAM( static_cast(screenPoint.x), static_cast(screenPoint.y)); return SendMessageW(m_state->window.hwnd, WM_NCHITTEST, 0, pointParam) == HTCLIENT; } LPCWSTR EditorWindow::ResolveCurrentCursorResource() const { const Host::BorderlessWindowResizeEdge borderlessResizeEdge = m_chromeController->IsBorderlessResizeActive() ? m_chromeController->GetBorderlessResizeEdge() : m_chromeController->GetHoveredBorderlessResizeEdge(); if (borderlessResizeEdge != Host::BorderlessWindowResizeEdge::None) { return Host::ResolveBorderlessWindowResizeCursor(borderlessResizeEdge); } switch (m_runtime->GetShellRuntime().GetHostedContentCursorKind()) { case ProjectPanel::CursorKind::ResizeEW: return IDC_SIZEWE; case ProjectPanel::CursorKind::Arrow: default: break; } switch (m_runtime->GetShellRuntime().GetDockCursorKind()) { case Widgets::UIEditorDockHostCursorKind::ResizeEW: return IDC_SIZEWE; case Widgets::UIEditorDockHostCursorKind::ResizeNS: return IDC_SIZENS; case Widgets::UIEditorDockHostCursorKind::Arrow: default: return IDC_ARROW; } } } // namespace XCEngine::UI::Editor::App