#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 #include #include #include 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(pixelWidth)); const float height = PixelsToDips(static_cast(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 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