230 lines
7.4 KiB
C++
230 lines
7.4 KiB
C++
#include "Platform/Win32/EditorWindow.h"
|
|
#include "Platform/Win32/EditorWindowConstants.h"
|
|
#include "Platform/Win32/EditorWindowFrameOrchestrator.h"
|
|
#include "Platform/Win32/EditorWindowInputController.h"
|
|
#include "Platform/Win32/EditorWindowInternalState.h"
|
|
#include "Platform/Win32/EditorWindowRuntimeController.h"
|
|
#include "Platform/Win32/EditorWindowRuntimeInternal.h"
|
|
#include "Platform/Win32/EditorWindowStyle.h"
|
|
#include "Composition/EditorShellPointerInteraction.h"
|
|
#include "Composition/EditorContext.h"
|
|
|
|
#include <algorithm>
|
|
#include <XCEngine/UI/DrawData.h>
|
|
|
|
#include <cstdint>
|
|
#include <utility>
|
|
|
|
namespace XCEngine::UI::Editor::App {
|
|
|
|
using namespace EditorWindowInternal;
|
|
using ::XCEngine::UI::UIDrawData;
|
|
using ::XCEngine::UI::UIDrawList;
|
|
using ::XCEngine::UI::UIInputEvent;
|
|
using ::XCEngine::UI::UIInputModifiers;
|
|
using ::XCEngine::UI::UIPointerButton;
|
|
using ::XCEngine::UI::UIRect;
|
|
|
|
namespace {
|
|
|
|
std::uint8_t ButtonMask(UIPointerButton button) {
|
|
switch (button) {
|
|
case UIPointerButton::Left: return 1u << 0u;
|
|
case UIPointerButton::Right: return 1u << 1u;
|
|
case UIPointerButton::Middle: return 1u << 2u;
|
|
case UIPointerButton::X1: return 1u << 3u;
|
|
case UIPointerButton::X2: return 1u << 4u;
|
|
case UIPointerButton::None:
|
|
default:
|
|
return 0u;
|
|
}
|
|
}
|
|
|
|
std::uint8_t ButtonMaskFromModifiers(const UIInputModifiers& modifiers) {
|
|
std::uint8_t mask = 0u;
|
|
if (modifiers.leftMouse) {
|
|
mask |= ButtonMask(UIPointerButton::Left);
|
|
}
|
|
if (modifiers.rightMouse) {
|
|
mask |= ButtonMask(UIPointerButton::Right);
|
|
}
|
|
if (modifiers.middleMouse) {
|
|
mask |= ButtonMask(UIPointerButton::Middle);
|
|
}
|
|
if (modifiers.x1Mouse) {
|
|
mask |= ButtonMask(UIPointerButton::X1);
|
|
}
|
|
if (modifiers.x2Mouse) {
|
|
mask |= ButtonMask(UIPointerButton::X2);
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
std::uint8_t ResolveExpectedShellCaptureButtons(
|
|
const EditorShellRuntime& shellRuntime) {
|
|
std::uint8_t expectedButtons = 0u;
|
|
const auto& shellState = shellRuntime.GetShellInteractionState();
|
|
const auto& dockHostState =
|
|
shellState.workspaceInteractionState.dockHostInteractionState;
|
|
if (dockHostState.splitterDragState.active ||
|
|
!dockHostState.activeTabDragNodeId.empty()) {
|
|
expectedButtons |= ButtonMask(UIPointerButton::Left);
|
|
}
|
|
|
|
for (const auto& panelState :
|
|
shellState.workspaceInteractionState.composeState.panelStates) {
|
|
const auto& inputBridgeState = panelState.viewportShellState.inputBridgeState;
|
|
if (inputBridgeState.captured) {
|
|
expectedButtons |= ButtonMask(inputBridgeState.captureButton);
|
|
}
|
|
}
|
|
|
|
return expectedButtons;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
EditorWindowFrameTransferRequests EditorWindow::RenderFrame(
|
|
EditorContext& editorContext,
|
|
bool globalTabDragActive) {
|
|
if (!m_runtime->IsReady() || m_state->window.hwnd == nullptr) {
|
|
return {};
|
|
}
|
|
|
|
UINT pixelWidth = 0u;
|
|
UINT pixelHeight = 0u;
|
|
if (!ResolveRenderClientPixelSize(pixelWidth, pixelHeight)) {
|
|
return {};
|
|
}
|
|
|
|
const float width = PixelsToDips(static_cast<float>(pixelWidth));
|
|
const float height = PixelsToDips(static_cast<float>(pixelHeight));
|
|
const UIRect workspaceBounds = ResolveWorkspaceBounds(width, height);
|
|
|
|
UIDrawData drawData = {};
|
|
UIDrawList& drawList = drawData.EmplaceDrawList("XCEditorShell");
|
|
drawList.AddFilledRect(
|
|
UIRect(0.0f, 0.0f, width, height),
|
|
kShellSurfaceColor);
|
|
|
|
EditorWindowFrameTransferRequests transferRequests = {};
|
|
if (editorContext.IsValid()) {
|
|
transferRequests =
|
|
RenderRuntimeFrame(editorContext, globalTabDragActive, workspaceBounds, drawList);
|
|
} else {
|
|
m_frameOrchestrator->AppendInvalidFrame(editorContext, drawList);
|
|
}
|
|
|
|
AppendBorderlessWindowChrome(drawList, width);
|
|
|
|
const Host::D3D12WindowRenderLoopPresentResult presentResult = m_runtime->Present(drawData);
|
|
if (!presentResult.warning.empty()) {
|
|
LogRuntimeTrace("present", presentResult.warning);
|
|
}
|
|
|
|
m_runtime->CaptureIfRequested(
|
|
drawData,
|
|
pixelWidth,
|
|
pixelHeight,
|
|
presentResult.framePresented);
|
|
return transferRequests;
|
|
}
|
|
|
|
EditorWindowFrameTransferRequests EditorWindow::OnPaintMessage(
|
|
EditorContext& editorContext,
|
|
bool globalTabDragActive) {
|
|
if (!m_runtime->IsReady() || m_state->window.hwnd == nullptr) {
|
|
return {};
|
|
}
|
|
|
|
PAINTSTRUCT paintStruct = {};
|
|
BeginPaint(m_state->window.hwnd, &paintStruct);
|
|
const EditorWindowFrameTransferRequests transferRequests =
|
|
RenderFrame(editorContext, globalTabDragActive);
|
|
EndPaint(m_state->window.hwnd, &paintStruct);
|
|
return transferRequests;
|
|
}
|
|
|
|
UIRect EditorWindow::ResolveWorkspaceBounds(float clientWidthDips, float clientHeightDips) const {
|
|
if (!IsBorderlessWindowEnabled()) {
|
|
return UIRect(0.0f, 0.0f, clientWidthDips, clientHeightDips);
|
|
}
|
|
|
|
if (ShouldUseDetachedTitleBarTabStrip()) {
|
|
return UIRect(0.0f, 0.0f, clientWidthDips, clientHeightDips);
|
|
}
|
|
|
|
const float titleBarHeight = (std::min)(kBorderlessTitleBarHeightDips, clientHeightDips);
|
|
return UIRect(
|
|
0.0f,
|
|
titleBarHeight,
|
|
clientWidthDips,
|
|
(std::max)(0.0f, clientHeightDips - titleBarHeight));
|
|
}
|
|
|
|
EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame(
|
|
EditorContext& editorContext,
|
|
bool globalTabDragActive,
|
|
const UIRect& workspaceBounds,
|
|
UIDrawList& drawList) {
|
|
SyncShellCapturedPointerButtonsFromSystemState();
|
|
std::vector<UIInputEvent> frameEvents = m_inputController->TakePendingEvents();
|
|
const bool useDetachedTitleBarTabStrip = ShouldUseDetachedTitleBarTabStrip();
|
|
const EditorWindowFrameTransferRequests transferRequests =
|
|
m_frameOrchestrator->UpdateAndAppend(
|
|
editorContext,
|
|
*m_runtime,
|
|
workspaceBounds,
|
|
frameEvents,
|
|
m_runtime->BuildCaptureStatusText(),
|
|
m_state->window.primary,
|
|
globalTabDragActive,
|
|
useDetachedTitleBarTabStrip,
|
|
drawList);
|
|
|
|
ApplyShellRuntimePointerCapture();
|
|
ApplyCurrentCursor();
|
|
return transferRequests;
|
|
}
|
|
|
|
void EditorWindow::SyncShellCapturedPointerButtonsFromSystemState() {
|
|
m_inputController->SyncInputModifiersFromSystemState();
|
|
|
|
const std::uint8_t expectedButtons = ResolveExpectedShellCaptureButtons(
|
|
m_runtime->GetShellRuntime());
|
|
if (expectedButtons == 0u ||
|
|
m_inputController->HasPendingPointerStateReconciliationEvent()) {
|
|
return;
|
|
}
|
|
|
|
const UIInputModifiers modifiers = m_inputController->GetCurrentModifiers();
|
|
if ((ButtonMaskFromModifiers(modifiers) & expectedButtons) == expectedButtons) {
|
|
return;
|
|
}
|
|
|
|
QueueSyntheticPointerStateSyncEvent(modifiers);
|
|
}
|
|
|
|
void EditorWindow::ApplyShellRuntimePointerCapture() {
|
|
const EditorShellPointerOwner owner = m_runtime->GetShellRuntime().GetPointerOwner();
|
|
if (IsShellPointerOwner(owner)) {
|
|
AcquirePointerCapture(EditorWindowPointerCaptureOwner::Shell);
|
|
return;
|
|
}
|
|
|
|
if (IsHostedContentPointerOwner(owner)) {
|
|
AcquirePointerCapture(EditorWindowPointerCaptureOwner::HostedContent);
|
|
return;
|
|
}
|
|
|
|
if (OwnsPointerCapture(EditorWindowPointerCaptureOwner::Shell)) {
|
|
ReleasePointerCapture(EditorWindowPointerCaptureOwner::Shell);
|
|
}
|
|
|
|
if (OwnsPointerCapture(EditorWindowPointerCaptureOwner::HostedContent)) {
|
|
ReleasePointerCapture(EditorWindowPointerCaptureOwner::HostedContent);
|
|
}
|
|
}
|
|
|
|
} // namespace XCEngine::UI::Editor::App
|