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

395 lines
13 KiB
C++
Raw Normal View History

#include "WindowMessageDispatcher.h"
#include "Platform/Win32/EditorWindow.h"
#include "WindowMessageHost.h"
#include <utility>
namespace XCEngine::UI::Editor::Host {
namespace {
constexpr UINT kMessageNcUaDrawCaption = 0x00AEu;
constexpr UINT kMessageNcUaDrawFrame = 0x00AFu;
} // namespace
struct WindowMessageDispatcher::DispatchContext {
HWND hwnd = nullptr;
WindowMessageHost& windowHost;
App::EditorWindow& window;
};
void WindowMessageDispatcher::RenderAndValidateWindow(const DispatchContext& context) {
if (!context.window.IsRenderReady()) {
return;
}
App::EditorWindowFrameTransferRequests transferRequests = context.window.RenderFrame(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive());
if (transferRequests.HasPendingRequests()) {
context.windowHost.HandleWindowFrameTransferRequests(
context.window,
std::move(transferRequests));
}
if (context.hwnd != nullptr && IsWindow(context.hwnd)) {
ValidateRect(context.hwnd, nullptr);
}
}
bool WindowMessageDispatcher::EnsureTrackingMouseLeave(const DispatchContext& context) {
if (context.window.IsTrackingMouseLeave()) {
return true;
}
TRACKMOUSEEVENT trackMouseEvent = {};
trackMouseEvent.cbSize = sizeof(trackMouseEvent);
trackMouseEvent.dwFlags = TME_LEAVE;
trackMouseEvent.hwndTrack = context.hwnd;
if (!TrackMouseEvent(&trackMouseEvent)) {
return false;
}
context.window.SetTrackingMouseLeave(true);
return true;
}
bool WindowMessageDispatcher::TryHandleChromeHoverConsumption(
const DispatchContext& context,
LPARAM lParam,
LRESULT& outResult) {
const bool resizeHoverChanged = context.window.UpdateBorderlessWindowResizeHover(lParam);
if (context.window.UpdateBorderlessWindowChromeHover(lParam)) {
context.window.InvalidateHostWindow();
}
if (resizeHoverChanged) {
context.window.InvalidateHostWindow();
}
EnsureTrackingMouseLeave(context);
if (context.window.HasHoveredBorderlessResizeEdge()) {
outResult = 0;
return true;
}
const BorderlessWindowChromeHitTarget chromeHitTarget =
context.window.HitTestBorderlessWindowChrome(lParam);
if (chromeHitTarget == BorderlessWindowChromeHitTarget::MinimizeButton ||
chromeHitTarget == BorderlessWindowChromeHitTarget::MaximizeRestoreButton ||
chromeHitTarget == BorderlessWindowChromeHitTarget::CloseButton) {
outResult = 0;
return true;
}
return false;
}
bool WindowMessageDispatcher::TryDispatchWindowPointerMessage(
const DispatchContext& context,
UINT message,
WPARAM wParam,
LPARAM lParam,
LRESULT& outResult) {
switch (message) {
case WM_MOUSEMOVE:
if (context.windowHost.HandleGlobalTabDragPointerMove(context.hwnd)) {
outResult = 0;
return true;
}
if (context.window.HandleBorderlessWindowResizePointerMove(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive())) {
outResult = 0;
return true;
}
if (context.window.HandleBorderlessWindowChromeDragRestorePointerMove(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive())) {
outResult = 0;
return true;
}
if (TryHandleChromeHoverConsumption(context, lParam, outResult)) {
return true;
}
context.window.QueuePointerEvent(
::XCEngine::UI::UIInputEventType::PointerMove,
::XCEngine::UI::UIPointerButton::None,
wParam,
lParam);
outResult = 0;
return true;
case WM_MOUSELEAVE:
context.window.SetTrackingMouseLeave(false);
context.window.ClearBorderlessWindowResizeState();
context.window.ClearBorderlessWindowChromeDragRestoreState();
context.window.ClearBorderlessWindowChromeState();
context.window.QueuePointerLeaveEvent();
outResult = 0;
return true;
case WM_LBUTTONDOWN:
if (context.window.HandleBorderlessWindowResizeButtonDown(lParam)) {
outResult = 0;
return true;
}
if (context.window.HandleBorderlessWindowChromeButtonDown(lParam)) {
outResult = 0;
return true;
}
SetFocus(context.hwnd);
context.window.QueuePointerEvent(
::XCEngine::UI::UIInputEventType::PointerButtonDown,
::XCEngine::UI::UIPointerButton::Left,
wParam,
lParam);
outResult = 0;
return true;
case WM_LBUTTONUP:
if (context.windowHost.HandleGlobalTabDragPointerButtonUp(context.hwnd)) {
outResult = 0;
return true;
}
if (context.window.HandleBorderlessWindowResizeButtonUp()) {
outResult = 0;
return true;
}
if (context.window.HandleBorderlessWindowChromeButtonUp(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive(),
lParam)) {
outResult = 0;
return true;
}
context.window.QueuePointerEvent(
::XCEngine::UI::UIInputEventType::PointerButtonUp,
::XCEngine::UI::UIPointerButton::Left,
wParam,
lParam);
outResult = 0;
return true;
case WM_LBUTTONDBLCLK:
if (context.window.HandleBorderlessWindowChromeDoubleClick(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive(),
lParam)) {
outResult = 0;
return true;
}
return false;
case WM_MOUSEWHEEL:
context.window.QueuePointerWheelEvent(GET_WHEEL_DELTA_WPARAM(wParam), wParam, lParam);
outResult = 0;
return true;
default:
return false;
}
}
bool WindowMessageDispatcher::TryDispatchWindowInputMessage(
const DispatchContext& context,
UINT message,
WPARAM wParam,
LPARAM lParam,
LRESULT& outResult) {
if (TryDispatchWindowPointerMessage(context, message, wParam, lParam, outResult)) {
return true;
}
switch (message) {
case WM_SETFOCUS:
context.window.SyncInputModifiersFromSystemState();
context.window.QueueWindowFocusEvent(::XCEngine::UI::UIInputEventType::FocusGained);
outResult = 0;
return true;
case WM_KILLFOCUS:
context.window.ResetInputModifiers();
context.window.QueueWindowFocusEvent(::XCEngine::UI::UIInputEventType::FocusLost);
outResult = 0;
return true;
case WM_CAPTURECHANGED:
if (context.windowHost.OwnsActiveGlobalTabDrag(context.window.GetWindowId()) &&
reinterpret_cast<HWND>(lParam) != context.hwnd) {
context.windowHost.EndGlobalTabDragSession();
outResult = 0;
return true;
}
if (reinterpret_cast<HWND>(lParam) != context.hwnd &&
context.window.HasInteractiveCaptureState()) {
context.window.QueueWindowFocusEvent(::XCEngine::UI::UIInputEventType::FocusLost);
context.window.ForceClearBorderlessWindowResizeState();
context.window.ClearBorderlessWindowChromeDragRestoreState();
context.window.ClearBorderlessWindowChromeState();
outResult = 0;
return true;
}
if (reinterpret_cast<HWND>(lParam) != context.hwnd) {
context.window.ForceClearBorderlessWindowResizeState();
context.window.ClearBorderlessWindowChromeDragRestoreState();
context.window.ClearBorderlessWindowChromeState();
}
return false;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (wParam == VK_F12) {
context.window.RequestManualScreenshot();
}
context.window.QueueKeyEvent(::XCEngine::UI::UIInputEventType::KeyDown, wParam, lParam);
outResult = 0;
return true;
case WM_KEYUP:
case WM_SYSKEYUP:
context.window.QueueKeyEvent(::XCEngine::UI::UIInputEventType::KeyUp, wParam, lParam);
outResult = 0;
return true;
case WM_CHAR:
context.window.QueueCharacterEvent(wParam, lParam);
outResult = 0;
return true;
default:
return false;
}
}
bool WindowMessageDispatcher::TryDispatchWindowLifecycleMessage(
const DispatchContext& context,
UINT message,
WPARAM wParam,
LPARAM lParam,
LRESULT& outResult) {
switch (message) {
case WM_DPICHANGED:
if (lParam == 0) {
return false;
}
context.window.OnDpiChanged(
static_cast<UINT>(LOWORD(wParam)),
*reinterpret_cast<const RECT*>(lParam));
RenderAndValidateWindow(context);
outResult = 0;
return true;
case WM_ENTERSIZEMOVE:
context.window.OnEnterSizeMove();
outResult = 0;
return true;
case WM_EXITSIZEMOVE:
context.window.OnExitSizeMove();
RenderAndValidateWindow(context);
outResult = 0;
return true;
case WM_SIZE:
if (wParam != SIZE_MINIMIZED) {
context.window.OnResize(
static_cast<UINT>(LOWORD(lParam)),
static_cast<UINT>(HIWORD(lParam)));
RenderAndValidateWindow(context);
}
outResult = 0;
return true;
case WM_PAINT:
if (App::EditorWindowFrameTransferRequests transferRequests = context.window.OnPaintMessage(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive());
transferRequests.HasPendingRequests()) {
context.windowHost.HandleWindowFrameTransferRequests(
context.window,
std::move(transferRequests));
}
outResult = 0;
return true;
case WM_ERASEBKGND:
outResult = 1;
return true;
case WM_DESTROY:
if (context.windowHost.OwnsActiveGlobalTabDrag(context.window.GetWindowId())) {
context.windowHost.EndGlobalTabDragSession();
}
context.windowHost.HandleDestroyedWindow(context.hwnd);
outResult = 0;
return true;
default:
return false;
}
}
bool WindowMessageDispatcher::TryDispatchWindowChromeMessage(
const DispatchContext& context,
UINT message,
WPARAM wParam,
LPARAM lParam,
LRESULT& outResult) {
switch (message) {
case WM_GETMINMAXINFO:
if (context.window.IsBorderlessWindowEnabled() &&
context.window.HandleBorderlessWindowGetMinMaxInfo(lParam)) {
outResult = 0;
return true;
}
return false;
case WM_NCCALCSIZE:
if (context.window.IsBorderlessWindowEnabled()) {
outResult = context.window.HandleBorderlessWindowNcCalcSize(wParam, lParam);
return true;
}
return false;
case WM_NCACTIVATE:
if (context.window.IsBorderlessWindowEnabled()) {
outResult = TRUE;
return true;
}
return false;
case WM_NCHITTEST:
if (context.window.IsBorderlessWindowEnabled()) {
outResult = HTCLIENT;
return true;
}
return false;
case WM_NCPAINT:
case kMessageNcUaDrawCaption:
case kMessageNcUaDrawFrame:
if (context.window.IsBorderlessWindowEnabled()) {
outResult = 0;
return true;
}
return false;
case WM_SYSCOMMAND:
if (context.window.HandleBorderlessWindowSystemCommand(
context.windowHost.GetEditorContext(),
context.windowHost.IsGlobalTabDragActive(),
wParam)) {
outResult = 0;
return true;
}
return false;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT && context.window.ApplyCurrentCursor()) {
outResult = TRUE;
return true;
}
return false;
default:
return false;
}
}
bool WindowMessageDispatcher::TryDispatch(
HWND hwnd,
WindowMessageHost& windowHost,
App::EditorWindow& window,
UINT message,
WPARAM wParam,
LPARAM lParam,
LRESULT& outResult) {
const DispatchContext context = {
.hwnd = hwnd,
.windowHost = windowHost,
.window = window,
};
return TryDispatchWindowChromeMessage(context, message, wParam, lParam, outResult) ||
TryDispatchWindowLifecycleMessage(context, message, wParam, lParam, outResult) ||
TryDispatchWindowInputMessage(context, message, wParam, lParam, outResult);
}
} // namespace XCEngine::UI::Editor::Host