refactor(new_editor): tighten app dependency boundaries

This commit is contained in:
2026-04-19 02:48:41 +08:00
parent 7429f22fb1
commit c59cd83c38
86 changed files with 1754 additions and 1077 deletions

View File

@@ -4,21 +4,61 @@
#define NOMINMAX
#endif
#include "Platform/Win32/EditorWindowState.h"
#include <XCEngine/UI/DrawData.h>
#include "Platform/Win32/EditorWindowPointerCapture.h"
#include "Platform/Win32/EditorWindowTransferRequests.h"
#include <windows.h>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI {
class UIDrawList;
struct UIPoint;
struct UIRect;
struct UIInputEvent;
struct UIInputModifiers;
enum class UIPointerButton : std::uint8_t;
enum class UIInputEventType : std::uint8_t;
} // namespace XCEngine::UI
namespace XCEngine::UI::Editor {
class UIEditorWorkspaceController;
struct UIEditorDockHostInteractionState;
struct UIEditorShellInteractionFrame;
struct UIEditorShellInteractionState;
struct UIEditorShellInteractionResult;
namespace Widgets {
struct UIEditorDockHostDropPreviewState;
}
} // namespace XCEngine::UI::Editor
namespace XCEngine::UI::Editor::Host {
enum class BorderlessWindowChromeHitTarget : std::uint8_t;
enum class BorderlessWindowResizeEdge : std::uint8_t;
struct BorderlessWindowChromeLayout;
} // namespace XCEngine::UI::Editor::Host
namespace XCEngine::UI::Editor::App {
class EditorContext;
class EditorShellRuntime;
struct EditorWindowState;
namespace Internal {
class EditorWindowHostRuntime;
@@ -40,6 +80,7 @@ public:
std::wstring title,
bool primary,
UIEditorWorkspaceController workspaceController);
~EditorWindow();
EditorWindow(const EditorWindow&) = delete;
EditorWindow& operator=(const EditorWindow&) = delete;
@@ -215,11 +256,7 @@ private:
void UpdateCachedTitleText();
static bool IsVerboseRuntimeTraceEnabled();
EditorWindowWindowState m_window = {};
EditorWindowRenderState m_render = {};
EditorWindowInputState m_input = {};
EditorWindowCompositionState m_composition = {};
EditorWindowChromeRuntimeState m_chrome = {};
std::unique_ptr<EditorWindowState> m_state = {};
};
} // namespace XCEngine::UI::Editor::App

View File

