From ba91e0f5ddfd3ca1e01d9c5377d92599a6e85ecc Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 14 Apr 2026 01:42:44 +0800 Subject: [PATCH] Fix borderless host resize presentation ordering --- new_editor/CMakeLists.txt | 1 + new_editor/app/Application.cpp | 204 +++++++++++++++++- new_editor/app/Application.h | 10 + new_editor/app/Host/BorderlessWindowFrame.cpp | 176 +++++++++++++++ new_editor/app/Host/BorderlessWindowFrame.h | 46 ++++ new_editor/app/Host/HostRuntimeState.h | 92 ++++++++ 6 files changed, 523 insertions(+), 6 deletions(-) create mode 100644 new_editor/app/Host/BorderlessWindowFrame.cpp create mode 100644 new_editor/app/Host/BorderlessWindowFrame.h diff --git a/new_editor/CMakeLists.txt b/new_editor/CMakeLists.txt index e5688636..3e623972 100644 --- a/new_editor/CMakeLists.txt +++ b/new_editor/CMakeLists.txt @@ -125,6 +125,7 @@ target_link_libraries(XCUIEditorLib PUBLIC add_library(XCUIEditorHost STATIC app/Host/AutoScreenshot.cpp app/Host/BorderlessWindowChrome.cpp + app/Host/BorderlessWindowFrame.cpp app/Host/D3D12HostDevice.cpp app/Host/D3D12ShaderResourceDescriptorAllocator.cpp app/Host/D3D12WindowInteropContext.cpp diff --git a/new_editor/app/Application.cpp b/new_editor/app/Application.cpp index 00c3c4bb..b7a2cf2a 100644 --- a/new_editor/app/Application.cpp +++ b/new_editor/app/Application.cpp @@ -527,12 +527,11 @@ void Application::RenderFrame() { return; } - RECT clientRect = {}; - GetClientRect(m_hwnd, &clientRect); - const unsigned int pixelWidth = - static_cast((std::max)(clientRect.right - clientRect.left, 1L)); - const unsigned int pixelHeight = - static_cast((std::max)(clientRect.bottom - clientRect.top, 1L)); + UINT pixelWidth = 0u; + UINT pixelHeight = 0u; + if (!ResolveRenderClientPixelSize(pixelWidth, pixelHeight)) { + return; + } const float width = PixelsToDips(static_cast(pixelWidth)); const float height = PixelsToDips(static_cast(pixelHeight)); @@ -674,6 +673,14 @@ bool Application::IsPointerInsideClientArea() const { } LPCWSTR Application::ResolveCurrentCursorResource() const { + const Host::BorderlessWindowResizeEdge borderlessResizeEdge = + m_hostRuntime.IsBorderlessResizeActive() + ? m_hostRuntime.GetBorderlessResizeEdge() + : m_hostRuntime.GetHoveredBorderlessResizeEdge(); + if (borderlessResizeEdge != Host::BorderlessWindowResizeEdge::None) { + return Host::ResolveBorderlessWindowResizeCursor(borderlessResizeEdge); + } + switch (m_editorWorkspace.GetHostedContentCursorKind()) { case App::ProductProjectPanel::CursorKind::ResizeEW: return IDC_SIZEWE; @@ -707,6 +714,147 @@ bool Application::ApplyCurrentCursor() const { return true; } +Host::BorderlessWindowResizeEdge Application::HitTestBorderlessWindowResizeEdge(LPARAM lParam) const { + if (!IsBorderlessWindowEnabled() || m_hwnd == nullptr || IsZoomed(m_hwnd)) { + return Host::BorderlessWindowResizeEdge::None; + } + + RECT clientRect = {}; + if (!GetClientRect(m_hwnd, &clientRect)) { + return Host::BorderlessWindowResizeEdge::None; + } + + const float clientWidthDips = + PixelsToDips(static_cast((std::max)(clientRect.right - clientRect.left, 1L))); + const float clientHeightDips = + PixelsToDips(static_cast((std::max)(clientRect.bottom - clientRect.top, 1L))); + return Host::HitTestBorderlessWindowResizeEdge( + ::XCEngine::UI::UIRect(0.0f, 0.0f, clientWidthDips, clientHeightDips), + ConvertClientPixelsToDips(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); +} + +bool Application::UpdateBorderlessWindowResizeHover(LPARAM lParam) { + const Host::BorderlessWindowResizeEdge hoveredEdge = HitTestBorderlessWindowResizeEdge(lParam); + if (m_hostRuntime.GetHoveredBorderlessResizeEdge() == hoveredEdge) { + return false; + } + + m_hostRuntime.SetHoveredBorderlessResizeEdge(hoveredEdge); + ApplyBorderlessWindowResizeCursorHoverPriority(); + return true; +} + +bool Application::HandleBorderlessWindowResizeButtonDown(LPARAM lParam) { + const Host::BorderlessWindowResizeEdge edge = HitTestBorderlessWindowResizeEdge(lParam); + if (edge == Host::BorderlessWindowResizeEdge::None || m_hwnd == nullptr) { + return false; + } + + POINT screenPoint = {}; + if (!GetCursorPos(&screenPoint)) { + return false; + } + + RECT windowRect = {}; + if (!GetWindowRect(m_hwnd, &windowRect)) { + return false; + } + + m_hostRuntime.BeginBorderlessResize(edge, screenPoint, windowRect); + SetCapture(m_hwnd); + InvalidateHostWindow(); + return true; +} + +bool Application::HandleBorderlessWindowResizeButtonUp() { + if (!m_hostRuntime.IsBorderlessResizeActive()) { + return false; + } + + m_hostRuntime.EndBorderlessResize(); + if (GetCapture() == m_hwnd) { + ReleaseCapture(); + } + InvalidateHostWindow(); + return true; +} + +bool Application::HandleBorderlessWindowResizePointerMove() { + if (!m_hostRuntime.IsBorderlessResizeActive() || m_hwnd == nullptr) { + return false; + } + + POINT currentScreenPoint = {}; + if (!GetCursorPos(¤tScreenPoint)) { + return false; + } + + RECT targetRect = Host::ComputeBorderlessWindowResizeRect( + m_hostRuntime.GetBorderlessResizeInitialWindowRect(), + m_hostRuntime.GetBorderlessResizeInitialScreenPoint(), + currentScreenPoint, + m_hostRuntime.GetBorderlessResizeEdge(), + 640, + 360); + const int width = targetRect.right - targetRect.left; + const int height = targetRect.bottom - targetRect.top; + if (width <= 0 || height <= 0) { + return true; + } + + m_hostRuntime.SetPredictedClientPixelSize( + static_cast(width), + static_cast(height)); + ApplyWindowResize( + static_cast(width), + static_cast(height)); + RenderFrame(); + + SetWindowPos( + m_hwnd, + nullptr, + targetRect.left, + targetRect.top, + width, + height, + SWP_NOZORDER | SWP_NOACTIVATE); + return true; +} + +void Application::ClearBorderlessWindowResizeState() { + if (m_hostRuntime.IsBorderlessResizeActive()) { + return; + } + + if (m_hostRuntime.GetHoveredBorderlessResizeEdge() == Host::BorderlessWindowResizeEdge::None) { + return; + } + + m_hostRuntime.SetHoveredBorderlessResizeEdge(Host::BorderlessWindowResizeEdge::None); + InvalidateHostWindow(); +} + +void Application::ForceClearBorderlessWindowResizeState() { + if (m_hostRuntime.GetHoveredBorderlessResizeEdge() == Host::BorderlessWindowResizeEdge::None && + !m_hostRuntime.IsBorderlessResizeActive()) { + return; + } + + m_hostRuntime.SetHoveredBorderlessResizeEdge(Host::BorderlessWindowResizeEdge::None); + m_hostRuntime.EndBorderlessResize(); + if (GetCapture() == m_hwnd) { + ReleaseCapture(); + } + InvalidateHostWindow(); +} + +void Application::ApplyBorderlessWindowResizeCursorHoverPriority() { + if (m_hostRuntime.GetHoveredBorderlessResizeEdge() != Host::BorderlessWindowResizeEdge::None || + m_hostRuntime.IsBorderlessResizeActive()) { + m_borderlessWindowChromeState.hoveredTarget = Host::BorderlessWindowChromeHitTarget::None; + } +} + Host::BorderlessWindowChromeLayout Application::ResolveBorderlessWindowChromeLayout( float clientWidthDips) const { const auto& menuBarMetrics = ResolveUIEditorMenuBarMetrics(); @@ -748,6 +896,14 @@ Host::BorderlessWindowChromeHitTarget Application::HitTestBorderlessWindowChrome } bool Application::UpdateBorderlessWindowChromeHover(LPARAM lParam) { + if (m_hostRuntime.GetHoveredBorderlessResizeEdge() != Host::BorderlessWindowResizeEdge::None || + m_hostRuntime.IsBorderlessResizeActive()) { + const bool changed = + m_borderlessWindowChromeState.hoveredTarget != Host::BorderlessWindowChromeHitTarget::None; + m_borderlessWindowChromeState.hoveredTarget = Host::BorderlessWindowChromeHitTarget::None; + return changed; + } + const Host::BorderlessWindowChromeHitTarget hitTarget = HitTestBorderlessWindowChrome(lParam); const Host::BorderlessWindowChromeHitTarget buttonTarget = hitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton || @@ -764,6 +920,11 @@ bool Application::UpdateBorderlessWindowChromeHover(LPARAM lParam) { } bool Application::HandleBorderlessWindowChromeButtonDown(LPARAM lParam) { + if (m_hostRuntime.GetHoveredBorderlessResizeEdge() != Host::BorderlessWindowResizeEdge::None || + m_hostRuntime.IsBorderlessResizeActive()) { + return false; + } + const Host::BorderlessWindowChromeHitTarget hitTarget = HitTestBorderlessWindowChrome(lParam); switch (hitTarget) { case Host::BorderlessWindowChromeHitTarget::MinimizeButton: @@ -929,6 +1090,7 @@ std::string Application::DescribeInputEvents( } void Application::OnResize(UINT width, UINT height) { + m_hostRuntime.ClearPredictedClientPixelSize(); ApplyWindowResize(width, height); } @@ -938,6 +1100,7 @@ void Application::OnEnterSizeMove() { void Application::OnExitSizeMove() { m_hostRuntime.EndInteractiveResize(); + m_hostRuntime.ClearPredictedClientPixelSize(); UINT width = 0u; UINT height = 0u; if (QueryCurrentClientPixelSize(width, height)) { @@ -989,6 +1152,14 @@ bool Application::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) c return true; } +bool Application::ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight) const { + if (m_hostRuntime.TryGetPredictedClientPixelSize(outWidth, outHeight)) { + return true; + } + + return QueryCurrentClientPixelSize(outWidth, outHeight); +} + void Application::OnDpiChanged(UINT dpi, const RECT& suggestedRect) { m_hostRuntime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi); m_renderer.SetDpiScale(GetDpiScale()); @@ -1156,9 +1327,17 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP switch (message) { case WM_MOUSEMOVE: if (application != nullptr) { + if (application->HandleBorderlessWindowResizePointerMove()) { + return 0; + } + const bool resizeHoverChanged = + application->UpdateBorderlessWindowResizeHover(lParam); if (application->UpdateBorderlessWindowChromeHover(lParam)) { application->InvalidateHostWindow(); } + if (resizeHoverChanged) { + application->InvalidateHostWindow(); + } if (!application->m_trackingMouseLeave) { TRACKMOUSEEVENT trackMouseEvent = {}; trackMouseEvent.cbSize = sizeof(trackMouseEvent); @@ -1168,6 +1347,10 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP application->m_trackingMouseLeave = true; } } + if (application->m_hostRuntime.GetHoveredBorderlessResizeEdge() != + Host::BorderlessWindowResizeEdge::None) { + return 0; + } const Host::BorderlessWindowChromeHitTarget chromeHitTarget = application->HitTestBorderlessWindowChrome(lParam); if (chromeHitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton || @@ -1186,6 +1369,7 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP case WM_MOUSELEAVE: if (application != nullptr) { application->m_trackingMouseLeave = false; + application->ClearBorderlessWindowResizeState(); application->ClearBorderlessWindowChromeState(); application->QueuePointerLeaveEvent(); return 0; @@ -1193,6 +1377,9 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP break; case WM_LBUTTONDOWN: if (application != nullptr) { + if (application->HandleBorderlessWindowResizeButtonDown(lParam)) { + return 0; + } if (application->HandleBorderlessWindowChromeButtonDown(lParam)) { return 0; } @@ -1207,6 +1394,9 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP break; case WM_LBUTTONUP: if (application != nullptr) { + if (application->HandleBorderlessWindowResizeButtonUp()) { + return 0; + } if (application->HandleBorderlessWindowChromeButtonUp(lParam)) { return 0; } @@ -1249,11 +1439,13 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP reinterpret_cast(lParam) != hwnd && application->HasInteractiveCaptureState()) { application->QueueWindowFocusEvent(UIInputEventType::FocusLost); + application->ForceClearBorderlessWindowResizeState(); application->ClearBorderlessWindowChromeState(); return 0; } if (application != nullptr && reinterpret_cast(lParam) != hwnd) { + application->ForceClearBorderlessWindowResizeState(); application->ClearBorderlessWindowChromeState(); } break; diff --git a/new_editor/app/Application.h b/new_editor/app/Application.h index 3dfd6070..3a23a2d2 100644 --- a/new_editor/app/Application.h +++ b/new_editor/app/Application.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -53,6 +54,7 @@ private: void OnDpiChanged(UINT dpi, const RECT& suggestedRect); bool ApplyWindowResize(UINT width, UINT height); bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const; + bool ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight) const; bool IsPointerInsideClientArea() const; bool ApplyCurrentCursor() const; LPCWSTR ResolveCurrentCursorResource() const; @@ -84,6 +86,14 @@ private: bool HandleBorderlessWindowChromeButtonUp(LPARAM lParam); bool HandleBorderlessWindowChromeDoubleClick(LPARAM lParam); void ClearBorderlessWindowChromeState(); + Host::BorderlessWindowResizeEdge HitTestBorderlessWindowResizeEdge(LPARAM lParam) const; + bool UpdateBorderlessWindowResizeHover(LPARAM lParam); + bool HandleBorderlessWindowResizeButtonDown(LPARAM lParam); + bool HandleBorderlessWindowResizeButtonUp(); + bool HandleBorderlessWindowResizePointerMove(); + void ClearBorderlessWindowResizeState(); + void ForceClearBorderlessWindowResizeState(); + void ApplyBorderlessWindowResizeCursorHoverPriority(); void AppendBorderlessWindowChrome( ::XCEngine::UI::UIDrawList& drawList, float clientWidthDips) const; diff --git a/new_editor/app/Host/BorderlessWindowFrame.cpp b/new_editor/app/Host/BorderlessWindowFrame.cpp new file mode 100644 index 00000000..81729226 --- /dev/null +++ b/new_editor/app/Host/BorderlessWindowFrame.cpp @@ -0,0 +1,176 @@ +#include "BorderlessWindowFrame.h" + +#include + +namespace XCEngine::UI::Editor::Host { + +namespace { + +using ::XCEngine::UI::UIPoint; +using ::XCEngine::UI::UIRect; + +bool IsPointInsideRect(const UIRect& rect, const UIPoint& point) { + return rect.width > 0.0f && + rect.height > 0.0f && + point.x >= rect.x && + point.x <= rect.x + rect.width && + point.y >= rect.y && + point.y <= rect.y + rect.height; +} + +int ClampMinimum(int value, int minimum) { + return (std::max)(value, minimum); +} + +} // namespace + +BorderlessWindowResizeEdge HitTestBorderlessWindowResizeEdge( + const UIRect& clientRect, + const UIPoint& point, + const BorderlessWindowFrameMetrics& metrics) { + const float edge = (std::max)(metrics.resizeBorderThickness, 0.0f); + if (edge <= 0.0f || !IsPointInsideRect(clientRect, point)) { + return BorderlessWindowResizeEdge::None; + } + + const bool left = point.x <= clientRect.x + edge; + const bool right = point.x >= clientRect.x + clientRect.width - edge; + const bool top = point.y <= clientRect.y + edge; + const bool bottom = point.y >= clientRect.y + clientRect.height - edge; + + if (left && top) { + return BorderlessWindowResizeEdge::TopLeft; + } + if (right && top) { + return BorderlessWindowResizeEdge::TopRight; + } + if (left && bottom) { + return BorderlessWindowResizeEdge::BottomLeft; + } + if (right && bottom) { + return BorderlessWindowResizeEdge::BottomRight; + } + if (left) { + return BorderlessWindowResizeEdge::Left; + } + if (right) { + return BorderlessWindowResizeEdge::Right; + } + if (top) { + return BorderlessWindowResizeEdge::Top; + } + if (bottom) { + return BorderlessWindowResizeEdge::Bottom; + } + return BorderlessWindowResizeEdge::None; +} + +LPCWSTR ResolveBorderlessWindowResizeCursor(BorderlessWindowResizeEdge edge) { + switch (edge) { + case BorderlessWindowResizeEdge::Left: + case BorderlessWindowResizeEdge::Right: + return IDC_SIZEWE; + case BorderlessWindowResizeEdge::Top: + case BorderlessWindowResizeEdge::Bottom: + return IDC_SIZENS; + case BorderlessWindowResizeEdge::TopLeft: + case BorderlessWindowResizeEdge::BottomRight: + return IDC_SIZENWSE; + case BorderlessWindowResizeEdge::TopRight: + case BorderlessWindowResizeEdge::BottomLeft: + return IDC_SIZENESW; + case BorderlessWindowResizeEdge::None: + default: + return IDC_ARROW; + } +} + +RECT ComputeBorderlessWindowResizeRect( + const RECT& initialRect, + const POINT& initialScreenPoint, + const POINT& currentScreenPoint, + BorderlessWindowResizeEdge edge, + int minimumOuterWidth, + int minimumOuterHeight) { + RECT result = initialRect; + const LONG deltaX = currentScreenPoint.x - initialScreenPoint.x; + const LONG deltaY = currentScreenPoint.y - initialScreenPoint.y; + const int minimumWidth = ClampMinimum(minimumOuterWidth, 1); + const int minimumHeight = ClampMinimum(minimumOuterHeight, 1); + + switch (edge) { + case BorderlessWindowResizeEdge::Left: + case BorderlessWindowResizeEdge::TopLeft: + case BorderlessWindowResizeEdge::BottomLeft: + result.left += deltaX; + if (result.right - result.left < minimumWidth) { + result.left = result.right - minimumWidth; + } + break; + case BorderlessWindowResizeEdge::None: + case BorderlessWindowResizeEdge::Top: + case BorderlessWindowResizeEdge::Bottom: + case BorderlessWindowResizeEdge::Right: + case BorderlessWindowResizeEdge::TopRight: + case BorderlessWindowResizeEdge::BottomRight: + break; + } + + switch (edge) { + case BorderlessWindowResizeEdge::Right: + case BorderlessWindowResizeEdge::TopRight: + case BorderlessWindowResizeEdge::BottomRight: + result.right += deltaX; + if (result.right - result.left < minimumWidth) { + result.right = result.left + minimumWidth; + } + break; + case BorderlessWindowResizeEdge::None: + case BorderlessWindowResizeEdge::Left: + case BorderlessWindowResizeEdge::Top: + case BorderlessWindowResizeEdge::Bottom: + case BorderlessWindowResizeEdge::TopLeft: + case BorderlessWindowResizeEdge::BottomLeft: + break; + } + + switch (edge) { + case BorderlessWindowResizeEdge::Top: + case BorderlessWindowResizeEdge::TopLeft: + case BorderlessWindowResizeEdge::TopRight: + result.top += deltaY; + if (result.bottom - result.top < minimumHeight) { + result.top = result.bottom - minimumHeight; + } + break; + case BorderlessWindowResizeEdge::None: + case BorderlessWindowResizeEdge::Left: + case BorderlessWindowResizeEdge::Right: + case BorderlessWindowResizeEdge::Bottom: + case BorderlessWindowResizeEdge::BottomLeft: + case BorderlessWindowResizeEdge::BottomRight: + break; + } + + switch (edge) { + case BorderlessWindowResizeEdge::Bottom: + case BorderlessWindowResizeEdge::BottomLeft: + case BorderlessWindowResizeEdge::BottomRight: + result.bottom += deltaY; + if (result.bottom - result.top < minimumHeight) { + result.bottom = result.top + minimumHeight; + } + break; + case BorderlessWindowResizeEdge::None: + case BorderlessWindowResizeEdge::Left: + case BorderlessWindowResizeEdge::Top: + case BorderlessWindowResizeEdge::Right: + case BorderlessWindowResizeEdge::TopLeft: + case BorderlessWindowResizeEdge::TopRight: + break; + } + + return result; +} + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/BorderlessWindowFrame.h b/new_editor/app/Host/BorderlessWindowFrame.h new file mode 100644 index 00000000..115c77b4 --- /dev/null +++ b/new_editor/app/Host/BorderlessWindowFrame.h @@ -0,0 +1,46 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include + +#include + +namespace XCEngine::UI::Editor::Host { + +enum class BorderlessWindowResizeEdge : std::uint8_t { + None = 0, + Left, + Top, + Right, + Bottom, + TopLeft, + TopRight, + BottomLeft, + BottomRight +}; + +struct BorderlessWindowFrameMetrics { + float resizeBorderThickness = 6.0f; + int minimumOuterWidth = 640; + int minimumOuterHeight = 360; +}; + +BorderlessWindowResizeEdge HitTestBorderlessWindowResizeEdge( + const ::XCEngine::UI::UIRect& clientRect, + const ::XCEngine::UI::UIPoint& point, + const BorderlessWindowFrameMetrics& metrics = {}); + +LPCWSTR ResolveBorderlessWindowResizeCursor(BorderlessWindowResizeEdge edge); + +RECT ComputeBorderlessWindowResizeRect( + const RECT& initialRect, + const POINT& initialScreenPoint, + const POINT& currentScreenPoint, + BorderlessWindowResizeEdge edge, + int minimumOuterWidth, + int minimumOuterHeight); + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/HostRuntimeState.h b/new_editor/app/Host/HostRuntimeState.h index c2594fcd..200d3b5d 100644 --- a/new_editor/app/Host/HostRuntimeState.h +++ b/new_editor/app/Host/HostRuntimeState.h @@ -1,14 +1,32 @@ #pragma once +#include "BorderlessWindowFrame.h" + #include namespace XCEngine::UI::Editor::Host { +struct BorderlessWindowResizeState { + bool active = false; + BorderlessWindowResizeEdge edge = BorderlessWindowResizeEdge::None; + POINT initialScreenPoint = {}; + RECT initialWindowRect = {}; + BorderlessWindowResizeEdge hoveredEdge = BorderlessWindowResizeEdge::None; +}; + +struct PredictedClientPixelSize { + bool active = false; + UINT width = 0u; + UINT height = 0u; +}; + class HostRuntimeState { public: void Reset() { m_windowDpi = 96u; m_inInteractiveResize = false; + m_borderlessResizeState = {}; + m_predictedClientPixelSize = {}; } void SetWindowDpi(UINT dpi) { @@ -37,9 +55,83 @@ public: return m_inInteractiveResize; } + void BeginBorderlessResize( + BorderlessWindowResizeEdge edge, + const POINT& initialScreenPoint, + const RECT& initialWindowRect) { + m_borderlessResizeState.active = edge != BorderlessWindowResizeEdge::None; + m_borderlessResizeState.edge = edge; + m_borderlessResizeState.initialScreenPoint = initialScreenPoint; + m_borderlessResizeState.initialWindowRect = initialWindowRect; + m_borderlessResizeState.hoveredEdge = edge; + m_inInteractiveResize = m_borderlessResizeState.active; + } + + void EndBorderlessResize() { + m_borderlessResizeState.active = false; + m_borderlessResizeState.edge = BorderlessWindowResizeEdge::None; + m_inInteractiveResize = false; + m_predictedClientPixelSize = {}; + } + + bool IsBorderlessResizeActive() const { + return m_borderlessResizeState.active; + } + + BorderlessWindowResizeEdge GetBorderlessResizeEdge() const { + return m_borderlessResizeState.edge; + } + + const POINT& GetBorderlessResizeInitialScreenPoint() const { + return m_borderlessResizeState.initialScreenPoint; + } + + const RECT& GetBorderlessResizeInitialWindowRect() const { + return m_borderlessResizeState.initialWindowRect; + } + + void SetHoveredBorderlessResizeEdge(BorderlessWindowResizeEdge edge) { + m_borderlessResizeState.hoveredEdge = edge; + } + + BorderlessWindowResizeEdge GetHoveredBorderlessResizeEdge() const { + return m_borderlessResizeState.hoveredEdge; + } + + void SetPredictedClientPixelSize(UINT width, UINT height) { + if (width == 0u || height == 0u) { + m_predictedClientPixelSize = {}; + return; + } + + m_predictedClientPixelSize.active = true; + m_predictedClientPixelSize.width = width; + m_predictedClientPixelSize.height = height; + } + + void ClearPredictedClientPixelSize() { + m_predictedClientPixelSize = {}; + } + + bool TryGetPredictedClientPixelSize(UINT& outWidth, UINT& outHeight) const { + outWidth = 0u; + outHeight = 0u; + if (!m_predictedClientPixelSize.active || + m_predictedClientPixelSize.width == 0u || + m_predictedClientPixelSize.height == 0u) { + return false; + } + + outWidth = m_predictedClientPixelSize.width; + outHeight = m_predictedClientPixelSize.height; + return true; + } + private: UINT m_windowDpi = 96u; bool m_inInteractiveResize = false; + BorderlessWindowResizeState m_borderlessResizeState = {}; + PredictedClientPixelSize m_predictedClientPixelSize = {}; }; } // namespace XCEngine::UI::Editor::Host