Fix borderless host resize presentation ordering
This commit is contained in:
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
|
||||
|
||||
#include "BorderlessWindowFrame.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user