262 lines
8.8 KiB
C++
262 lines
8.8 KiB
C++
|
|
#include "Platform/Win32/EditorWindow.h"
|
||
|
|
#include "Platform/Win32/EditorWindowConstants.h"
|
||
|
|
|
||
|
|
#include <algorithm>
|
||
|
|
|
||
|
|
#include <windowsx.h>
|
||
|
|
|
||
|
|
namespace XCEngine::UI::Editor::App {
|
||
|
|
|
||
|
|
using namespace EditorWindowSupport;
|
||
|
|
|
||
|
|
bool EditorWindow::UpdateBorderlessWindowChromeHover(LPARAM lParam) {
|
||
|
|
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() !=
|
||
|
|
Host::BorderlessWindowResizeEdge::None ||
|
||
|
|
m_chrome.runtime.IsBorderlessResizeActive()) {
|
||
|
|
const bool changed =
|
||
|
|
m_chrome.chromeState.hoveredTarget !=
|
||
|
|
Host::BorderlessWindowChromeHitTarget::None;
|
||
|
|
m_chrome.chromeState.hoveredTarget =
|
||
|
|
Host::BorderlessWindowChromeHitTarget::None;
|
||
|
|
return changed;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Host::BorderlessWindowChromeHitTarget hitTarget =
|
||
|
|
HitTestBorderlessWindowChrome(lParam);
|
||
|
|
const Host::BorderlessWindowChromeHitTarget buttonTarget =
|
||
|
|
hitTarget == Host::BorderlessWindowChromeHitTarget::MinimizeButton ||
|
||
|
|
hitTarget == Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton ||
|
||
|
|
hitTarget == Host::BorderlessWindowChromeHitTarget::CloseButton
|
||
|
|
? hitTarget
|
||
|
|
: Host::BorderlessWindowChromeHitTarget::None;
|
||
|
|
if (m_chrome.chromeState.hoveredTarget == buttonTarget) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_chrome.chromeState.hoveredTarget = buttonTarget;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EditorWindow::HandleBorderlessWindowChromeButtonDown(LPARAM lParam) {
|
||
|
|
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() !=
|
||
|
|
Host::BorderlessWindowResizeEdge::None ||
|
||
|
|
m_chrome.runtime.IsBorderlessResizeActive()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Host::BorderlessWindowChromeHitTarget hitTarget =
|
||
|
|
HitTestBorderlessWindowChrome(lParam);
|
||
|
|
switch (hitTarget) {
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::MinimizeButton:
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton:
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::CloseButton:
|
||
|
|
m_chrome.chromeState.pressedTarget = hitTarget;
|
||
|
|
if (m_window.hwnd != nullptr) {
|
||
|
|
SetCapture(m_window.hwnd);
|
||
|
|
}
|
||
|
|
InvalidateHostWindow();
|
||
|
|
return true;
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::DragRegion:
|
||
|
|
if (m_window.hwnd != nullptr) {
|
||
|
|
if (IsBorderlessWindowMaximized()) {
|
||
|
|
POINT screenPoint = {};
|
||
|
|
if (GetCursorPos(&screenPoint)) {
|
||
|
|
m_chrome.runtime.BeginBorderlessWindowDragRestore(screenPoint);
|
||
|
|
SetCapture(m_window.hwnd);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ReleaseCapture();
|
||
|
|
SendMessageW(m_window.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::None:
|
||
|
|
default:
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EditorWindow::HandleBorderlessWindowChromeButtonUp(
|
||
|
|
EditorContext& editorContext,
|
||
|
|
bool globalTabDragActive,
|
||
|
|
LPARAM lParam) {
|
||
|
|
if (m_chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
|
||
|
|
ClearBorderlessWindowChromeDragRestoreState();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Host::BorderlessWindowChromeHitTarget pressedTarget =
|
||
|
|
m_chrome.chromeState.pressedTarget;
|
||
|
|
if (pressedTarget != Host::BorderlessWindowChromeHitTarget::MinimizeButton &&
|
||
|
|
pressedTarget != Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton &&
|
||
|
|
pressedTarget != Host::BorderlessWindowChromeHitTarget::CloseButton) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Host::BorderlessWindowChromeHitTarget releasedTarget =
|
||
|
|
HitTestBorderlessWindowChrome(lParam);
|
||
|
|
m_chrome.chromeState.pressedTarget =
|
||
|
|
Host::BorderlessWindowChromeHitTarget::None;
|
||
|
|
if (GetCapture() == m_window.hwnd) {
|
||
|
|
ReleaseCapture();
|
||
|
|
}
|
||
|
|
InvalidateHostWindow();
|
||
|
|
|
||
|
|
if (pressedTarget == releasedTarget) {
|
||
|
|
ExecuteBorderlessWindowChromeAction(
|
||
|
|
editorContext,
|
||
|
|
globalTabDragActive,
|
||
|
|
pressedTarget);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EditorWindow::HandleBorderlessWindowChromeDoubleClick(
|
||
|
|
EditorContext& editorContext,
|
||
|
|
bool globalTabDragActive,
|
||
|
|
LPARAM lParam) {
|
||
|
|
if (m_chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
|
||
|
|
ClearBorderlessWindowChromeDragRestoreState();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (HitTestBorderlessWindowChrome(lParam) !=
|
||
|
|
Host::BorderlessWindowChromeHitTarget::DragRegion) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
ExecuteBorderlessWindowChromeAction(
|
||
|
|
editorContext,
|
||
|
|
globalTabDragActive,
|
||
|
|
Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EditorWindow::HandleBorderlessWindowChromeDragRestorePointerMove(
|
||
|
|
EditorContext& editorContext,
|
||
|
|
bool globalTabDragActive) {
|
||
|
|
if (!m_chrome.runtime.IsBorderlessWindowDragRestoreArmed() || m_window.hwnd == nullptr) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
POINT currentScreenPoint = {};
|
||
|
|
if (!GetCursorPos(¤tScreenPoint)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const POINT initialScreenPoint =
|
||
|
|
m_chrome.runtime.GetBorderlessWindowDragRestoreInitialScreenPoint();
|
||
|
|
const int dragThresholdX = (std::max)(GetSystemMetrics(SM_CXDRAG), 1);
|
||
|
|
const int dragThresholdY = (std::max)(GetSystemMetrics(SM_CYDRAG), 1);
|
||
|
|
const LONG deltaX = currentScreenPoint.x - initialScreenPoint.x;
|
||
|
|
const LONG deltaY = currentScreenPoint.y - initialScreenPoint.y;
|
||
|
|
if (std::abs(deltaX) < dragThresholdX &&
|
||
|
|
std::abs(deltaY) < dragThresholdY) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
RECT restoreRect = {};
|
||
|
|
RECT currentRect = {};
|
||
|
|
RECT workAreaRect = {};
|
||
|
|
if (!m_chrome.runtime.TryGetBorderlessWindowRestoreRect(restoreRect) ||
|
||
|
|
!QueryCurrentWindowRect(currentRect) ||
|
||
|
|
!QueryBorderlessWindowWorkAreaRect(workAreaRect)) {
|
||
|
|
ClearBorderlessWindowChromeDragRestoreState();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const int restoreWidth = restoreRect.right - restoreRect.left;
|
||
|
|
const int restoreHeight = restoreRect.bottom - restoreRect.top;
|
||
|
|
const int currentWidth = currentRect.right - currentRect.left;
|
||
|
|
if (restoreWidth <= 0 || restoreHeight <= 0 || currentWidth <= 0) {
|
||
|
|
ClearBorderlessWindowChromeDragRestoreState();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const float pointerRatio =
|
||
|
|
static_cast<float>(currentScreenPoint.x - currentRect.left) /
|
||
|
|
static_cast<float>(currentWidth);
|
||
|
|
const float clampedPointerRatio = (std::clamp)(pointerRatio, 0.0f, 1.0f);
|
||
|
|
const int newLeft =
|
||
|
|
(std::clamp)(
|
||
|
|
currentScreenPoint.x -
|
||
|
|
static_cast<int>(clampedPointerRatio * static_cast<float>(restoreWidth)),
|
||
|
|
workAreaRect.left,
|
||
|
|
workAreaRect.right - restoreWidth);
|
||
|
|
const int titleBarHeightPixels =
|
||
|
|
static_cast<int>(kBorderlessTitleBarHeightDips * GetDpiScale());
|
||
|
|
const int newTop =
|
||
|
|
(std::clamp)(
|
||
|
|
currentScreenPoint.y - (std::max)(titleBarHeightPixels / 2, 1),
|
||
|
|
workAreaRect.top,
|
||
|
|
workAreaRect.bottom - restoreHeight);
|
||
|
|
const RECT targetRect = {
|
||
|
|
newLeft,
|
||
|
|
newTop,
|
||
|
|
newLeft + restoreWidth,
|
||
|
|
newTop + restoreHeight
|
||
|
|
};
|
||
|
|
|
||
|
|
m_chrome.runtime.SetBorderlessWindowMaximized(false);
|
||
|
|
ApplyPredictedWindowRectTransition(
|
||
|
|
editorContext,
|
||
|
|
globalTabDragActive,
|
||
|
|
targetRect);
|
||
|
|
ClearBorderlessWindowChromeDragRestoreState();
|
||
|
|
ReleaseCapture();
|
||
|
|
SendMessageW(m_window.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void EditorWindow::ClearBorderlessWindowChromeDragRestoreState() {
|
||
|
|
if (!m_chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_chrome.runtime.EndBorderlessWindowDragRestore();
|
||
|
|
if (GetCapture() == m_window.hwnd) {
|
||
|
|
ReleaseCapture();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void EditorWindow::ClearBorderlessWindowChromeState() {
|
||
|
|
if (m_chrome.chromeState.hoveredTarget ==
|
||
|
|
Host::BorderlessWindowChromeHitTarget::None &&
|
||
|
|
m_chrome.chromeState.pressedTarget ==
|
||
|
|
Host::BorderlessWindowChromeHitTarget::None) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_chrome.chromeState = {};
|
||
|
|
InvalidateHostWindow();
|
||
|
|
}
|
||
|
|
|
||
|
|
void EditorWindow::ExecuteBorderlessWindowChromeAction(
|
||
|
|
EditorContext& editorContext,
|
||
|
|
bool globalTabDragActive,
|
||
|
|
Host::BorderlessWindowChromeHitTarget target) {
|
||
|
|
if (m_window.hwnd == nullptr) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (target) {
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::MinimizeButton:
|
||
|
|
ShowWindow(m_window.hwnd, SW_MINIMIZE);
|
||
|
|
break;
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton:
|
||
|
|
ToggleBorderlessWindowMaximizeRestore(editorContext, globalTabDragActive);
|
||
|
|
break;
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::CloseButton:
|
||
|
|
PostMessageW(m_window.hwnd, WM_CLOSE, 0, 0);
|
||
|
|
break;
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::DragRegion:
|
||
|
|
case Host::BorderlessWindowChromeHitTarget::None:
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
InvalidateHostWindow();
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace XCEngine::UI::Editor::App
|