Add XCUI new editor sandbox phase 1
This commit is contained in:
596
new_editor/src/XCUIBackend/XCUIInputBridge.cpp
Normal file
596
new_editor/src/XCUIBackend/XCUIInputBridge.cpp
Normal file
@@ -0,0 +1,596 @@
|
||||
#include "XCUIBackend/XCUIInputBridge.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace XCUIBackend {
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Input::KeyCode;
|
||||
using XCEngine::UI::UIInputEvent;
|
||||
using XCEngine::UI::UIInputEventType;
|
||||
using XCEngine::UI::UIPointerButton;
|
||||
using XCEngine::UI::UIPoint;
|
||||
|
||||
UIPoint Subtract(const UIPoint& lhs, const UIPoint& rhs) {
|
||||
return UIPoint(lhs.x - rhs.x, lhs.y - rhs.y);
|
||||
}
|
||||
|
||||
UIPointerButton ToPointerButton(std::size_t index) {
|
||||
switch (index) {
|
||||
case 0u:
|
||||
return UIPointerButton::Left;
|
||||
case 1u:
|
||||
return UIPointerButton::Right;
|
||||
case 2u:
|
||||
return UIPointerButton::Middle;
|
||||
case 3u:
|
||||
return UIPointerButton::X1;
|
||||
case 4u:
|
||||
return UIPointerButton::X2;
|
||||
default:
|
||||
return UIPointerButton::None;
|
||||
}
|
||||
}
|
||||
|
||||
void AppendEvent(std::vector<UIInputEvent>& events, const UIInputEvent& event) {
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
UIInputEvent MakeBaseEvent(
|
||||
UIInputEventType type,
|
||||
const XCUIInputBridgeFrameSnapshot& snapshot) {
|
||||
UIInputEvent event = {};
|
||||
event.type = type;
|
||||
event.position = snapshot.pointerPosition;
|
||||
event.timestampNanoseconds = snapshot.timestampNanoseconds;
|
||||
event.modifiers = snapshot.modifiers;
|
||||
return event;
|
||||
}
|
||||
|
||||
void AppendUniqueKeyCode(std::vector<std::int32_t>& keyCodes, std::int32_t keyCode) {
|
||||
if (keyCode == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto it = std::find(keyCodes.begin(), keyCodes.end(), keyCode);
|
||||
if (it == keyCodes.end()) {
|
||||
keyCodes.push_back(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPointerPositionValid(const ImVec2& position) {
|
||||
return std::isfinite(position.x) &&
|
||||
std::isfinite(position.y) &&
|
||||
position.x > -std::numeric_limits<float>::max() * 0.5f &&
|
||||
position.y > -std::numeric_limits<float>::max() * 0.5f;
|
||||
}
|
||||
|
||||
std::uint64_t GetTimestampNanoseconds() {
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
|
||||
UIPoint MakeClientPoint(LPARAM lParam) {
|
||||
return UIPoint(
|
||||
static_cast<float>(static_cast<short>(LOWORD(static_cast<DWORD_PTR>(lParam)))),
|
||||
static_cast<float>(static_cast<short>(HIWORD(static_cast<DWORD_PTR>(lParam)))));
|
||||
}
|
||||
|
||||
std::int32_t TranslateVirtualKeyToXCUIKeyCode(WPARAM wParam, LPARAM lParam) {
|
||||
switch (static_cast<UINT>(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: {
|
||||
const UINT scanCode = (static_cast<UINT>(lParam) >> 16) & 0xFFu;
|
||||
const UINT leftShiftScanCode = MapVirtualKeyW(VK_LSHIFT, MAPVK_VK_TO_VSC);
|
||||
return static_cast<std::int32_t>(
|
||||
scanCode == leftShiftScanCode ? KeyCode::LeftShift : KeyCode::RightShift);
|
||||
}
|
||||
case VK_CONTROL:
|
||||
return static_cast<std::int32_t>(
|
||||
(static_cast<UINT>(lParam) & 0x01000000u) != 0u ? KeyCode::RightCtrl : KeyCode::LeftCtrl);
|
||||
case VK_MENU:
|
||||
return static_cast<std::int32_t>(
|
||||
(static_cast<UINT>(lParam) & 0x01000000u) != 0u ? KeyCode::RightAlt : 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);
|
||||
case VK_OEM_MINUS: return static_cast<std::int32_t>(KeyCode::Minus);
|
||||
case VK_OEM_PLUS: return static_cast<std::int32_t>(KeyCode::Equals);
|
||||
case VK_OEM_4: return static_cast<std::int32_t>(KeyCode::BracketLeft);
|
||||
case VK_OEM_6: return static_cast<std::int32_t>(KeyCode::BracketRight);
|
||||
case VK_OEM_1: return static_cast<std::int32_t>(KeyCode::Semicolon);
|
||||
case VK_OEM_7: return static_cast<std::int32_t>(KeyCode::Quote);
|
||||
case VK_OEM_COMMA: return static_cast<std::int32_t>(KeyCode::Comma);
|
||||
case VK_OEM_PERIOD: return static_cast<std::int32_t>(KeyCode::Period);
|
||||
case VK_OEM_2: return static_cast<std::int32_t>(KeyCode::Slash);
|
||||
case VK_OEM_5: return static_cast<std::int32_t>(KeyCode::Backslash);
|
||||
case VK_OEM_3: return static_cast<std::int32_t>(KeyCode::Backtick);
|
||||
default:
|
||||
return static_cast<std::int32_t>(KeyCode::None);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const XCUIInputBridgeKeyState* XCUIInputBridgeFrameSnapshot::FindKeyState(std::int32_t keyCode) const {
|
||||
for (const XCUIInputBridgeKeyState& keyState : keys) {
|
||||
if (keyState.keyCode == keyCode) {
|
||||
return &keyState;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool XCUIInputBridgeFrameSnapshot::IsKeyDown(std::int32_t keyCode) const {
|
||||
const XCUIInputBridgeKeyState* keyState = FindKeyState(keyCode);
|
||||
return keyState != nullptr && keyState->down;
|
||||
}
|
||||
|
||||
bool XCUIInputBridgeFrameDelta::HasEvents() const {
|
||||
return !events.empty();
|
||||
}
|
||||
|
||||
bool XCUIInputBridgeFrameDelta::HasPointerActivity() const {
|
||||
return pointer.moved ||
|
||||
pointer.entered ||
|
||||
pointer.left ||
|
||||
pointer.wheelDelta.x != 0.0f ||
|
||||
pointer.wheelDelta.y != 0.0f ||
|
||||
std::any_of(pointer.pressed.begin(), pointer.pressed.end(), [](bool value) { return value; }) ||
|
||||
std::any_of(pointer.released.begin(), pointer.released.end(), [](bool value) { return value; });
|
||||
}
|
||||
|
||||
bool XCUIInputBridgeFrameDelta::HasKeyboardActivity() const {
|
||||
return !keyboard.pressedKeys.empty() ||
|
||||
!keyboard.releasedKeys.empty() ||
|
||||
!keyboard.repeatedKeys.empty() ||
|
||||
!keyboard.characters.empty();
|
||||
}
|
||||
|
||||
bool XCUIInputBridgeFrameDelta::HasEventType(UI::UIInputEventType type) const {
|
||||
return std::any_of(
|
||||
events.begin(),
|
||||
events.end(),
|
||||
[type](const UIInputEvent& event) {
|
||||
return event.type == type;
|
||||
});
|
||||
}
|
||||
|
||||
void XCUIInputBridge::Reset() {
|
||||
m_hasBaseline = false;
|
||||
m_baseline = {};
|
||||
}
|
||||
|
||||
void XCUIInputBridge::Prime(const XCUIInputBridgeFrameSnapshot& snapshot) {
|
||||
m_baseline = snapshot;
|
||||
m_hasBaseline = true;
|
||||
}
|
||||
|
||||
XCUIInputBridgeFrameDelta XCUIInputBridge::Translate(const XCUIInputBridgeFrameSnapshot& current) {
|
||||
const XCUIInputBridgeFrameSnapshot previous = m_hasBaseline ? m_baseline : XCUIInputBridgeFrameSnapshot();
|
||||
XCUIInputBridgeFrameDelta delta = Translate(previous, current);
|
||||
m_baseline = current;
|
||||
m_hasBaseline = true;
|
||||
return delta;
|
||||
}
|
||||
|
||||
XCUIInputBridgeFrameDelta XCUIInputBridge::Translate(
|
||||
const XCUIInputBridgeFrameSnapshot& previous,
|
||||
const XCUIInputBridgeFrameSnapshot& current) {
|
||||
XCUIInputBridgeFrameDelta delta = {};
|
||||
delta.state = current;
|
||||
delta.pointer.wheelDelta = current.wheelDelta;
|
||||
delta.keyboard.characters = current.characters;
|
||||
|
||||
delta.focusGained = current.windowFocused && !previous.windowFocused;
|
||||
delta.focusLost = previous.windowFocused && !current.windowFocused;
|
||||
|
||||
if (delta.focusGained) {
|
||||
AppendEvent(delta.events, MakeBaseEvent(UIInputEventType::FocusGained, current));
|
||||
}
|
||||
if (delta.focusLost) {
|
||||
AppendEvent(delta.events, MakeBaseEvent(UIInputEventType::FocusLost, current));
|
||||
}
|
||||
|
||||
delta.pointer.entered = current.pointerInside && !previous.pointerInside;
|
||||
delta.pointer.left = previous.pointerInside && !current.pointerInside;
|
||||
if (previous.pointerInside && current.pointerInside) {
|
||||
delta.pointer.delta = Subtract(current.pointerPosition, previous.pointerPosition);
|
||||
}
|
||||
|
||||
const bool pointerMovedInside =
|
||||
current.pointerInside &&
|
||||
(delta.pointer.entered ||
|
||||
previous.pointerPosition.x != current.pointerPosition.x ||
|
||||
previous.pointerPosition.y != current.pointerPosition.y);
|
||||
delta.pointer.moved = pointerMovedInside;
|
||||
|
||||
if (delta.pointer.entered) {
|
||||
AppendEvent(delta.events, MakeBaseEvent(UIInputEventType::PointerEnter, current));
|
||||
}
|
||||
if (delta.pointer.moved) {
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::PointerMove, current);
|
||||
event.delta = delta.pointer.delta;
|
||||
AppendEvent(delta.events, event);
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < XCUIInputBridgeFrameSnapshot::PointerButtonCount; ++index) {
|
||||
const bool wasDown = previous.pointerButtonsDown[index];
|
||||
const bool isDown = current.pointerButtonsDown[index];
|
||||
if (isDown == wasDown) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIInputEvent event = MakeBaseEvent(
|
||||
isDown ? UIInputEventType::PointerButtonDown : UIInputEventType::PointerButtonUp,
|
||||
current);
|
||||
event.pointerButton = ToPointerButton(index);
|
||||
|
||||
if (isDown) {
|
||||
delta.pointer.pressed[index] = true;
|
||||
} else {
|
||||
delta.pointer.released[index] = true;
|
||||
}
|
||||
|
||||
AppendEvent(delta.events, event);
|
||||
}
|
||||
|
||||
if (current.wheelDelta.x != 0.0f || current.wheelDelta.y != 0.0f) {
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::PointerWheel, current);
|
||||
event.delta = current.wheelDelta;
|
||||
event.wheelDelta = current.wheelDelta.y != 0.0f ? current.wheelDelta.y : current.wheelDelta.x;
|
||||
AppendEvent(delta.events, event);
|
||||
}
|
||||
|
||||
if (delta.pointer.left) {
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::PointerLeave, current);
|
||||
if (previous.pointerInside) {
|
||||
event.delta = Subtract(current.pointerPosition, previous.pointerPosition);
|
||||
}
|
||||
AppendEvent(delta.events, event);
|
||||
}
|
||||
|
||||
std::vector<std::int32_t> keyCodes = {};
|
||||
keyCodes.reserve(previous.keys.size() + current.keys.size());
|
||||
for (const XCUIInputBridgeKeyState& keyState : previous.keys) {
|
||||
AppendUniqueKeyCode(keyCodes, keyState.keyCode);
|
||||
}
|
||||
for (const XCUIInputBridgeKeyState& keyState : current.keys) {
|
||||
AppendUniqueKeyCode(keyCodes, keyState.keyCode);
|
||||
}
|
||||
std::sort(keyCodes.begin(), keyCodes.end());
|
||||
|
||||
for (std::int32_t keyCode : keyCodes) {
|
||||
const XCUIInputBridgeKeyState* previousKeyState = previous.FindKeyState(keyCode);
|
||||
const XCUIInputBridgeKeyState* currentKeyState = current.FindKeyState(keyCode);
|
||||
const bool wasDown = previousKeyState != nullptr && previousKeyState->down;
|
||||
const bool isDown = currentKeyState != nullptr && currentKeyState->down;
|
||||
|
||||
if (isDown && !wasDown) {
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::KeyDown, current);
|
||||
event.keyCode = keyCode;
|
||||
delta.keyboard.pressedKeys.push_back(keyCode);
|
||||
AppendEvent(delta.events, event);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isDown && wasDown) {
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::KeyUp, current);
|
||||
event.keyCode = keyCode;
|
||||
delta.keyboard.releasedKeys.push_back(keyCode);
|
||||
AppendEvent(delta.events, event);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDown && wasDown && currentKeyState != nullptr && currentKeyState->repeat) {
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::KeyDown, current);
|
||||
event.keyCode = keyCode;
|
||||
event.repeat = true;
|
||||
delta.keyboard.repeatedKeys.push_back(keyCode);
|
||||
AppendEvent(delta.events, event);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::uint32_t character : current.characters) {
|
||||
if (character == 0u) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIInputEvent event = MakeBaseEvent(UIInputEventType::Character, current);
|
||||
event.character = character;
|
||||
AppendEvent(delta.events, event);
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::Reset() {
|
||||
m_pointerPosition = {};
|
||||
m_pointerInside = false;
|
||||
m_windowFocused = false;
|
||||
m_trackingMouseLeave = false;
|
||||
m_pointerButtonsDown.fill(false);
|
||||
m_wheelDelta = {};
|
||||
m_modifiers = {};
|
||||
m_keyStates.clear();
|
||||
m_characters.clear();
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::HandleWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
switch (message) {
|
||||
case WM_SETFOCUS:
|
||||
m_windowFocused = true;
|
||||
UpdateModifierState();
|
||||
return;
|
||||
case WM_KILLFOCUS:
|
||||
m_windowFocused = false;
|
||||
m_pointerInside = false;
|
||||
m_trackingMouseLeave = false;
|
||||
m_pointerButtonsDown.fill(false);
|
||||
m_keyStates.clear();
|
||||
UpdateModifierState();
|
||||
return;
|
||||
case WM_MOUSEMOVE: {
|
||||
m_pointerPosition = MakeClientPoint(lParam);
|
||||
UpdatePointerInside(hwnd, m_pointerPosition.x, m_pointerPosition.y, true);
|
||||
UpdateModifierState();
|
||||
if (hwnd != nullptr && !m_trackingMouseLeave) {
|
||||
TRACKMOUSEEVENT trackMouseEvent = {};
|
||||
trackMouseEvent.cbSize = sizeof(trackMouseEvent);
|
||||
trackMouseEvent.dwFlags = TME_LEAVE;
|
||||
trackMouseEvent.hwndTrack = hwnd;
|
||||
m_trackingMouseLeave = TrackMouseEvent(&trackMouseEvent) == TRUE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case WM_MOUSELEAVE:
|
||||
m_trackingMouseLeave = false;
|
||||
m_pointerInside = false;
|
||||
return;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONDOWN:
|
||||
case WM_XBUTTONUP: {
|
||||
m_pointerPosition = MakeClientPoint(lParam);
|
||||
UpdatePointerInside(hwnd, m_pointerPosition.x, m_pointerPosition.y, true);
|
||||
UpdateModifierState();
|
||||
switch (message) {
|
||||
case WM_LBUTTONDOWN: SetPointerButtonDown(0u, true); return;
|
||||
case WM_LBUTTONUP: SetPointerButtonDown(0u, false); return;
|
||||
case WM_RBUTTONDOWN: SetPointerButtonDown(1u, true); return;
|
||||
case WM_RBUTTONUP: SetPointerButtonDown(1u, false); return;
|
||||
case WM_MBUTTONDOWN: SetPointerButtonDown(2u, true); return;
|
||||
case WM_MBUTTONUP: SetPointerButtonDown(2u, false); return;
|
||||
case WM_XBUTTONDOWN:
|
||||
SetPointerButtonDown(HIWORD(wParam) == XBUTTON1 ? 3u : 4u, true);
|
||||
return;
|
||||
case WM_XBUTTONUP:
|
||||
SetPointerButtonDown(HIWORD(wParam) == XBUTTON1 ? 3u : 4u, false);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL: {
|
||||
POINT screenPoint = {
|
||||
static_cast<LONG>(static_cast<short>(LOWORD(static_cast<DWORD_PTR>(lParam)))),
|
||||
static_cast<LONG>(static_cast<short>(HIWORD(static_cast<DWORD_PTR>(lParam))))
|
||||
};
|
||||
if (hwnd != nullptr && ScreenToClient(hwnd, &screenPoint)) {
|
||||
m_pointerPosition = UIPoint(static_cast<float>(screenPoint.x), static_cast<float>(screenPoint.y));
|
||||
UpdatePointerInside(hwnd, m_pointerPosition.x, m_pointerPosition.y, false);
|
||||
}
|
||||
const float wheelStep = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wParam)) / static_cast<float>(WHEEL_DELTA);
|
||||
if (message == WM_MOUSEHWHEEL) {
|
||||
m_wheelDelta.x += wheelStep;
|
||||
} else {
|
||||
m_wheelDelta.y += wheelStep;
|
||||
}
|
||||
UpdateModifierState();
|
||||
return;
|
||||
}
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN: {
|
||||
UpdateModifierState();
|
||||
const std::int32_t keyCode = TranslateVirtualKeyToXCUIKeyCode(wParam, lParam);
|
||||
const bool repeat = (static_cast<UINT>(lParam) & 0x40000000u) != 0u;
|
||||
SetKeyDown(keyCode, true, repeat);
|
||||
UpdateModifierState();
|
||||
return;
|
||||
}
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
SetKeyDown(TranslateVirtualKeyToXCUIKeyCode(wParam, lParam), false, false);
|
||||
UpdateModifierState();
|
||||
return;
|
||||
case WM_CHAR:
|
||||
case WM_SYSCHAR:
|
||||
if (wParam != 0u) {
|
||||
m_characters.push_back(static_cast<std::uint32_t>(wParam));
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::ClearFrameTransients() {
|
||||
m_wheelDelta = {};
|
||||
m_characters.clear();
|
||||
for (XCUIInputBridgeKeyState& keyState : m_keyStates) {
|
||||
keyState.repeat = false;
|
||||
}
|
||||
}
|
||||
|
||||
XCUIInputBridgeFrameSnapshot XCUIWin32InputSource::CaptureSnapshot(
|
||||
const XCUIInputBridgeCaptureOptions& options) const {
|
||||
XCUIInputBridgeFrameSnapshot snapshot = {};
|
||||
snapshot.pointerPosition = UIPoint(
|
||||
m_pointerPosition.x - options.pointerOffset.x,
|
||||
m_pointerPosition.y - options.pointerOffset.y);
|
||||
snapshot.pointerInside = options.hasPointerInsideOverride ? options.pointerInsideOverride : m_pointerInside;
|
||||
snapshot.pointerButtonsDown = m_pointerButtonsDown;
|
||||
snapshot.wheelDelta = m_wheelDelta;
|
||||
snapshot.modifiers = m_modifiers;
|
||||
snapshot.windowFocused = options.windowFocused && m_windowFocused;
|
||||
snapshot.wantCaptureMouse = false;
|
||||
snapshot.wantCaptureKeyboard = false;
|
||||
snapshot.wantTextInput = false;
|
||||
snapshot.timestampNanoseconds =
|
||||
options.timestampNanoseconds != 0u ? options.timestampNanoseconds : GetTimestampNanoseconds();
|
||||
snapshot.keys = m_keyStates;
|
||||
snapshot.characters = m_characters;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::UpdateModifierState() {
|
||||
m_modifiers.shift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
m_modifiers.control = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||
m_modifiers.alt = (GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
m_modifiers.super =
|
||||
(GetKeyState(VK_LWIN) & 0x8000) != 0 ||
|
||||
(GetKeyState(VK_RWIN) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::UpdatePointerInside(
|
||||
HWND hwnd,
|
||||
float x,
|
||||
float y,
|
||||
bool assumeInsideIfUnknown) {
|
||||
if (hwnd == nullptr) {
|
||||
m_pointerInside = assumeInsideIfUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
RECT clientRect = {};
|
||||
if (!GetClientRect(hwnd, &clientRect)) {
|
||||
m_pointerInside = assumeInsideIfUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
m_pointerInside =
|
||||
x >= static_cast<float>(clientRect.left) &&
|
||||
y >= static_cast<float>(clientRect.top) &&
|
||||
x < static_cast<float>(clientRect.right) &&
|
||||
y < static_cast<float>(clientRect.bottom);
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::SetPointerButtonDown(std::size_t index, bool down) {
|
||||
if (index < m_pointerButtonsDown.size()) {
|
||||
m_pointerButtonsDown[index] = down;
|
||||
}
|
||||
}
|
||||
|
||||
void XCUIWin32InputSource::SetKeyDown(std::int32_t keyCode, bool down, bool repeat) {
|
||||
if (keyCode == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto it = std::find_if(
|
||||
m_keyStates.begin(),
|
||||
m_keyStates.end(),
|
||||
[keyCode](const XCUIInputBridgeKeyState& state) {
|
||||
return state.keyCode == keyCode;
|
||||
});
|
||||
|
||||
if (!down) {
|
||||
if (it != m_keyStates.end()) {
|
||||
m_keyStates.erase(it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (it != m_keyStates.end()) {
|
||||
it->down = true;
|
||||
it->repeat = it->repeat || repeat;
|
||||
return;
|
||||
}
|
||||
|
||||
XCUIInputBridgeKeyState state = {};
|
||||
state.keyCode = keyCode;
|
||||
state.down = true;
|
||||
state.repeat = repeat;
|
||||
m_keyStates.push_back(state);
|
||||
std::sort(
|
||||
m_keyStates.begin(),
|
||||
m_keyStates.end(),
|
||||
[](const XCUIInputBridgeKeyState& lhs, const XCUIInputBridgeKeyState& rhs) {
|
||||
return lhs.keyCode < rhs.keyCode;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace XCUIBackend
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user