@@ -1,4 +1,5 @@
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowInternalState.h"
namespace XCEngine::UI::Editor::App {
bool EditorWindow::IsBorderlessWindowEnabled() const {
@@ -6,7 +7,7 @@ bool EditorWindow::IsBorderlessWindowEnabled() const {
}
bool EditorWindow::IsBorderlessWindowMaximized() const {
return m_chrome.runtime.IsBorderlessWindowMaximized();
return m_state->chrome.runtime.IsBorderlessWindowMaximized();
}
bool EditorWindow::HandleBorderlessWindowSystemCommand(
@@ -22,7 +23,7 @@ bool EditorWindow::HandleBorderlessWindowSystemCommand(
ToggleBorderlessWindowMaximizeRestore(editorContext, globalTabDragActive);
return true;
case SC_RESTORE:
if (!IsIconic(m_window.hwnd)) {
if (!IsIconic(m_state->window.hwnd)) {
ToggleBorderlessWindowMaximizeRestore(editorContext, globalTabDragActive);
return true;
}
@@ -33,29 +34,31 @@ bool EditorWindow::HandleBorderlessWindowSystemCommand(
}
bool EditorWindow::HandleBorderlessWindowGetMinMaxInfo(LPARAM lParam) const {
return Host::HandleBorderlessWindowGetMinMaxInfo(m_window.hwnd, lParam);
return Host::HandleBorderlessWindowGetMinMaxInfo(m_state->window.hwnd, lParam);
}
LRESULT EditorWindow::HandleBorderlessWindowNcCalcSize(WPARAM wParam, LPARAM lParam) const {
return Host::HandleBorderlessWindowNcCalcSize(
m_window.hwnd,
m_state->window.hwnd,
wParam,
lParam,
m_chrome.runtime.GetWindowDpi());
m_state->chrome.runtime.GetWindowDpi());
}
bool EditorWindow::QueryCurrentWindowRect(RECT& outRect) const {
outRect = {};
return m_window.hwnd != nullptr && GetWindowRect(m_window.hwnd, &outRect) != FALSE;
return m_state->window.hwnd != nullptr &&
GetWindowRect(m_state->window.hwnd, &outRect) != FALSE;
}
bool EditorWindow::QueryBorderlessWindowWorkAreaRect(RECT& outRect) const {
outRect = {};
if (m_window.hwnd == nullptr) {
if (m_state->window.hwnd == nullptr) {
return false;
}
const HMONITOR monitor = MonitorFromWindow(m_window.hwnd, MONITOR_DEFAULTTONEAREST);
const HMONITOR monitor =
MonitorFromWindow(m_state->window.hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor == nullptr) {
return false;
}
@@ -74,7 +77,7 @@ bool EditorWindow::ApplyPredictedWindowRectTransition(
EditorContext& editorContext,
bool globalTabDragActive,
const RECT& targetRect) {
if (m_window.hwnd == nullptr) {
if (m_state->window.hwnd == nullptr) {
return false;
}
@@ -84,13 +87,13 @@ bool EditorWindow::ApplyPredictedWindowRectTransition(
return false;
}
m_chrome.runtime.SetPredictedClientPixelSize(
m_state->chrome.runtime.SetPredictedClientPixelSize(
static_cast<UINT>(width),
static_cast<UINT>(height));
ApplyWindowResize(static_cast<UINT>(width), static_cast<UINT>(height));
(void)RenderFrame(editorContext, globalTabDragActive);
SetWindowPos(
m_window.hwnd,
m_state->window.hwnd,
nullptr,
targetRect.left,
targetRect.top,
@@ -104,7 +107,7 @@ bool EditorWindow::ApplyPredictedWindowRectTransition(
void EditorWindow::ToggleBorderlessWindowMaximizeRestore(
EditorContext& editorContext,
bool globalTabDragActive) {
if (m_window.hwnd == nullptr) {
if (m_state->window.hwnd == nullptr) {
return;
}
@@ -116,8 +119,8 @@ void EditorWindow::ToggleBorderlessWindowMaximizeRestore(
return;
}
m_chrome.runtime.SetBorderlessWindowRestoreRect(currentRect);
m_chrome.runtime.SetBorderlessWindowMaximized(true);
m_state->chrome.runtime.SetBorderlessWindowRestoreRect(currentRect);
m_state->chrome.runtime.SetBorderlessWindowMaximized(true);
ApplyPredictedWindowRectTransition(
editorContext,
globalTabDragActive,
@@ -126,11 +129,11 @@ void EditorWindow::ToggleBorderlessWindowMaximizeRestore(
}
RECT restoreRect = {};
if (!m_chrome.runtime.TryGetBorderlessWindowRestoreRect(restoreRect)) {
if (!m_state->chrome.runtime.TryGetBorderlessWindowRestoreRect(restoreRect)) {
return;
}
m_chrome.runtime.SetBorderlessWindowMaximized(false);
m_state->chrome.runtime.SetBorderlessWindowMaximized(false);
ApplyPredictedWindowRectTransition(
editorContext,
globalTabDragActive,

View File

@@ -1,4 +1,5 @@
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include <XCEngine/UI/Types.h>
@@ -13,11 +14,11 @@ using ::XCEngine::UI::UIRect;
bool EditorWindow::UpdateBorderlessWindowResizeHover(LPARAM lParam) {
const Host::BorderlessWindowResizeEdge hoveredEdge =
HitTestBorderlessWindowResizeEdge(lParam);
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() == hoveredEdge) {
if (m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() == hoveredEdge) {
return false;
}
m_chrome.runtime.SetHoveredBorderlessResizeEdge(hoveredEdge);
m_state->chrome.runtime.SetHoveredBorderlessResizeEdge(hoveredEdge);
ApplyBorderlessWindowResizeCursorHoverPriority();
return true;
}
@@ -25,7 +26,8 @@ bool EditorWindow::UpdateBorderlessWindowResizeHover(LPARAM lParam) {
bool EditorWindow::HandleBorderlessWindowResizeButtonDown(LPARAM lParam) {
const Host::BorderlessWindowResizeEdge edge =
HitTestBorderlessWindowResizeEdge(lParam);
if (edge == Host::BorderlessWindowResizeEdge::None || m_window.hwnd == nullptr) {
if (edge == Host::BorderlessWindowResizeEdge::None ||
m_state->window.hwnd == nullptr) {
return false;
}
@@ -35,22 +37,22 @@ bool EditorWindow::HandleBorderlessWindowResizeButtonDown(LPARAM lParam) {
}
RECT windowRect = {};
if (!GetWindowRect(m_window.hwnd, &windowRect)) {
if (!GetWindowRect(m_state->window.hwnd, &windowRect)) {
return false;
}
m_chrome.runtime.BeginBorderlessResize(edge, screenPoint, windowRect);
m_state->chrome.runtime.BeginBorderlessResize(edge, screenPoint, windowRect);
AcquirePointerCapture(EditorWindowPointerCaptureOwner::BorderlessResize);
InvalidateHostWindow();
return true;
}
bool EditorWindow::HandleBorderlessWindowResizeButtonUp() {
if (!m_chrome.runtime.IsBorderlessResizeActive()) {
if (!m_state->chrome.runtime.IsBorderlessResizeActive()) {
return false;
}
m_chrome.runtime.EndBorderlessResize();
m_state->chrome.runtime.EndBorderlessResize();
ReleasePointerCapture(EditorWindowPointerCaptureOwner::BorderlessResize);
InvalidateHostWindow();
return true;
@@ -59,7 +61,8 @@ bool EditorWindow::HandleBorderlessWindowResizeButtonUp() {
bool EditorWindow::HandleBorderlessWindowResizePointerMove(
EditorContext& editorContext,
bool globalTabDragActive) {
if (!m_chrome.runtime.IsBorderlessResizeActive() || m_window.hwnd == nullptr) {
if (!m_state->chrome.runtime.IsBorderlessResizeActive() ||
m_state->window.hwnd == nullptr) {
return false;
}
@@ -69,10 +72,10 @@ bool EditorWindow::HandleBorderlessWindowResizePointerMove(
}
RECT targetRect = Host::ComputeBorderlessWindowResizeRect(
m_chrome.runtime.GetBorderlessResizeInitialWindowRect(),
m_chrome.runtime.GetBorderlessResizeInitialScreenPoint(),
m_state->chrome.runtime.GetBorderlessResizeInitialWindowRect(),
m_state->chrome.runtime.GetBorderlessResizeInitialScreenPoint(),
currentScreenPoint,
m_chrome.runtime.GetBorderlessResizeEdge(),
m_state->chrome.runtime.GetBorderlessResizeEdge(),
640,
360);
const int width = targetRect.right - targetRect.left;
@@ -81,14 +84,14 @@ bool EditorWindow::HandleBorderlessWindowResizePointerMove(
return true;
}
m_chrome.runtime.SetPredictedClientPixelSize(
m_state->chrome.runtime.SetPredictedClientPixelSize(
static_cast<UINT>(width),
static_cast<UINT>(height));
ApplyWindowResize(static_cast<UINT>(width), static_cast<UINT>(height));
(void)RenderFrame(editorContext, globalTabDragActive);
SetWindowPos(
m_window.hwnd,
m_state->window.hwnd,
nullptr,
targetRect.left,
targetRect.top,
@@ -99,40 +102,44 @@ bool EditorWindow::HandleBorderlessWindowResizePointerMove(
}
void EditorWindow::ClearBorderlessWindowResizeState() {
if (m_chrome.runtime.IsBorderlessResizeActive()) {
if (m_state->chrome.runtime.IsBorderlessResizeActive()) {
return;
}
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() ==
if (m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() ==
Host::BorderlessWindowResizeEdge::None) {
return;
}
m_chrome.runtime.SetHoveredBorderlessResizeEdge(Host::BorderlessWindowResizeEdge::None);
m_state->chrome.runtime.SetHoveredBorderlessResizeEdge(
Host::BorderlessWindowResizeEdge::None);
InvalidateHostWindow();
}
void EditorWindow::ForceClearBorderlessWindowResizeState() {
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() ==
if (m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() ==
Host::BorderlessWindowResizeEdge::None &&
!m_chrome.runtime.IsBorderlessResizeActive()) {
!m_state->chrome.runtime.IsBorderlessResizeActive()) {
return;
}
m_chrome.runtime.SetHoveredBorderlessResizeEdge(Host::BorderlessWindowResizeEdge::None);
m_chrome.runtime.EndBorderlessResize();
m_state->chrome.runtime.SetHoveredBorderlessResizeEdge(
Host::BorderlessWindowResizeEdge::None);
m_state->chrome.runtime.EndBorderlessResize();
ReleasePointerCapture(EditorWindowPointerCaptureOwner::BorderlessResize);
InvalidateHostWindow();
}
Host::BorderlessWindowResizeEdge EditorWindow::HitTestBorderlessWindowResizeEdge(
LPARAM lParam) const {
if (!IsBorderlessWindowEnabled() || m_window.hwnd == nullptr || IsBorderlessWindowMaximized()) {
if (!IsBorderlessWindowEnabled() ||
m_state->window.hwnd == nullptr ||
IsBorderlessWindowMaximized()) {
return Host::BorderlessWindowResizeEdge::None;
}
RECT clientRect = {};
if (!GetClientRect(m_window.hwnd, &clientRect)) {
if (!GetClientRect(m_state->window.hwnd, &clientRect)) {
return Host::BorderlessWindowResizeEdge::None;
}
@@ -146,10 +153,10 @@ Host::BorderlessWindowResizeEdge EditorWindow::HitTestBorderlessWindowResizeEdge
}
void EditorWindow::ApplyBorderlessWindowResizeCursorHoverPriority() {
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() !=
if (m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() !=
Host::BorderlessWindowResizeEdge::None ||
m_chrome.runtime.IsBorderlessResizeActive()) {
m_chrome.chromeState.hoveredTarget =
m_state->chrome.runtime.IsBorderlessResizeActive()) {
m_state->chrome.chromeState.hoveredTarget =
Host::BorderlessWindowChromeHitTarget::None;
}
}

View File

@@ -1,5 +1,6 @@
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowConstants.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include "Platform/Win32/EditorWindowRuntimeInternal.h"
#include "Platform/Win32/EditorWindowStyle.h"
#include "State/EditorContext.h"
@@ -127,7 +128,7 @@ std::uint8_t ResolveExpectedShellCaptureButtons(
EditorWindowFrameTransferRequests EditorWindow::RenderFrame(
EditorContext& editorContext,
bool globalTabDragActive) {
if (!m_render.ready || m_window.hwnd == nullptr) {
if (!m_state->render.ready || m_state->window.hwnd == nullptr) {
return {};
}
@@ -158,13 +159,13 @@ EditorWindowFrameTransferRequests EditorWindow::RenderFrame(
AppendBorderlessWindowChrome(drawList, width);
const Host::D3D12WindowRenderLoopPresentResult presentResult =
m_render.windowRenderLoop.Present(drawData);
m_state->render.windowRenderLoop.Present(drawData);
if (!presentResult.warning.empty()) {
LogRuntimeTrace("present", presentResult.warning);
}
m_render.autoScreenshot.CaptureIfRequested(
m_render.renderer,
m_state->render.autoScreenshot.CaptureIfRequested(
m_state->render.renderer,
drawData,
pixelWidth,
pixelHeight,
@@ -175,15 +176,15 @@ EditorWindowFrameTransferRequests EditorWindow::RenderFrame(
EditorWindowFrameTransferRequests EditorWindow::OnPaintMessage(
EditorContext& editorContext,
bool globalTabDragActive) {
if (!m_render.ready || m_window.hwnd == nullptr) {
if (!m_state->render.ready || m_state->window.hwnd == nullptr) {
return {};
}
PAINTSTRUCT paintStruct = {};
BeginPaint(m_window.hwnd, &paintStruct);
BeginPaint(m_state->window.hwnd, &paintStruct);
const EditorWindowFrameTransferRequests transferRequests =
RenderFrame(editorContext, globalTabDragActive);
EndPaint(m_window.hwnd, &paintStruct);
EndPaint(m_state->window.hwnd, &paintStruct);
return transferRequests;
}
@@ -210,40 +211,40 @@ EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame(
const UIRect& workspaceBounds,
UIDrawList& drawList) {
SyncShellCapturedPointerButtonsFromSystemState();
std::vector<UIInputEvent> frameEvents = std::move(m_input.pendingEvents);
m_input.pendingEvents.clear();
std::vector<UIInputEvent> frameEvents = std::move(m_state->input.pendingEvents);
m_state->input.pendingEvents.clear();
if (!frameEvents.empty() && IsVerboseRuntimeTraceEnabled()) {
LogRuntimeTrace(
"input",
DescribeInputEvents(frameEvents) + " | " +
editorContext.DescribeWorkspaceState(
m_composition.workspaceController,
m_composition.shellRuntime.GetShellInteractionState()));
m_state->composition.workspaceController,
m_state->composition.shellRuntime.GetShellInteractionState()));
}
const Host::D3D12WindowRenderLoopFrameContext frameContext =
m_render.windowRenderLoop.BeginFrame();
m_state->render.windowRenderLoop.BeginFrame();
if (!frameContext.warning.empty()) {
LogRuntimeTrace("viewport", frameContext.warning);
}
editorContext.AttachTextMeasurer(m_render.renderer);
editorContext.AttachTextMeasurer(m_state->render.renderer);
const bool useDetachedTitleBarTabStrip = ShouldUseDetachedTitleBarTabStrip();
m_composition.shellRuntime.Update(
m_state->composition.shellRuntime.Update(
editorContext,
m_composition.workspaceController,
m_state->composition.workspaceController,
workspaceBounds,
frameEvents,
BuildCaptureStatusText(),
m_window.primary
m_state->window.primary
? EditorShellVariant::Primary
: EditorShellVariant::DetachedWindow,
useDetachedTitleBarTabStrip,
useDetachedTitleBarTabStrip ? kBorderlessTitleBarHeightDips : 0.0f);
const UIEditorShellInteractionFrame& shellFrame =
m_composition.shellRuntime.GetShellFrame();
m_state->composition.shellRuntime.GetShellFrame();
const UIEditorDockHostInteractionState& dockHostInteractionState =
m_composition.shellRuntime
m_state->composition.shellRuntime
.GetShellInteractionState()
.workspaceInteractionState
.dockHostInteractionState;
@@ -253,14 +254,15 @@ EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame(
BuildShellTransferRequests(globalTabDragActive, dockHostInteractionState, shellFrame);
ApplyHostCaptureRequests(shellFrame.result);
for (const WorkspaceTraceEntry& entry : m_composition.shellRuntime.GetTraceEntries()) {
for (const WorkspaceTraceEntry& entry :
m_state->composition.shellRuntime.GetTraceEntries()) {
LogRuntimeTrace(entry.channel, entry.message);
}
ApplyHostedContentCaptureRequests();
ApplyCurrentCursor();
m_composition.shellRuntime.Append(drawList);
m_state->composition.shellRuntime.Append(drawList);
if (frameContext.canRenderViewports) {
m_composition.shellRuntime.RenderRequestedViewports(frameContext.renderContext);
m_state->composition.shellRuntime.RenderRequestedViewports(frameContext.renderContext);
}
return transferRequests;
}
@@ -301,7 +303,7 @@ void EditorWindow::LogFrameInteractionTrace(
<< " commandExecuted="
<< (shellFrame.result.workspaceResult.dockHostResult.commandExecuted ? "true" : "false")
<< " active="
<< m_composition.workspaceController.GetWorkspace().activePanelId
<< m_state->composition.workspaceController.GetWorkspace().activePanelId
<< " message="
<< shellFrame.result.workspaceResult.dockHostResult.layoutResult.message;
LogRuntimeTrace("frame", frameTrace.str());
@@ -339,33 +341,33 @@ EditorWindowFrameTransferRequests EditorWindow::BuildShellTransferRequests(
}
std::string EditorWindow::BuildCaptureStatusText() const {
if (m_render.autoScreenshot.HasPendingCapture()) {
if (m_state->render.autoScreenshot.HasPendingCapture()) {
return "Shot pending...";
}
if (!m_render.autoScreenshot.GetLastCaptureError().empty()) {
return TruncateText(m_render.autoScreenshot.GetLastCaptureError(), 38u);
if (!m_state->render.autoScreenshot.GetLastCaptureError().empty()) {
return TruncateText(m_state->render.autoScreenshot.GetLastCaptureError(), 38u);
}
if (!m_render.autoScreenshot.GetLastCaptureSummary().empty()) {
return TruncateText(m_render.autoScreenshot.GetLastCaptureSummary(), 38u);
if (!m_state->render.autoScreenshot.GetLastCaptureSummary().empty()) {
return TruncateText(m_state->render.autoScreenshot.GetLastCaptureSummary(), 38u);
}
return {};
}
void EditorWindow::SyncShellCapturedPointerButtonsFromSystemState() {
m_input.modifierTracker.SyncFromSystemState();
m_state->input.modifierTracker.SyncFromSystemState();
const std::uint8_t expectedButtons =
ResolveExpectedShellCaptureButtons(m_composition.shellRuntime);
ResolveExpectedShellCaptureButtons(m_state->composition.shellRuntime);
if (expectedButtons == 0u ||
HasPendingPointerStateReconciliationEvent(m_input.pendingEvents)) {
HasPendingPointerStateReconciliationEvent(m_state->input.pendingEvents)) {
return;
}
const UIInputModifiers modifiers =
m_input.modifierTracker.GetCurrentModifiers();
m_state->input.modifierTracker.GetCurrentModifiers();
if ((ButtonMaskFromModifiers(modifiers) & expectedButtons) == expectedButtons) {
return;
}
@@ -383,12 +385,12 @@ void EditorWindow::ApplyHostCaptureRequests(const UIEditorShellInteractionResult
}
void EditorWindow::ApplyHostedContentCaptureRequests() {
if (m_composition.shellRuntime.WantsHostPointerCapture()) {
if (m_state->composition.shellRuntime.WantsHostPointerCapture()) {
AcquirePointerCapture(EditorWindowPointerCaptureOwner::HostedContent);
}
if (m_composition.shellRuntime.WantsHostPointerRelease() &&
!m_composition.shellRuntime.HasShellInteractiveCapture()) {
if (m_state->composition.shellRuntime.WantsHostPointerRelease() &&
!m_state->composition.shellRuntime.HasShellInteractiveCapture()) {
ReleasePointerCapture(EditorWindowPointerCaptureOwner::HostedContent);
}
}

View File

@@ -1,4 +1,5 @@
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Types.h>
@@ -128,59 +129,59 @@ bool EditorWindow::ApplyCurrentCursor() const {
bool EditorWindow::HasInteractiveCaptureState() const {
return m_composition.shellRuntime.HasInteractiveCapture() ||
m_chrome.runtime.IsBorderlessWindowDragRestoreArmed() ||
m_chrome.runtime.IsBorderlessResizeActive() ||
m_input.pointerCaptureOwner != EditorWindowPointerCaptureOwner::None;
return m_state->composition.shellRuntime.HasInteractiveCapture() ||
m_state->chrome.runtime.IsBorderlessWindowDragRestoreArmed() ||
m_state->chrome.runtime.IsBorderlessResizeActive() ||
m_state->input.pointerCaptureOwner != EditorWindowPointerCaptureOwner::None;
}
EditorWindowPointerCaptureOwner EditorWindow::GetPointerCaptureOwner() const {
return m_input.pointerCaptureOwner;
return m_state->input.pointerCaptureOwner;
}
bool EditorWindow::OwnsPointerCapture(EditorWindowPointerCaptureOwner owner) const {
return m_input.pointerCaptureOwner == owner;
return m_state->input.pointerCaptureOwner == owner;
}
void EditorWindow::AcquirePointerCapture(EditorWindowPointerCaptureOwner owner) {
if (owner == EditorWindowPointerCaptureOwner::None ||
m_window.hwnd == nullptr ||
!IsWindow(m_window.hwnd)) {
m_state->window.hwnd == nullptr ||
!IsWindow(m_state->window.hwnd)) {
return;
}
m_input.pointerCaptureOwner = owner;
if (GetCapture() != m_window.hwnd) {
SetCapture(m_window.hwnd);
m_state->input.pointerCaptureOwner = owner;
if (GetCapture() != m_state->window.hwnd) {
SetCapture(m_state->window.hwnd);
}
}
void EditorWindow::ReleasePointerCapture(EditorWindowPointerCaptureOwner owner) {
if (m_input.pointerCaptureOwner != owner) {
if (m_state->input.pointerCaptureOwner != owner) {
return;
}
m_input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
if (m_window.hwnd != nullptr && GetCapture() == m_window.hwnd) {
m_state->input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
if (m_state->window.hwnd != nullptr && GetCapture() == m_state->window.hwnd) {
ReleaseCapture();
}
}
void EditorWindow::ForceReleasePointerCapture() {
m_input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
if (m_window.hwnd != nullptr && GetCapture() == m_window.hwnd) {
m_state->input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
if (m_state->window.hwnd != nullptr && GetCapture() == m_state->window.hwnd) {
ReleaseCapture();
}
}
void EditorWindow::ClearPointerCaptureOwner() {
m_input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
m_state->input.pointerCaptureOwner = EditorWindowPointerCaptureOwner::None;
}
void EditorWindow::TryStartImmediateShellPointerCapture(LPARAM lParam) {
if (m_window.hwnd == nullptr ||
!IsWindow(m_window.hwnd) ||
GetCapture() == m_window.hwnd) {
if (m_state->window.hwnd == nullptr ||
!IsWindow(m_state->window.hwnd) ||
GetCapture() == m_state->window.hwnd) {
return;
}
@@ -188,7 +189,7 @@ void EditorWindow::TryStartImmediateShellPointerCapture(LPARAM lParam) {
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
if (!ShouldStartImmediateUIEditorShellPointerCapture(
m_composition.shellRuntime.GetShellFrame(),
m_state->composition.shellRuntime.GetShellFrame(),
clientPoint)) {
return;
}
@@ -207,16 +208,16 @@ void EditorWindow::QueuePointerEvent(
event.position = ConvertClientPixelsToDips(
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam));
event.modifiers = m_input.modifierTracker.ApplyPointerMessage(
event.modifiers = m_state->input.modifierTracker.ApplyPointerMessage(
type,
button,
static_cast<std::size_t>(wParam));
m_input.pendingEvents.push_back(event);
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::QueueSyntheticPointerStateSyncEvent(
const ::XCEngine::UI::UIInputModifiers& modifiers) {
if (m_window.hwnd == nullptr || !IsWindow(m_window.hwnd)) {
if (m_state->window.hwnd == nullptr || !IsWindow(m_state->window.hwnd)) {
return;
}
@@ -224,7 +225,7 @@ void EditorWindow::QueueSyntheticPointerStateSyncEvent(
if (!GetCursorPos(&screenPoint)) {
return;
}
if (!ScreenToClient(m_window.hwnd, &screenPoint)) {
if (!ScreenToClient(m_state->window.hwnd, &screenPoint)) {
return;
}
@@ -232,24 +233,24 @@ void EditorWindow::QueueSyntheticPointerStateSyncEvent(
event.type = UIInputEventType::PointerMove;
event.position = ConvertClientPixelsToDips(screenPoint.x, screenPoint.y);
event.modifiers = modifiers;
m_input.pendingEvents.push_back(event);
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::QueuePointerLeaveEvent() {
UIInputEvent event = {};
event.type = UIInputEventType::PointerLeave;
if (m_window.hwnd != nullptr) {
if (m_state->window.hwnd != nullptr) {
POINT clientPoint = {};
GetCursorPos(&clientPoint);
ScreenToClient(m_window.hwnd, &clientPoint);
ScreenToClient(m_state->window.hwnd, &clientPoint);
event.position = ConvertClientPixelsToDips(clientPoint.x, clientPoint.y);
}
event.modifiers = m_input.modifierTracker.GetCurrentModifiers();
m_input.pendingEvents.push_back(event);
event.modifiers = m_state->input.modifierTracker.GetCurrentModifiers();
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM lParam) {
if (m_window.hwnd == nullptr) {
if (m_state->window.hwnd == nullptr) {
return;
}
@@ -257,56 +258,56 @@ void EditorWindow::QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARA
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam)
};
ScreenToClient(m_window.hwnd, &screenPoint);
ScreenToClient(m_state->window.hwnd, &screenPoint);
UIInputEvent event = {};
event.type = UIInputEventType::PointerWheel;
event.position = ConvertClientPixelsToDips(screenPoint.x, screenPoint.y);
event.wheelDelta = static_cast<float>(wheelDelta);
event.modifiers = m_input.modifierTracker.ApplyPointerMessage(
event.modifiers = m_state->input.modifierTracker.ApplyPointerMessage(
UIInputEventType::PointerWheel,
UIPointerButton::None,
static_cast<std::size_t>(wParam));
m_input.pendingEvents.push_back(event);
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::QueueKeyEvent(UIInputEventType type, WPARAM wParam, LPARAM lParam) {
UIInputEvent event = {};
event.type = type;
event.keyCode = MapVirtualKeyToUIKeyCode(wParam);
event.modifiers = m_input.modifierTracker.ApplyKeyMessage(type, wParam, lParam);
event.modifiers = m_state->input.modifierTracker.ApplyKeyMessage(type, wParam, lParam);
event.repeat = IsRepeatKeyMessage(lParam);
m_input.pendingEvents.push_back(event);
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::QueueCharacterEvent(WPARAM wParam, LPARAM) {
UIInputEvent event = {};
event.type = UIInputEventType::Character;
event.character = static_cast<std::uint32_t>(wParam);
event.modifiers = m_input.modifierTracker.GetCurrentModifiers();
m_input.pendingEvents.push_back(event);
event.modifiers = m_state->input.modifierTracker.GetCurrentModifiers();
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::QueueWindowFocusEvent(UIInputEventType type) {
UIInputEvent event = {};
event.type = type;
m_input.pendingEvents.push_back(event);
m_state->input.pendingEvents.push_back(event);
}
void EditorWindow::SyncInputModifiersFromSystemState() {
m_input.modifierTracker.SyncFromSystemState();
m_state->input.modifierTracker.SyncFromSystemState();
}
void EditorWindow::ResetInputModifiers() {
m_input.modifierTracker.Reset();
m_state->input.modifierTracker.Reset();
}
void EditorWindow::RequestManualScreenshot() {
m_render.autoScreenshot.RequestCapture("manual_f12");
m_state->render.autoScreenshot.RequestCapture("manual_f12");
}
bool EditorWindow::IsPointerInsideClientArea() const {
if (m_window.hwnd == nullptr || !IsWindow(m_window.hwnd)) {
if (m_state->window.hwnd == nullptr || !IsWindow(m_state->window.hwnd)) {
return false;
}
@@ -315,26 +316,26 @@ bool EditorWindow::IsPointerInsideClientArea() const {
return false;
}
if (!IsScreenPointOverWindow(m_window.hwnd, screenPoint)) {
if (!IsScreenPointOverWindow(m_state->window.hwnd, screenPoint)) {
return false;
}
const LPARAM pointParam = MAKELPARAM(
static_cast<SHORT>(screenPoint.x),
static_cast<SHORT>(screenPoint.y));
return SendMessageW(m_window.hwnd, WM_NCHITTEST, 0, pointParam) == HTCLIENT;
return SendMessageW(m_state->window.hwnd, WM_NCHITTEST, 0, pointParam) == HTCLIENT;
}
LPCWSTR EditorWindow::ResolveCurrentCursorResource() const {
const Host::BorderlessWindowResizeEdge borderlessResizeEdge =
m_chrome.runtime.IsBorderlessResizeActive()
? m_chrome.runtime.GetBorderlessResizeEdge()
: m_chrome.runtime.GetHoveredBorderlessResizeEdge();
m_state->chrome.runtime.IsBorderlessResizeActive()
? m_state->chrome.runtime.GetBorderlessResizeEdge()
: m_state->chrome.runtime.GetHoveredBorderlessResizeEdge();
if (borderlessResizeEdge != Host::BorderlessWindowResizeEdge::None) {
return Host::ResolveBorderlessWindowResizeCursor(borderlessResizeEdge);
}
switch (m_composition.shellRuntime.GetHostedContentCursorKind()) {
switch (m_state->composition.shellRuntime.GetHostedContentCursorKind()) {
case ProjectPanel::CursorKind::ResizeEW:
return IDC_SIZEWE;
case ProjectPanel::CursorKind::Arrow:
@@ -342,7 +343,7 @@ LPCWSTR EditorWindow::ResolveCurrentCursorResource() const {
break;
}
switch (m_composition.shellRuntime.GetDockCursorKind()) {
switch (m_state->composition.shellRuntime.GetDockCursorKind()) {
case Widgets::UIEditorDockHostCursorKind::ResizeEW:
return IDC_SIZEWE;
case Widgets::UIEditorDockHostCursorKind::ResizeNS:

View File

@@ -4,8 +4,8 @@
#define NOMINMAX
#endif
#include "Platform/Win32/EditorWindowPointerCapture.h"
#include "Composition/EditorShellRuntime.h"
#include "Platform/Win32/EditorWindowPointerCapture.h"
#include <Platform/Win32/BorderlessWindowChrome.h>
#include <Platform/Win32/HostRuntimeState.h>
@@ -21,7 +21,6 @@
#include <windows.h>
#include <optional>
#include <string>
#include <vector>
@@ -63,23 +62,12 @@ struct EditorWindowChromeRuntimeState {
Host::HostRuntimeState runtime = {};
};
struct EditorWindowPanelTransferRequest {
std::string nodeId = {};
std::string panelId = {};
POINT screenPoint = {};
bool IsValid() const {
return !nodeId.empty() && !panelId.empty();
}
};
struct EditorWindowFrameTransferRequests {
std::optional<EditorWindowPanelTransferRequest> beginGlobalTabDrag = {};
std::optional<EditorWindowPanelTransferRequest> detachPanel = {};
bool HasPendingRequests() const {
return beginGlobalTabDrag.has_value() || detachPanel.has_value();
}
struct EditorWindowState {
EditorWindowWindowState window = {};
EditorWindowRenderState render = {};
EditorWindowInputState input = {};
EditorWindowCompositionState composition = {};
EditorWindowChromeRuntimeState chrome = {};
};
} // namespace XCEngine::UI::Editor::App

View File

@@ -2,6 +2,7 @@
#include "Bootstrap/EditorResources.h"
#include "Platform/Win32/EditorWindowConstants.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include "Platform/Win32/EditorWindowPlatformInternal.h"
#include "Platform/Win32/EditorWindowRuntimeInternal.h"
#include "State/EditorContext.h"
@@ -73,122 +74,121 @@ EditorWindow::EditorWindow(
std::wstring title,
bool primary,
UIEditorWorkspaceController workspaceController)
: m_window{
nullptr,
std::move(windowId),
std::move(title),
{},
primary,
false }
, m_composition{ std::move(workspaceController), {} } {
: m_state(std::make_unique<EditorWindowState>()) {
m_state->window.windowId = std::move(windowId);
m_state->window.title = std::move(title);
m_state->window.primary = primary;
m_state->composition.workspaceController = std::move(workspaceController);
UpdateCachedTitleText();
}
EditorWindow::~EditorWindow() = default;
std::string_view EditorWindow::GetWindowId() const {
return m_window.windowId;
return m_state->window.windowId;
}
HWND EditorWindow::GetHwnd() const {
return m_window.hwnd;
return m_state->window.hwnd;
}
bool EditorWindow::HasHwnd() const {
return m_window.hwnd != nullptr;
return m_state->window.hwnd != nullptr;
}
bool EditorWindow::IsPrimary() const {
return m_window.primary;
return m_state->window.primary;
}
bool EditorWindow::IsClosing() const {
return m_window.closing;
return m_state->window.closing;
}
bool EditorWindow::IsRenderReady() const {
return m_render.ready;
return m_state->render.ready;
}
bool EditorWindow::IsTrackingMouseLeave() const {
return m_input.trackingMouseLeave;
return m_state->input.trackingMouseLeave;
}
bool EditorWindow::HasHoveredBorderlessResizeEdge() const {
return m_chrome.runtime.GetHoveredBorderlessResizeEdge() !=
return m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() !=
Host::BorderlessWindowResizeEdge::None;
}
const std::wstring& EditorWindow::GetTitle() const {
return m_window.title;
return m_state->window.title;
}
const UIEditorWorkspaceController& EditorWindow::GetWorkspaceController() const {
return m_composition.workspaceController;
return m_state->composition.workspaceController;
}
UIEditorWorkspaceController& EditorWindow::GetWorkspaceController() {
return m_composition.workspaceController;
return m_state->composition.workspaceController;
}
const EditorShellRuntime& EditorWindow::GetShellRuntime() const {
return m_composition.shellRuntime;
return m_state->composition.shellRuntime;
}
EditorShellRuntime& EditorWindow::GetShellRuntime() {
return m_composition.shellRuntime;
return m_state->composition.shellRuntime;
}
const UIEditorShellInteractionFrame& EditorWindow::GetShellFrame() const {
return m_composition.shellRuntime.GetShellFrame();
return m_state->composition.shellRuntime.GetShellFrame();
}
const UIEditorShellInteractionState& EditorWindow::GetShellInteractionState() const {
return m_composition.shellRuntime.GetShellInteractionState();
return m_state->composition.shellRuntime.GetShellInteractionState();
}
void EditorWindow::SetExternalDockHostDropPreview(
const Widgets::UIEditorDockHostDropPreviewState& preview) {
m_composition.shellRuntime.SetExternalDockHostDropPreview(preview);
m_state->composition.shellRuntime.SetExternalDockHostDropPreview(preview);
}
void EditorWindow::ClearExternalDockHostDropPreview() {
m_composition.shellRuntime.ClearExternalDockHostDropPreview();
m_state->composition.shellRuntime.ClearExternalDockHostDropPreview();
}
void EditorWindow::AttachHwnd(HWND hwnd) {
m_window.hwnd = hwnd;
m_window.closing = false;
m_state->window.hwnd = hwnd;
m_state->window.closing = false;
}
void EditorWindow::MarkDestroyed() {
m_window.hwnd = nullptr;
m_window.closing = false;
m_input.trackingMouseLeave = false;
m_state->window.hwnd = nullptr;
m_state->window.closing = false;
m_state->input.trackingMouseLeave = false;
}
void EditorWindow::MarkClosing() {
m_window.closing = true;
m_state->window.closing = true;
}
void EditorWindow::ClearClosing() {
m_window.closing = false;
m_state->window.closing = false;
}
void EditorWindow::SetTrackingMouseLeave(bool trackingMouseLeave) {
m_input.trackingMouseLeave = trackingMouseLeave;
m_state->input.trackingMouseLeave = trackingMouseLeave;
}
void EditorWindow::SetTitle(std::wstring title) {
m_window.title = std::move(title);
m_state->window.title = std::move(title);
UpdateCachedTitleText();
}
void EditorWindow::ReplaceWorkspaceController(UIEditorWorkspaceController workspaceController) {
m_composition.workspaceController = std::move(workspaceController);
m_state->composition.workspaceController = std::move(workspaceController);
}
void EditorWindow::InvalidateHostWindow() const {
if (m_window.hwnd != nullptr && IsWindow(m_window.hwnd)) {
InvalidateRect(m_window.hwnd, nullptr, FALSE);
if (m_state->window.hwnd != nullptr && IsWindow(m_state->window.hwnd)) {
InvalidateRect(m_state->window.hwnd, nullptr, FALSE);
}
}
@@ -197,71 +197,80 @@ bool EditorWindow::Initialize(
EditorContext& editorContext,
const std::filesystem::path& captureRoot,
bool autoCaptureOnStartup) {
if (m_window.hwnd == nullptr) {
if (m_state->window.hwnd == nullptr) {
LogRuntimeTrace("app", "window initialize skipped: hwnd is null");
return false;
}
Host::RefreshBorderlessWindowDwmDecorations(m_window.hwnd);
m_chrome.runtime.Reset();
m_chrome.runtime.SetWindowDpi(QueryWindowDpi(m_window.hwnd));
m_render.renderer.SetDpiScale(GetDpiScale());
Host::RefreshBorderlessWindowDwmDecorations(m_state->window.hwnd);
m_state->chrome.runtime.Reset();
m_state->chrome.runtime.SetWindowDpi(QueryWindowDpi(m_state->window.hwnd));
m_state->render.renderer.SetDpiScale(GetDpiScale());
std::ostringstream dpiTrace = {};
dpiTrace << "initial dpi=" << m_chrome.runtime.GetWindowDpi()
dpiTrace << "initial dpi=" << m_state->chrome.runtime.GetWindowDpi()
<< " scale=" << GetDpiScale();
LogRuntimeTrace("window", dpiTrace.str());
if (!m_render.renderer.Initialize(m_window.hwnd)) {
if (!m_state->render.renderer.Initialize(m_state->window.hwnd)) {
LogRuntimeTrace("app", "renderer initialization failed");
return false;
}
RECT clientRect = {};
GetClientRect(m_window.hwnd, &clientRect);
GetClientRect(m_state->window.hwnd, &clientRect);
const int clientWidth = (std::max)(clientRect.right - clientRect.left, 1L);
const int clientHeight = (std::max)(clientRect.bottom - clientRect.top, 1L);
if (!m_render.windowRenderer.Initialize(m_window.hwnd, clientWidth, clientHeight)) {
if (!m_state->render.windowRenderer.Initialize(
m_state->window.hwnd,
clientWidth,
clientHeight)) {
LogRuntimeTrace("app", "d3d12 window renderer initialization failed");
m_render.renderer.Shutdown();
m_state->render.renderer.Shutdown();
return false;
}
const Host::D3D12WindowRenderLoopAttachResult attachResult =
m_render.windowRenderLoop.Attach(m_render.renderer, m_render.windowRenderer);
m_state->render.windowRenderLoop.Attach(
m_state->render.renderer,
m_state->render.windowRenderer);
if (!attachResult.interopWarning.empty()) {
LogRuntimeTrace("app", attachResult.interopWarning);
}
editorContext.AttachTextMeasurer(m_render.renderer);
m_composition.shellRuntime.Initialize(repoRoot, m_render.renderer);
m_composition.shellRuntime.AttachViewportWindowRenderer(m_render.windowRenderer);
m_composition.shellRuntime.SetViewportSurfacePresentationEnabled(
editorContext.AttachTextMeasurer(m_state->render.renderer);
m_state->composition.shellRuntime.Initialize(
repoRoot,
m_state->render.renderer,
m_state->render.renderer);
m_state->composition.shellRuntime.AttachViewportWindowRenderer(
m_state->render.windowRenderer);
m_state->composition.shellRuntime.SetViewportSurfacePresentationEnabled(
attachResult.hasViewportSurfacePresentation);
std::string titleBarLogoError = {};
if (!LoadEmbeddedPngTexture(
m_render.renderer,
m_state->render.renderer,
IDR_PNG_LOGO_ICON,
m_render.titleBarLogoIcon,
m_state->render.titleBarLogoIcon,
titleBarLogoError)) {
LogRuntimeTrace("icons", "titlebar logo_icon.png: " + titleBarLogoError);
}
if (!m_composition.shellRuntime.GetBuiltInIconError().empty()) {
LogRuntimeTrace("icons", m_composition.shellRuntime.GetBuiltInIconError());
if (!m_state->composition.shellRuntime.GetBuiltInIconError().empty()) {
LogRuntimeTrace("icons", m_state->composition.shellRuntime.GetBuiltInIconError());
}
LogRuntimeTrace(
"app",
"shell runtime initialized: " +
editorContext.DescribeWorkspaceState(
m_composition.workspaceController,
m_composition.shellRuntime.GetShellInteractionState()));
m_render.ready = true;
m_state->composition.workspaceController,
m_state->composition.shellRuntime.GetShellInteractionState()));
m_state->render.ready = true;
m_render.autoScreenshot.Initialize(captureRoot);
m_state->render.autoScreenshot.Initialize(captureRoot);
if (autoCaptureOnStartup && IsAutoCaptureOnStartupEnabled()) {
m_render.autoScreenshot.RequestCapture("startup");
m_state->render.autoScreenshot.RequestCapture("startup");
editorContext.SetStatus("Capture", "Startup capture requested.");
}
@@ -271,41 +280,42 @@ bool EditorWindow::Initialize(
void EditorWindow::Shutdown() {
ForceReleasePointerCapture();
m_render.ready = false;
m_render.autoScreenshot.Shutdown();
m_composition.shellRuntime.Shutdown();
m_render.renderer.ReleaseTexture(m_render.titleBarLogoIcon);
m_render.windowRenderLoop.Detach();
m_render.windowRenderer.Shutdown();
m_render.renderer.Shutdown();
m_input.pendingEvents.clear();
m_chrome.chromeState = {};
m_chrome.runtime.Reset();
m_state->render.ready = false;
m_state->render.autoScreenshot.Shutdown();
m_state->composition.shellRuntime.Shutdown();
m_state->render.renderer.ReleaseTexture(m_state->render.titleBarLogoIcon);
m_state->render.windowRenderLoop.Detach();
m_state->render.windowRenderer.Shutdown();
m_state->render.renderer.Shutdown();
m_state->input.pendingEvents.clear();
m_state->chrome.chromeState = {};
m_state->chrome.runtime.Reset();
}
void EditorWindow::ResetInteractionState() {
ForceReleasePointerCapture();
m_input.pendingEvents.clear();
m_input.trackingMouseLeave = false;
m_input.modifierTracker.Reset();
m_composition.shellRuntime.ResetInteractionState();
m_chrome.chromeState = {};
m_chrome.runtime.EndBorderlessResize();
m_chrome.runtime.EndBorderlessWindowDragRestore();
m_chrome.runtime.EndInteractiveResize();
m_chrome.runtime.SetHoveredBorderlessResizeEdge(Host::BorderlessWindowResizeEdge::None);
m_chrome.runtime.ClearPredictedClientPixelSize();
m_state->input.pendingEvents.clear();
m_state->input.trackingMouseLeave = false;
m_state->input.modifierTracker.Reset();
m_state->composition.shellRuntime.ResetInteractionState();
m_state->chrome.chromeState = {};
m_state->chrome.runtime.EndBorderlessResize();
m_state->chrome.runtime.EndBorderlessWindowDragRestore();
m_state->chrome.runtime.EndInteractiveResize();
m_state->chrome.runtime.SetHoveredBorderlessResizeEdge(
Host::BorderlessWindowResizeEdge::None);
m_state->chrome.runtime.ClearPredictedClientPixelSize();
}
bool EditorWindow::ApplyWindowResize(UINT width, UINT height) {
if (!m_render.ready || width == 0u || height == 0u) {
if (!m_state->render.ready || width == 0u || height == 0u) {
return false;
}
const Host::D3D12WindowRenderLoopResizeResult resizeResult =
m_render.windowRenderLoop.ApplyResize(width, height);
m_composition.shellRuntime.SetViewportSurfacePresentationEnabled(
m_state->render.windowRenderLoop.ApplyResize(width, height);
m_state->composition.shellRuntime.SetViewportSurfacePresentationEnabled(
resizeResult.hasViewportSurfacePresentation);
if (!resizeResult.windowRendererWarning.empty()) {
@@ -322,12 +332,12 @@ bool EditorWindow::ApplyWindowResize(UINT width, UINT height) {
bool EditorWindow::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const {
outWidth = 0u;
outHeight = 0u;
if (m_window.hwnd == nullptr || !IsWindow(m_window.hwnd)) {
if (m_state->window.hwnd == nullptr || !IsWindow(m_state->window.hwnd)) {
return false;
}
RECT clientRect = {};
if (!GetClientRect(m_window.hwnd, &clientRect)) {
if (!GetClientRect(m_state->window.hwnd, &clientRect)) {
return false;
}
@@ -343,7 +353,7 @@ bool EditorWindow::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight)
}
bool EditorWindow::ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight) const {
if (m_chrome.runtime.TryGetPredictedClientPixelSize(outWidth, outHeight)) {
if (m_state->chrome.runtime.TryGetPredictedClientPixelSize(outWidth, outHeight)) {
return true;
}
@@ -351,7 +361,7 @@ bool EditorWindow::ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight)
}
float EditorWindow::GetDpiScale() const {
return m_chrome.runtime.GetDpiScale(kBaseDpiScale);
return m_state->chrome.runtime.GetDpiScale(kBaseDpiScale);
}
float EditorWindow::PixelsToDips(float pixels) const {
@@ -367,11 +377,11 @@ UIPoint EditorWindow::ConvertClientPixelsToDips(LONG x, LONG y) const {
UIPoint EditorWindow::ConvertScreenPixelsToClientDips(const POINT& screenPoint) const {
POINT clientPoint = screenPoint;
if (m_window.hwnd != nullptr) {
ScreenToClient(m_window.hwnd, &clientPoint);
if (m_state->window.hwnd != nullptr) {
ScreenToClient(m_state->window.hwnd, &clientPoint);
}
const float dpiScale = m_chrome.runtime.GetDpiScale(kBaseDpiScale);
const float dpiScale = m_state->chrome.runtime.GetDpiScale(kBaseDpiScale);
return UIPoint(
dpiScale > 0.0f
? static_cast<float>(clientPoint.x) / dpiScale
@@ -385,15 +395,17 @@ void EditorWindow::OnResize(UINT width, UINT height) {
bool matchesPredictedClientSize = false;
UINT predictedWidth = 0u;
UINT predictedHeight = 0u;
if (m_chrome.runtime.TryGetPredictedClientPixelSize(predictedWidth, predictedHeight)) {
if (m_state->chrome.runtime.TryGetPredictedClientPixelSize(
predictedWidth,
predictedHeight)) {
matchesPredictedClientSize =
predictedWidth == width &&
predictedHeight == height;
}
m_chrome.runtime.ClearPredictedClientPixelSize();
if (IsBorderlessWindowEnabled() && m_window.hwnd != nullptr) {
Host::RefreshBorderlessWindowDwmDecorations(m_window.hwnd);
m_state->chrome.runtime.ClearPredictedClientPixelSize();
if (IsBorderlessWindowEnabled() && m_state->window.hwnd != nullptr) {
Host::RefreshBorderlessWindowDwmDecorations(m_state->window.hwnd);
}
if (!matchesPredictedClientSize) {
@@ -402,12 +414,12 @@ void EditorWindow::OnResize(UINT width, UINT height) {
}
void EditorWindow::OnEnterSizeMove() {
m_chrome.runtime.BeginInteractiveResize();
m_state->chrome.runtime.BeginInteractiveResize();
}
void EditorWindow::OnExitSizeMove() {
m_chrome.runtime.EndInteractiveResize();
m_chrome.runtime.ClearPredictedClientPixelSize();
m_state->chrome.runtime.EndInteractiveResize();
m_state->chrome.runtime.ClearPredictedClientPixelSize();
UINT width = 0u;
UINT height = 0u;
if (QueryCurrentClientPixelSize(width, height)) {
@@ -416,13 +428,13 @@ void EditorWindow::OnExitSizeMove() {
}
void EditorWindow::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
m_chrome.runtime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
m_render.renderer.SetDpiScale(GetDpiScale());
if (m_window.hwnd != nullptr) {
m_state->chrome.runtime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
m_state->render.renderer.SetDpiScale(GetDpiScale());
if (m_state->window.hwnd != nullptr) {
const LONG windowWidth = suggestedRect.right - suggestedRect.left;
const LONG windowHeight = suggestedRect.bottom - suggestedRect.top;
SetWindowPos(
m_window.hwnd,
m_state->window.hwnd,
nullptr,
suggestedRect.left,
suggestedRect.top,
@@ -434,11 +446,11 @@ void EditorWindow::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
if (QueryCurrentClientPixelSize(clientWidth, clientHeight)) {
ApplyWindowResize(clientWidth, clientHeight);
}
Host::RefreshBorderlessWindowDwmDecorations(m_window.hwnd);
Host::RefreshBorderlessWindowDwmDecorations(m_state->window.hwnd);
}
std::ostringstream trace = {};
trace << "dpi changed to " << m_chrome.runtime.GetWindowDpi()
trace << "dpi changed to " << m_state->chrome.runtime.GetWindowDpi()
<< " scale=" << GetDpiScale();
LogRuntimeTrace("window", trace.str());
}
@@ -449,7 +461,7 @@ bool EditorWindow::IsVerboseRuntimeTraceEnabled() {
}
void EditorWindow::UpdateCachedTitleText() {
m_window.titleText = WideToUtf8(m_window.title);
m_state->window.titleText = WideToUtf8(m_state->window.title);
}
} // namespace XCEngine::UI::Editor::App

View File

@@ -1,5 +1,6 @@
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowConstants.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include <algorithm>
@@ -10,13 +11,13 @@ namespace XCEngine::UI::Editor::App {
using namespace EditorWindowInternal;
bool EditorWindow::UpdateBorderlessWindowChromeHover(LPARAM lParam) {
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() !=
if (m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() !=
Host::BorderlessWindowResizeEdge::None ||
m_chrome.runtime.IsBorderlessResizeActive()) {
m_state->chrome.runtime.IsBorderlessResizeActive()) {
const bool changed =
m_chrome.chromeState.hoveredTarget !=
m_state->chrome.chromeState.hoveredTarget !=
Host::BorderlessWindowChromeHitTarget::None;
m_chrome.chromeState.hoveredTarget =
m_state->chrome.chromeState.hoveredTarget =
Host::BorderlessWindowChromeHitTarget::None;
return changed;
}
@@ -29,18 +30,18 @@ bool EditorWindow::UpdateBorderlessWindowChromeHover(LPARAM lParam) {
hitTarget == Host::BorderlessWindowChromeHitTarget::CloseButton
? hitTarget
: Host::BorderlessWindowChromeHitTarget::None;
if (m_chrome.chromeState.hoveredTarget == buttonTarget) {
if (m_state->chrome.chromeState.hoveredTarget == buttonTarget) {
return false;
}
m_chrome.chromeState.hoveredTarget = buttonTarget;
m_state->chrome.chromeState.hoveredTarget = buttonTarget;
return true;
}
bool EditorWindow::HandleBorderlessWindowChromeButtonDown(LPARAM lParam) {
if (m_chrome.runtime.GetHoveredBorderlessResizeEdge() !=
if (m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() !=
Host::BorderlessWindowResizeEdge::None ||
m_chrome.runtime.IsBorderlessResizeActive()) {
m_state->chrome.runtime.IsBorderlessResizeActive()) {
return false;
}
@@ -50,23 +51,23 @@ bool EditorWindow::HandleBorderlessWindowChromeButtonDown(LPARAM lParam) {
case Host::BorderlessWindowChromeHitTarget::MinimizeButton:
case Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton:
case Host::BorderlessWindowChromeHitTarget::CloseButton:
m_chrome.chromeState.pressedTarget = hitTarget;
m_state->chrome.chromeState.pressedTarget = hitTarget;
AcquirePointerCapture(EditorWindowPointerCaptureOwner::BorderlessChrome);
InvalidateHostWindow();
return true;
case Host::BorderlessWindowChromeHitTarget::DragRegion:
if (m_window.hwnd != nullptr) {
if (m_state->window.hwnd != nullptr) {
if (IsBorderlessWindowMaximized()) {
POINT screenPoint = {};
if (GetCursorPos(&screenPoint)) {
m_chrome.runtime.BeginBorderlessWindowDragRestore(screenPoint);
m_state->chrome.runtime.BeginBorderlessWindowDragRestore(screenPoint);
AcquirePointerCapture(EditorWindowPointerCaptureOwner::BorderlessChrome);
return true;
}
}
ForceReleasePointerCapture();
SendMessageW(m_window.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
SendMessageW(m_state->window.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
return true;
case Host::BorderlessWindowChromeHitTarget::None:
@@ -79,13 +80,13 @@ bool EditorWindow::HandleBorderlessWindowChromeButtonUp(
EditorContext& editorContext,
bool globalTabDragActive,
LPARAM lParam) {
if (m_chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
if (m_state->chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
ClearBorderlessWindowChromeDragRestoreState();
return true;
}
const Host::BorderlessWindowChromeHitTarget pressedTarget =
m_chrome.chromeState.pressedTarget;
m_state->chrome.chromeState.pressedTarget;
if (pressedTarget != Host::BorderlessWindowChromeHitTarget::MinimizeButton &&
pressedTarget != Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton &&
pressedTarget != Host::BorderlessWindowChromeHitTarget::CloseButton) {
@@ -94,7 +95,7 @@ bool EditorWindow::HandleBorderlessWindowChromeButtonUp(
const Host::BorderlessWindowChromeHitTarget releasedTarget =
HitTestBorderlessWindowChrome(lParam);
m_chrome.chromeState.pressedTarget =
m_state->chrome.chromeState.pressedTarget =
Host::BorderlessWindowChromeHitTarget::None;
ReleasePointerCapture(EditorWindowPointerCaptureOwner::BorderlessChrome);
InvalidateHostWindow();
@@ -112,7 +113,7 @@ bool EditorWindow::HandleBorderlessWindowChromeDoubleClick(
EditorContext& editorContext,
bool globalTabDragActive,
LPARAM lParam) {
if (m_chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
if (m_state->chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
ClearBorderlessWindowChromeDragRestoreState();
}
@@ -131,7 +132,8 @@ bool EditorWindow::HandleBorderlessWindowChromeDoubleClick(
bool EditorWindow::HandleBorderlessWindowChromeDragRestorePointerMove(
EditorContext& editorContext,
bool globalTabDragActive) {
if (!m_chrome.runtime.IsBorderlessWindowDragRestoreArmed() || m_window.hwnd == nullptr) {
if (!m_state->chrome.runtime.IsBorderlessWindowDragRestoreArmed() ||
m_state->window.hwnd == nullptr) {
return false;
}
@@ -141,7 +143,7 @@ bool EditorWindow::HandleBorderlessWindowChromeDragRestorePointerMove(
}
const POINT initialScreenPoint =
m_chrome.runtime.GetBorderlessWindowDragRestoreInitialScreenPoint();
m_state->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;
@@ -154,7 +156,7 @@ bool EditorWindow::HandleBorderlessWindowChromeDragRestorePointerMove(
RECT restoreRect = {};
RECT currentRect = {};
RECT workAreaRect = {};
if (!m_chrome.runtime.TryGetBorderlessWindowRestoreRect(restoreRect) ||
if (!m_state->chrome.runtime.TryGetBorderlessWindowRestoreRect(restoreRect) ||
!QueryCurrentWindowRect(currentRect) ||
!QueryBorderlessWindowWorkAreaRect(workAreaRect)) {
ClearBorderlessWindowChromeDragRestoreState();
@@ -193,34 +195,34 @@ bool EditorWindow::HandleBorderlessWindowChromeDragRestorePointerMove(
newTop + restoreHeight
};
m_chrome.runtime.SetBorderlessWindowMaximized(false);
m_state->chrome.runtime.SetBorderlessWindowMaximized(false);
ApplyPredictedWindowRectTransition(
editorContext,
globalTabDragActive,
targetRect);
ClearBorderlessWindowChromeDragRestoreState();
SendMessageW(m_window.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
SendMessageW(m_state->window.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
return true;
}
void EditorWindow::ClearBorderlessWindowChromeDragRestoreState() {
if (!m_chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
if (!m_state->chrome.runtime.IsBorderlessWindowDragRestoreArmed()) {
return;
}
m_chrome.runtime.EndBorderlessWindowDragRestore();
m_state->chrome.runtime.EndBorderlessWindowDragRestore();
ReleasePointerCapture(EditorWindowPointerCaptureOwner::BorderlessChrome);
}
void EditorWindow::ClearBorderlessWindowChromeState() {
if (m_chrome.chromeState.hoveredTarget ==
if (m_state->chrome.chromeState.hoveredTarget ==
Host::BorderlessWindowChromeHitTarget::None &&
m_chrome.chromeState.pressedTarget ==
m_state->chrome.chromeState.pressedTarget ==
Host::BorderlessWindowChromeHitTarget::None) {
return;
}
m_chrome.chromeState = {};
m_state->chrome.chromeState = {};
InvalidateHostWindow();
}
@@ -228,19 +230,19 @@ void EditorWindow::ExecuteBorderlessWindowChromeAction(
EditorContext& editorContext,
bool globalTabDragActive,
Host::BorderlessWindowChromeHitTarget target) {
if (m_window.hwnd == nullptr) {
if (m_state->window.hwnd == nullptr) {
return;
}
switch (target) {
case Host::BorderlessWindowChromeHitTarget::MinimizeButton:
ShowWindow(m_window.hwnd, SW_MINIMIZE);
ShowWindow(m_state->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);
PostMessageW(m_state->window.hwnd, WM_CLOSE, 0, 0);
break;
case Host::BorderlessWindowChromeHitTarget::DragRegion:
case Host::BorderlessWindowChromeHitTarget::None:

View File

@@ -1,5 +1,6 @@
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowConstants.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include "Platform/Win32/EditorWindowStyle.h"
#include <XCEditor/Foundation/UIEditorTheme.h>
@@ -100,17 +101,18 @@ UIRect BuildDetachedTitleLogoRect(const Host::BorderlessWindowChromeLayout& layo
} // namespace
bool EditorWindow::ShouldUseDetachedTitleBarTabStrip() const {
return !m_window.primary && HasSingleVisibleRootTab(m_composition.workspaceController);
return !m_state->window.primary &&
HasSingleVisibleRootTab(m_state->composition.workspaceController);
}
Host::BorderlessWindowChromeHitTarget EditorWindow::HitTestBorderlessWindowChrome(
LPARAM lParam) const {
if (!IsBorderlessWindowEnabled() || m_window.hwnd == nullptr) {
if (!IsBorderlessWindowEnabled() || m_state->window.hwnd == nullptr) {
return Host::BorderlessWindowChromeHitTarget::None;
}
RECT clientRect = {};
if (!GetClientRect(m_window.hwnd, &clientRect)) {
if (!GetClientRect(m_state->window.hwnd, &clientRect)) {
return Host::BorderlessWindowChromeHitTarget::None;
}
@@ -156,11 +158,11 @@ void EditorWindow::AppendBorderlessWindowChrome(
1.0f);
}
if (!m_window.primary) {
if (m_render.titleBarLogoIcon.IsValid()) {
if (!m_state->window.primary) {
if (m_state->render.titleBarLogoIcon.IsValid()) {
drawList.AddImage(
BuildDetachedTitleLogoRect(layout),
m_render.titleBarLogoIcon,
m_state->render.titleBarLogoIcon,
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
}
} else {
@@ -168,19 +170,21 @@ void EditorWindow::AppendBorderlessWindowChrome(
const float iconY =
layout.titleBarRect.y +
(std::max)(0.0f, (layout.titleBarRect.height - kTitleBarLogoExtent) * 0.5f);
if (m_render.titleBarLogoIcon.IsValid()) {
if (m_state->render.titleBarLogoIcon.IsValid()) {
drawList.AddImage(
UIRect(iconX, iconY, kTitleBarLogoExtent, kTitleBarLogoExtent),
m_render.titleBarLogoIcon,
m_state->render.titleBarLogoIcon,
UIColor(1.0f, 1.0f, 1.0f, 1.0f));
}
const std::string& titleText =
m_window.titleText.empty() ? std::string("XCEngine Editor") : m_window.titleText;
m_state->window.titleText.empty()
? std::string("XCEngine Editor")
: m_state->window.titleText;
drawList.AddText(
UIPoint(
iconX +
(m_render.titleBarLogoIcon.IsValid()
(m_state->render.titleBarLogoIcon.IsValid()
? (kTitleBarLogoExtent + kTitleBarLogoTextGap)
: 4.0f),
layout.titleBarRect.y +
@@ -195,7 +199,7 @@ void EditorWindow::AppendBorderlessWindowChrome(
Host::AppendBorderlessWindowChrome(
drawList,
layout,
m_chrome.chromeState,
m_state->chrome.chromeState,
IsBorderlessWindowMaximized());
}

View File

@@ -0,0 +1,33 @@
#pragma once
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <optional>
#include <string>
namespace XCEngine::UI::Editor::App {
struct EditorWindowPanelTransferRequest {
std::string nodeId = {};
std::string panelId = {};
POINT screenPoint = {};
bool IsValid() const {
return !nodeId.empty() && !panelId.empty();
}
};
struct EditorWindowFrameTransferRequests {
std::optional<EditorWindowPanelTransferRequest> beginGlobalTabDrag = {};
std::optional<EditorWindowPanelTransferRequest> detachPanel = {};
bool HasPendingRequests() const {
return beginGlobalTabDrag.has_value() || detachPanel.has_value();
}
};
} // namespace XCEngine::UI::Editor::App

View File

@@ -2,9 +2,6 @@
#include "Platform/Win32/EditorWindowManager.h"
#include <XCEditor/Workspace/UIEditorWindowWorkspaceController.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
namespace XCEngine::UI::Editor::App::Internal {
class EditorWindowHostRuntime final {

View File

@@ -5,6 +5,8 @@
#include "Platform/Win32/EditorWindow.h"
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <XCEditor/Workspace/UIEditorWindowWorkspaceController.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <algorithm>
#include <utility>

View File

@@ -1,5 +1,7 @@
#include "WindowMessageDispatcher.h"
#include "Composition/EditorShellRuntime.h"
#include "Platform/Win32/BorderlessWindowChrome.h"
#include "Platform/Win32/EditorWindow.h"
#include "Platform/Win32/EditorWindowPointerCapture.h"
#include "WindowMessageHost.h"