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

358 lines
13 KiB
C++
Raw Normal View History

#include "Platform/Win32/EditorWindow.h"
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Types.h>
#include <XCEditor/Shell/UIEditorShellCapturePolicy.h>
#include <windowsx.h>
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;
}
std::int32_t MapVirtualKeyToUIKeyCode(WPARAM wParam) {
using ::XCEngine::Input::KeyCode;
switch (wParam) {
case 'A': return static_cast<std::int32_t>(KeyCode::A);
case 'B': return static_cast<std::int32_t>(KeyCode::B);
case 'C': return static_cast<std::int32_t>(KeyCode::C);
case 'D': return static_cast<std::int32_t>(KeyCode::D);
case 'E': return static_cast<std::int32_t>(KeyCode::E);
case 'F': return static_cast<std::int32_t>(KeyCode::F);
case 'G': return static_cast<std::int32_t>(KeyCode::G);
case 'H': return static_cast<std::int32_t>(KeyCode::H);
case 'I': return static_cast<std::int32_t>(KeyCode::I);
case 'J': return static_cast<std::int32_t>(KeyCode::J);
case 'K': return static_cast<std::int32_t>(KeyCode::K);
case 'L': return static_cast<std::int32_t>(KeyCode::L);
case 'M': return static_cast<std::int32_t>(KeyCode::M);
case 'N': return static_cast<std::int32_t>(KeyCode::N);
case 'O': return static_cast<std::int32_t>(KeyCode::O);
case 'P': return static_cast<std::int32_t>(KeyCode::P);
case 'Q': return static_cast<std::int32_t>(KeyCode::Q);
case 'R': return static_cast<std::int32_t>(KeyCode::R);
case 'S': return static_cast<std::int32_t>(KeyCode::S);
case 'T': return static_cast<std::int32_t>(KeyCode::T);
case 'U': return static_cast<std::int32_t>(KeyCode::U);
case 'V': return static_cast<std::int32_t>(KeyCode::V);
case 'W': return static_cast<std::int32_t>(KeyCode::W);
case 'X': return static_cast<std::int32_t>(KeyCode::X);
case 'Y': return static_cast<std::int32_t>(KeyCode::Y);
case 'Z': return static_cast<std::int32_t>(KeyCode::Z);
case '0': return static_cast<std::int32_t>(KeyCode::Zero);
case '1': return static_cast<std::int32_t>(KeyCode::One);
case '2': return static_cast<std::int32_t>(KeyCode::Two);
case '3': return static_cast<std::int32_t>(KeyCode::Three);
case '4': return static_cast<std::int32_t>(KeyCode::Four);
case '5': return static_cast<std::int32_t>(KeyCode::Five);
case '6': return static_cast<std::int32_t>(KeyCode::Six);
case '7': return static_cast<std::int32_t>(KeyCode::Seven);
case '8': return static_cast<std::int32_t>(KeyCode::Eight);
case '9': return static_cast<std::int32_t>(KeyCode::Nine);
case VK_SPACE: return static_cast<std::int32_t>(KeyCode::Space);
case VK_TAB: return static_cast<std::int32_t>(KeyCode::Tab);
case VK_RETURN: return static_cast<std::int32_t>(KeyCode::Enter);
case VK_ESCAPE: return static_cast<std::int32_t>(KeyCode::Escape);
case VK_SHIFT: return static_cast<std::int32_t>(KeyCode::LeftShift);
case VK_CONTROL: return static_cast<std::int32_t>(KeyCode::LeftCtrl);
case VK_MENU: return static_cast<std::int32_t>(KeyCode::LeftAlt);
case VK_UP: return static_cast<std::int32_t>(KeyCode::Up);
case VK_DOWN: return static_cast<std::int32_t>(KeyCode::Down);
case VK_LEFT: return static_cast<std::int32_t>(KeyCode::Left);
case VK_RIGHT: return static_cast<std::int32_t>(KeyCode::Right);
case VK_HOME: return static_cast<std::int32_t>(KeyCode::Home);
case VK_END: return static_cast<std::int32_t>(KeyCode::End);
case VK_PRIOR: return static_cast<std::int32_t>(KeyCode::PageUp);
case VK_NEXT: return static_cast<std::int32_t>(KeyCode::PageDown);
case VK_DELETE: return static_cast<std::int32_t>(KeyCode::Delete);
case VK_BACK: return static_cast<std::int32_t>(KeyCode::Backspace);
case VK_F1: return static_cast<std::int32_t>(KeyCode::F1);
case VK_F2: return static_cast<std::int32_t>(KeyCode::F2);
case VK_F3: return static_cast<std::int32_t>(KeyCode::F3);
case VK_F4: return static_cast<std::int32_t>(KeyCode::F4);
case VK_F5: return static_cast<std::int32_t>(KeyCode::F5);
case VK_F6: return static_cast<std::int32_t>(KeyCode::F6);
case VK_F7: return static_cast<std::int32_t>(KeyCode::F7);
case VK_F8: return static_cast<std::int32_t>(KeyCode::F8);
case VK_F9: return static_cast<std::int32_t>(KeyCode::F9);
case VK_F10: return static_cast<std::int32_t>(KeyCode::F10);
case VK_F11: return static_cast<std::int32_t>(KeyCode::F11);
case VK_F12: return static_cast<std::int32_t>(KeyCode::F12);
default: return static_cast<std::int32_t>(KeyCode::None);
}
}
bool IsRepeatKeyMessage(LPARAM lParam) {
return (static_cast<unsigned long>(lParam) & (1ul << 30)) != 0ul;
}
} // 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_composition.shellRuntime.HasInteractiveCapture() ||
m_chrome.runtime.IsBorderlessWindowDragRestoreArmed() ||
m_chrome.runtime.IsBorderlessResizeActive() ||
m_input.pointerCaptureOwner != EditorWindowPointerCaptureOwner::None;
}
EditorWindowPointerCaptureOwner EditorWindow::GetPointerCaptureOwner() const {
return m_input.pointerCaptureOwner;
}
bool EditorWindow::OwnsPointerCapture(EditorWindowPointerCaptureOwner owner) const {
return m_input.pointerCaptureOwner == owner;
}
void EditorWindow::AcquirePointerCapture(EditorWindowPointerCaptureOwner owner) {
if (owner == EditorWindowPointerCaptureOwner::None ||
m_window.hwnd == nullptr ||
!IsWindow(m_window.hwnd)) {
return;
}
m_input.pointerCaptureOwner = owner;
if (GetCapture() != m_window.hwnd) {
SetCapture(m_window.hwnd);
}
}
void EditorWindow::ReleasePointerCapture(EditorWindowPointerCaptureOwner owner) {
if (m_input.pointerCaptureOwner != owner) {
return;
}
m_input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
if (m_window.hwnd != nullptr && GetCapture() == m_window.hwnd) {
ReleaseCapture();
}
}
void EditorWindow::ForceReleasePointerCapture() {
m_input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
if (m_window.hwnd != nullptr && GetCapture() == m_window.hwnd) {
ReleaseCapture();
}
}
void EditorWindow::ClearPointerCaptureOwner() {
m_input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
}
void EditorWindow::TryStartImmediateShellPointerCapture(LPARAM lParam) {
if (m_window.hwnd == nullptr ||
!IsWindow(m_window.hwnd) ||
GetCapture() == m_window.hwnd) {
return;
}
const ::XCEngine::UI::UIPoint clientPoint = ConvertClientPixelsToDips(
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
if (!ShouldStartImmediateUIEditorShellPointerCapture(
m_composition.shellRuntime.GetShellFrame(),
clientPoint)) {
return;
}
AcquirePointerCapture(EditorWindowPointerCaptureOwner::Shell);
}
void EditorWindow::QueuePointerEvent(
UIInputEventType type,
UIPointerButton button,
WPARAM wParam,
LPARAM lParam) {
UIInputEvent event = {};
event.type = type;
event.pointerButton = button;
event.position = ConvertClientPixelsToDips(
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
event.modifiers = m_input.modifierTracker.ApplyPointerMessage(
type,
button,
static_cast<std::size_t>(wParam));
m_input.pendingEvents.push_back(event);
}
void EditorWindow::QueueSyntheticPointerStateSyncEvent(
const ::XCEngine::UI::UIInputModifiers& modifiers) {
if (m_window.hwnd == nullptr || !IsWindow(m_window.hwnd)) {
return;
}
POINT screenPoint = {};
if (!GetCursorPos(&screenPoint)) {
return;
}
if (!ScreenToClient(m_window.hwnd, &screenPoint)) {
return;
}
UIInputEvent event = {};
event.type = UIInputEventType::PointerMove;
event.position = ConvertClientPixelsToDips(screenPoint.x, screenPoint.y);
event.modifiers = modifiers;
m_input.pendingEvents.push_back(event);
}
void EditorWindow::QueuePointerLeaveEvent() {
UIInputEvent event = {};
event.type = UIInputEventType::PointerLeave;
if (m_window.hwnd != nullptr) {
POINT clientPoint = {};
GetCursorPos(&clientPoint);
ScreenToClient(m_window.hwnd, &clientPoint);
event.position = ConvertClientPixelsToDips(clientPoint.x, clientPoint.y);
}
event.modifiers = m_input.modifierTracker.GetCurrentModifiers();
m_input.pendingEvents.push_back(event);
}
void EditorWindow::QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM lParam) {
if (m_window.hwnd == nullptr) {
return;
}
POINT screenPoint = {
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)
};
ScreenToClient(m_window.hwnd, &screenPoint);
UIInputEvent event = {};
event.type = UIInputEventType::PointerWheel;
event.position = ConvertClientPixelsToDips(screenPoint.x, screenPoint.y);
event.wheelDelta = static_cast<float>(wheelDelta);
event.modifiers = m_input.modifierTracker.ApplyPointerMessage(
UIInputEventType::PointerWheel,
UIPointerButton::None,
static_cast<std::size_t>(wParam));
m_input.pendingEvents.push_back(event);
}
void EditorWindow::QueueKeyEvent(UIInputEventType type, WPARAM wParam, LPARAM lParam) {
UIInputEvent event = {};
event.type = type;
event.keyCode = MapVirtualKeyToUIKeyCode(wParam);
event.modifiers = m_input.modifierTracker.ApplyKeyMessage(type, wParam, lParam);
event.repeat = IsRepeatKeyMessage(lParam);
m_input.pendingEvents.push_back(event);
}
void EditorWindow::QueueCharacterEvent(WPARAM wParam, LPARAM) {
UIInputEvent event = {};
event.type = UIInputEventType::Character;
event.character = static_cast<std::uint32_t>(wParam);
event.modifiers = m_input.modifierTracker.GetCurrentModifiers();
m_input.pendingEvents.push_back(event);
}
void EditorWindow::QueueWindowFocusEvent(UIInputEventType type) {
UIInputEvent event = {};
event.type = type;
m_input.pendingEvents.push_back(event);
}
void EditorWindow::SyncInputModifiersFromSystemState() {
m_input.modifierTracker.SyncFromSystemState();
}
void EditorWindow::ResetInputModifiers() {
m_input.modifierTracker.Reset();
}
void EditorWindow::RequestManualScreenshot() {
m_render.autoScreenshot.RequestCapture("manual_f12");
}
bool EditorWindow::IsPointerInsideClientArea() const {
if (m_window.hwnd == nullptr || !IsWindow(m_window.hwnd)) {
return false;
}
POINT screenPoint = {};
if (!GetCursorPos(&screenPoint)) {
return false;
}
if (!IsScreenPointOverWindow(m_window.hwnd, screenPoint)) {
return false;
}
const LPARAM pointParam = MAKELPARAM(
static_cast<SHORT>(screenPoint.x),
static_cast<SHORT>(screenPoint.y));
return SendMessageW(m_window.hwnd, WM_NCHITTEST, 0, pointParam) == HTCLIENT;
}
LPCWSTR EditorWindow::ResolveCurrentCursorResource() const {
const Host::BorderlessWindowResizeEdge borderlessResizeEdge =
m_chrome.runtime.IsBorderlessResizeActive()
? m_chrome.runtime.GetBorderlessResizeEdge()
: m_chrome.runtime.GetHoveredBorderlessResizeEdge();
if (borderlessResizeEdge != Host::BorderlessWindowResizeEdge::None) {
return Host::ResolveBorderlessWindowResizeCursor(borderlessResizeEdge);
}
switch (m_composition.shellRuntime.GetHostedContentCursorKind()) {
case ProjectPanel::CursorKind::ResizeEW:
return IDC_SIZEWE;
case ProjectPanel::CursorKind::Arrow:
default:
break;
}
switch (m_composition.shellRuntime.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