Fix borderless host resize presentation ordering
This commit is contained in:
@@ -125,6 +125,7 @@ target_link_libraries(XCUIEditorLib PUBLIC
|
|||||||
add_library(XCUIEditorHost STATIC
|
add_library(XCUIEditorHost STATIC
|
||||||
app/Host/AutoScreenshot.cpp
|
app/Host/AutoScreenshot.cpp
|
||||||
app/Host/BorderlessWindowChrome.cpp
|
app/Host/BorderlessWindowChrome.cpp
|
||||||
|
app/Host/BorderlessWindowFrame.cpp
|
||||||
app/Host/D3D12HostDevice.cpp
|
app/Host/D3D12HostDevice.cpp
|
||||||
app/Host/D3D12ShaderResourceDescriptorAllocator.cpp
|
app/Host/D3D12ShaderResourceDescriptorAllocator.cpp
|
||||||
app/Host/D3D12WindowInteropContext.cpp
|
app/Host/D3D12WindowInteropContext.cpp
|
||||||
|
|||||||
@@ -527,12 +527,11 @@ void Application::RenderFrame() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RECT clientRect = {};
|
UINT pixelWidth = 0u;
|
||||||
GetClientRect(m_hwnd, &clientRect);
|
UINT pixelHeight = 0u;
|
||||||
const unsigned int pixelWidth =
|
if (!ResolveRenderClientPixelSize(pixelWidth, pixelHeight)) {
|
||||||
static_cast<unsigned int>((std::max)(clientRect.right - clientRect.left, 1L));
|
return;
|
||||||
const unsigned int pixelHeight =
|
}
|
||||||
static_cast<unsigned int>((std::max)(clientRect.bottom - clientRect.top, 1L));
|
|
||||||
const float width = PixelsToDips(static_cast<float>(pixelWidth));
|
const float width = PixelsToDips(static_cast<float>(pixelWidth));
|
||||||
const float height = PixelsToDips(static_cast<float>(pixelHeight));
|
const float height = PixelsToDips(static_cast<float>(pixelHeight));
|
||||||
|
|
||||||
@@ -674,6 +673,14 @@ bool Application::IsPointerInsideClientArea() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LPCWSTR Application::ResolveCurrentCursorResource() 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()) {
|
switch (m_editorWorkspace.GetHostedContentCursorKind()) {
|
||||||
case App::ProductProjectPanel::CursorKind::ResizeEW:
|
case App::ProductProjectPanel::CursorKind::ResizeEW:
|
||||||
return IDC_SIZEWE;
|
return IDC_SIZEWE;
|
||||||
@@ -707,6 +714,147 @@ bool Application::ApplyCurrentCursor() const {
|
|||||||
return true;
|
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<float>((std::max)(clientRect.right - clientRect.left, 1L)));
|
||||||
|
const float clientHeightDips =
|
||||||
|
PixelsToDips(static_cast<float>((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<UINT>(width),
|
||||||
|
static_cast<UINT>(height));
|
||||||
|
ApplyWindowResize(
|
||||||
|
static_cast<UINT>(width),
|
||||||
|
static_cast<UINT>(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(
|
Host::BorderlessWindowChromeLayout Application::ResolveBorderlessWindowChromeLayout(
|
||||||
float clientWidthDips) const {
|
float clientWidthDips) const {
|
||||||
const auto& menuBarMetrics = ResolveUIEditorMenuBarMetrics();
|
const auto& menuBarMetrics = ResolveUIEditorMenuBarMetrics();
|
||||||
@@ -748,6 +896,14 @@ Host::BorderlessWindowChromeHitTarget Application::HitTestBorderlessWindowChrome
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Application::UpdateBorderlessWindowChromeHover(LPARAM lParam) {
|
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 hitTarget = HitTestBorderlessWindowChrome(lParam);
|
||||||
const Host::BorderlessWindowChromeHitTarget buttonTarget =
|
const Host::BorderlessWindowChromeHitTarget buttonTarget =
|
||||||
hitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton ||
|
hitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton ||
|
||||||
@@ -764,6 +920,11 @@ bool Application::UpdateBorderlessWindowChromeHover(LPARAM lParam) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Application::HandleBorderlessWindowChromeButtonDown(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);
|
const Host::BorderlessWindowChromeHitTarget hitTarget = HitTestBorderlessWindowChrome(lParam);
|
||||||
switch (hitTarget) {
|
switch (hitTarget) {
|
||||||
case Host::BorderlessWindowChromeHitTarget::MinimizeButton:
|
case Host::BorderlessWindowChromeHitTarget::MinimizeButton:
|
||||||
@@ -929,6 +1090,7 @@ std::string Application::DescribeInputEvents(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::OnResize(UINT width, UINT height) {
|
void Application::OnResize(UINT width, UINT height) {
|
||||||
|
m_hostRuntime.ClearPredictedClientPixelSize();
|
||||||
ApplyWindowResize(width, height);
|
ApplyWindowResize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,6 +1100,7 @@ void Application::OnEnterSizeMove() {
|
|||||||
|
|
||||||
void Application::OnExitSizeMove() {
|
void Application::OnExitSizeMove() {
|
||||||
m_hostRuntime.EndInteractiveResize();
|
m_hostRuntime.EndInteractiveResize();
|
||||||
|
m_hostRuntime.ClearPredictedClientPixelSize();
|
||||||
UINT width = 0u;
|
UINT width = 0u;
|
||||||
UINT height = 0u;
|
UINT height = 0u;
|
||||||
if (QueryCurrentClientPixelSize(width, height)) {
|
if (QueryCurrentClientPixelSize(width, height)) {
|
||||||
@@ -989,6 +1152,14 @@ bool Application::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) c
|
|||||||
return true;
|
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) {
|
void Application::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
|
||||||
m_hostRuntime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
|
m_hostRuntime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
|
||||||
m_renderer.SetDpiScale(GetDpiScale());
|
m_renderer.SetDpiScale(GetDpiScale());
|
||||||
@@ -1156,9 +1327,17 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
|||||||
switch (message) {
|
switch (message) {
|
||||||
case WM_MOUSEMOVE:
|
case WM_MOUSEMOVE:
|
||||||
if (application != nullptr) {
|
if (application != nullptr) {
|
||||||
|
if (application->HandleBorderlessWindowResizePointerMove()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const bool resizeHoverChanged =
|
||||||
|
application->UpdateBorderlessWindowResizeHover(lParam);
|
||||||
if (application->UpdateBorderlessWindowChromeHover(lParam)) {
|
if (application->UpdateBorderlessWindowChromeHover(lParam)) {
|
||||||
application->InvalidateHostWindow();
|
application->InvalidateHostWindow();
|
||||||
}
|
}
|
||||||
|
if (resizeHoverChanged) {
|
||||||
|
application->InvalidateHostWindow();
|
||||||
|
}
|
||||||
if (!application->m_trackingMouseLeave) {
|
if (!application->m_trackingMouseLeave) {
|
||||||
TRACKMOUSEEVENT trackMouseEvent = {};
|
TRACKMOUSEEVENT trackMouseEvent = {};
|
||||||
trackMouseEvent.cbSize = sizeof(trackMouseEvent);
|
trackMouseEvent.cbSize = sizeof(trackMouseEvent);
|
||||||
@@ -1168,6 +1347,10 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
|||||||
application->m_trackingMouseLeave = true;
|
application->m_trackingMouseLeave = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (application->m_hostRuntime.GetHoveredBorderlessResizeEdge() !=
|
||||||
|
Host::BorderlessWindowResizeEdge::None) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
const Host::BorderlessWindowChromeHitTarget chromeHitTarget =
|
const Host::BorderlessWindowChromeHitTarget chromeHitTarget =
|
||||||
application->HitTestBorderlessWindowChrome(lParam);
|
application->HitTestBorderlessWindowChrome(lParam);
|
||||||
if (chromeHitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton ||
|
if (chromeHitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton ||
|
||||||
@@ -1186,6 +1369,7 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
|||||||
case WM_MOUSELEAVE:
|
case WM_MOUSELEAVE:
|
||||||
if (application != nullptr) {
|
if (application != nullptr) {
|
||||||
application->m_trackingMouseLeave = false;
|
application->m_trackingMouseLeave = false;
|
||||||
|
application->ClearBorderlessWindowResizeState();
|
||||||
application->ClearBorderlessWindowChromeState();
|
application->ClearBorderlessWindowChromeState();
|
||||||
application->QueuePointerLeaveEvent();
|
application->QueuePointerLeaveEvent();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1193,6 +1377,9 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
|||||||
break;
|
break;
|
||||||
case WM_LBUTTONDOWN:
|
case WM_LBUTTONDOWN:
|
||||||
if (application != nullptr) {
|
if (application != nullptr) {
|
||||||
|
if (application->HandleBorderlessWindowResizeButtonDown(lParam)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (application->HandleBorderlessWindowChromeButtonDown(lParam)) {
|
if (application->HandleBorderlessWindowChromeButtonDown(lParam)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1207,6 +1394,9 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
|||||||
break;
|
break;
|
||||||
case WM_LBUTTONUP:
|
case WM_LBUTTONUP:
|
||||||
if (application != nullptr) {
|
if (application != nullptr) {
|
||||||
|
if (application->HandleBorderlessWindowResizeButtonUp()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (application->HandleBorderlessWindowChromeButtonUp(lParam)) {
|
if (application->HandleBorderlessWindowChromeButtonUp(lParam)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1249,11 +1439,13 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
|||||||
reinterpret_cast<HWND>(lParam) != hwnd &&
|
reinterpret_cast<HWND>(lParam) != hwnd &&
|
||||||
application->HasInteractiveCaptureState()) {
|
application->HasInteractiveCaptureState()) {
|
||||||
application->QueueWindowFocusEvent(UIInputEventType::FocusLost);
|
application->QueueWindowFocusEvent(UIInputEventType::FocusLost);
|
||||||
|
application->ForceClearBorderlessWindowResizeState();
|
||||||
application->ClearBorderlessWindowChromeState();
|
application->ClearBorderlessWindowChromeState();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (application != nullptr &&
|
if (application != nullptr &&
|
||||||
reinterpret_cast<HWND>(lParam) != hwnd) {
|
reinterpret_cast<HWND>(lParam) != hwnd) {
|
||||||
|
application->ForceClearBorderlessWindowResizeState();
|
||||||
application->ClearBorderlessWindowChromeState();
|
application->ClearBorderlessWindowChromeState();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <Host/AutoScreenshot.h>
|
#include <Host/AutoScreenshot.h>
|
||||||
#include <Host/BorderlessWindowChrome.h>
|
#include <Host/BorderlessWindowChrome.h>
|
||||||
|
#include <Host/BorderlessWindowFrame.h>
|
||||||
#include <Host/D3D12WindowRenderer.h>
|
#include <Host/D3D12WindowRenderer.h>
|
||||||
#include <Host/D3D12WindowRenderLoop.h>
|
#include <Host/D3D12WindowRenderLoop.h>
|
||||||
#include <Host/HostRuntimeState.h>
|
#include <Host/HostRuntimeState.h>
|
||||||
@@ -53,6 +54,7 @@ private:
|
|||||||
void OnDpiChanged(UINT dpi, const RECT& suggestedRect);
|
void OnDpiChanged(UINT dpi, const RECT& suggestedRect);
|
||||||
bool ApplyWindowResize(UINT width, UINT height);
|
bool ApplyWindowResize(UINT width, UINT height);
|
||||||
bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const;
|
bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const;
|
||||||
|
bool ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight) const;
|
||||||
bool IsPointerInsideClientArea() const;
|
bool IsPointerInsideClientArea() const;
|
||||||
bool ApplyCurrentCursor() const;
|
bool ApplyCurrentCursor() const;
|
||||||
LPCWSTR ResolveCurrentCursorResource() const;
|
LPCWSTR ResolveCurrentCursorResource() const;
|
||||||
@@ -84,6 +86,14 @@ private:
|
|||||||
bool HandleBorderlessWindowChromeButtonUp(LPARAM lParam);
|
bool HandleBorderlessWindowChromeButtonUp(LPARAM lParam);
|
||||||
bool HandleBorderlessWindowChromeDoubleClick(LPARAM lParam);
|
bool HandleBorderlessWindowChromeDoubleClick(LPARAM lParam);
|
||||||
void ClearBorderlessWindowChromeState();
|
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(
|
void AppendBorderlessWindowChrome(
|
||||||
::XCEngine::UI::UIDrawList& drawList,
|
::XCEngine::UI::UIDrawList& drawList,
|
||||||
float clientWidthDips) const;
|
float clientWidthDips) const;
|
||||||
|
|||||||
176
new_editor/app/Host/BorderlessWindowFrame.cpp
Normal file
176
new_editor/app/Host/BorderlessWindowFrame.cpp
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#include "BorderlessWindowFrame.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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
|
||||||
46
new_editor/app/Host/BorderlessWindowFrame.h
Normal file
46
new_editor/app/Host/BorderlessWindowFrame.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <XCEngine/UI/Types.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -1,14 +1,32 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "BorderlessWindowFrame.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
namespace XCEngine::UI::Editor::Host {
|
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 {
|
class HostRuntimeState {
|
||||||
public:
|
public:
|
||||||
void Reset() {
|
void Reset() {
|
||||||
m_windowDpi = 96u;
|
m_windowDpi = 96u;
|
||||||
m_inInteractiveResize = false;
|
m_inInteractiveResize = false;
|
||||||
|
m_borderlessResizeState = {};
|
||||||
|
m_predictedClientPixelSize = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetWindowDpi(UINT dpi) {
|
void SetWindowDpi(UINT dpi) {
|
||||||
@@ -37,9 +55,83 @@ public:
|
|||||||
return m_inInteractiveResize;
|
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:
|
private:
|
||||||
UINT m_windowDpi = 96u;
|
UINT m_windowDpi = 96u;
|
||||||
bool m_inInteractiveResize = false;
|
bool m_inInteractiveResize = false;
|
||||||
|
BorderlessWindowResizeState m_borderlessResizeState = {};
|
||||||
|
PredictedClientPixelSize m_predictedClientPixelSize = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace XCEngine::UI::Editor::Host
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
|
|||||||
Reference in New Issue
Block a user