Files
XCEngine/editor/app/Windowing/EditorWindowInstance.cpp

493 lines
15 KiB
C++
Raw Normal View History

#include "Windowing/EditorWindowInstance.h"
#include "Support/StringEncoding.h"
#include "Windowing/Host/EditorWindowContentBindings.h"
#include "Windowing/Runtime/EditorWindowRuntimeController.h"
#include <XCEditor/Docking/UIEditorDockHostTransfer.h>
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <XCEngine/UI/DrawData.h>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <sstream>
#include <utility>
namespace XCEngine::UI::Editor::App {
using ::XCEngine::UI::UIColor;
using ::XCEngine::UI::UIDrawData;
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
EditorWindowInstance::EditorWindowInstance(
std::string windowId,
std::wstring title,
EditorWindowCategory category,
EditorWindowChromePolicy chromePolicy,
bool primary,
std::unique_ptr<EditorWindowRuntimeController> runtimeController)
: m_windowId(std::move(windowId))
, m_title(std::move(title))
, m_category(category)
, m_chromePolicy(chromePolicy)
, m_primary(primary)
, m_runtime(std::move(runtimeController)) {
UpdateCachedTitleText();
}
EditorWindowInstance::~EditorWindowInstance() = default;
std::string_view EditorWindowInstance::GetWindowId() const {
return m_windowId;
}
EditorWindowLifecycleState EditorWindowInstance::GetLifecycleState() const {
return m_lifecycle;
}
const EditorWindowChromePolicy& EditorWindowInstance::GetChromePolicy() const {
return m_chromePolicy;
}
bool EditorWindowInstance::IsPrimary() const {
return m_primary;
}
bool EditorWindowInstance::IsWorkspaceWindow() const {
return m_category == EditorWindowCategory::Workspace;
}
bool EditorWindowInstance::IsUtilityWindow() const {
return m_category == EditorWindowCategory::Utility;
}
bool EditorWindowInstance::IsClosing() const {
return m_lifecycle == EditorWindowLifecycleState::Closing;
}
bool EditorWindowInstance::IsDestroyed() const {
return m_lifecycle == EditorWindowLifecycleState::Destroyed;
}
bool EditorWindowInstance::HasLiveHostWindow() const {
return m_nativePeer != nullptr && m_nativePeer->HasLiveHostWindow();
}
const std::wstring& EditorWindowInstance::GetTitle() const {
return m_title;
}
std::string_view EditorWindowInstance::GetCachedTitleText() const {
return m_cachedTitleText;
}
const EditorWorkspaceWindowProjection*
EditorWindowInstance::TryGetWorkspaceProjection() const {
const EditorWindowWorkspaceBinding* workspaceBinding =
m_runtime != nullptr ? m_runtime->TryGetWorkspaceBinding() : nullptr;
return workspaceBinding != nullptr
? workspaceBinding->TryGetWorkspaceProjection()
: nullptr;
}
EditorWindowDockHostBinding* EditorWindowInstance::TryGetDockHostBinding() {
return m_runtime != nullptr ? m_runtime->TryGetDockHostBinding() : nullptr;
}
const EditorWindowDockHostBinding* EditorWindowInstance::TryGetDockHostBinding() const {
return m_runtime != nullptr ? m_runtime->TryGetDockHostBinding() : nullptr;
}
const EditorWindowInputFeedbackBinding*
EditorWindowInstance::TryGetInputFeedbackBinding() const {
return m_runtime != nullptr ? m_runtime->TryGetInputFeedbackBinding() : nullptr;
}
const EditorWindowTitleBarBinding* EditorWindowInstance::TryGetTitleBarBinding() const {
return m_runtime != nullptr ? m_runtime->TryGetTitleBarBinding() : nullptr;
}
const UIEditorShellInteractionFrame& EditorWindowInstance::GetShellFrame() const {
return m_runtime->GetShellFrame();
}
const UIEditorShellInteractionState& EditorWindowInstance::GetShellInteractionState() const {
return m_runtime->GetShellInteractionState();
}
::XCEngine::UI::UISize EditorWindowInstance::ResolveMinimumOuterSize() const {
return m_runtime->ResolveMinimumOuterSize();
}
UIEditorTextMeasurer& EditorWindowInstance::GetTextMeasurer() {
return m_runtime->GetTextMeasurer();
}
const UIEditorTextMeasurer& EditorWindowInstance::GetTextMeasurer() const {
return m_runtime->GetTextMeasurer();
}
const ::XCEngine::UI::UITextureHandle& EditorWindowInstance::GetTitleBarLogoIcon() const {
return m_runtime->GetTitleBarLogoIcon();
}
std::string EditorWindowInstance::BuildFrameRateText() const {
return m_runtime->BuildFrameRateText();
}
::XCEngine::UI::UIPoint EditorWindowInstance::ConvertScreenPixelsToClientDips(
const EditorWindowScreenPoint& screenPoint) const {
return m_nativePeer != nullptr
? m_nativePeer->ConvertScreenPixelsToClientDips(screenPoint)
: ::XCEngine::UI::UIPoint{};
}
bool EditorWindowInstance::TryResolveDockTabDragHotspot(
std::string_view nodeId,
std::string_view panelId,
const EditorWindowScreenPoint& screenPoint,
EditorWindowScreenPoint& outHotspot) const {
const EditorWindowDockHostBinding* dockHostBinding = TryGetDockHostBinding();
if (dockHostBinding == nullptr || m_nativePeer == nullptr) {
outHotspot = {};
return false;
}
const UIPoint clientPointDips = m_nativePeer->ConvertScreenPixelsToClientDips(screenPoint);
UIPoint hotspotDips = {};
if (!dockHostBinding->TryResolveDockTabDragHotspot(
nodeId,
panelId,
clientPointDips,
hotspotDips)) {
outHotspot = {};
return false;
}
const float dpiScale = m_nativePeer->CaptureMetrics().dpiScale;
outHotspot.x = static_cast<std::int32_t>(std::lround(hotspotDips.x * dpiScale));
outHotspot.y = static_cast<std::int32_t>(std::lround(hotspotDips.y * dpiScale));
return true;
}
bool EditorWindowInstance::TryResolveDockTabDropTarget(
const EditorWindowScreenPoint& screenPoint,
UIEditorDockHostTabDropTarget& outTarget) const {
const EditorWindowDockHostBinding* dockHostBinding = TryGetDockHostBinding();
if (dockHostBinding == nullptr || m_nativePeer == nullptr) {
outTarget = {};
return false;
}
outTarget = dockHostBinding->ResolveDockTabDropTarget(
m_nativePeer->ConvertScreenPixelsToClientDips(screenPoint));
return outTarget.valid;
}
void EditorWindowInstance::InvalidateHostWindow() const {
if (m_nativePeer != nullptr) {
m_nativePeer->InvalidateHostWindow();
}
}
void EditorWindowInstance::SetPrimary(bool primary) {
m_primary = primary;
}
void EditorWindowInstance::SetTitle(std::wstring title) {
m_title = std::move(title);
UpdateCachedTitleText();
}
void EditorWindowInstance::ApplyHostWindowTitle() {
if (m_nativePeer != nullptr) {
m_nativePeer->ApplyHostWindowTitle(m_title);
}
}
void EditorWindowInstance::RefreshWorkspaceProjection(
EditorWorkspaceWindowProjection projection) {
EditorWindowWorkspaceBinding* workspaceBinding =
m_runtime != nullptr ? m_runtime->TryGetWorkspaceBinding() : nullptr;
assert(workspaceBinding != nullptr);
workspaceBinding->RefreshWorkspaceProjection(std::move(projection));
}
void EditorWindowInstance::ResetInteractionState() {
if (m_nativePeer != nullptr) {
m_nativePeer->ResetNativeInteractionState();
}
if (m_runtime != nullptr) {
m_runtime->ResetInteractionState();
}
}
void EditorWindowInstance::SetDpiScale(float dpiScale) {
if (m_runtime != nullptr) {
m_runtime->SetDpiScale(dpiScale);
}
}
bool EditorWindowInstance::ApplyResize(std::uint32_t width, std::uint32_t height) {
if (m_runtime == nullptr || !m_runtime->IsReady() || width == 0u || height == 0u) {
return false;
}
return m_runtime->ApplyResize(width, height);
}
void EditorWindowInstance::AcquirePointerCapture(EditorWindowPointerCaptureOwner owner) {
if (m_nativePeer != nullptr) {
m_nativePeer->AcquirePointerCapture(owner);
}
}
void EditorWindowInstance::ReleasePointerCapture(EditorWindowPointerCaptureOwner owner) {
if (m_nativePeer != nullptr) {
m_nativePeer->ReleasePointerCapture(owner);
}
}
void EditorWindowInstance::AttachNativePeer(EditorWindowNativePeer& nativePeer) {
m_nativePeer = &nativePeer;
}
void EditorWindowInstance::DetachNativePeer(EditorWindowNativePeer& nativePeer) {
if (m_nativePeer == &nativePeer) {
m_nativePeer = nullptr;
}
}
void EditorWindowInstance::MarkNativeAttached() {
m_lifecycle = EditorWindowLifecycleState::NativeAttached;
}
void EditorWindowInstance::MarkInitializing() {
m_lifecycle = EditorWindowLifecycleState::Initializing;
}
void EditorWindowInstance::MarkRunning() {
m_lifecycle = EditorWindowLifecycleState::Running;
}
void EditorWindowInstance::MarkClosing() {
m_lifecycle = EditorWindowLifecycleState::Closing;
}
void EditorWindowInstance::MarkDestroyed() {
m_lifecycle = EditorWindowLifecycleState::Destroyed;
}
bool EditorWindowInstance::IsRenderReady() const {
return m_runtime != nullptr && m_runtime->IsReady();
}
bool EditorWindowInstance::InitializeRuntime(
const EditorHostWindowRuntimeInitializationParams& params) {
if (m_nativePeer == nullptr) {
AppendUIEditorRuntimeTrace("app", "window initialize skipped: native window is null");
return false;
}
m_nativePeer->PrepareRuntimeInitialization(*this);
EditorNativeWindowRuntimeSurface runtimeSurface = {};
if (!m_nativePeer->CaptureRuntimeSurface(*this, runtimeSurface) ||
!runtimeSurface.IsValid()) {
AppendUIEditorRuntimeTrace("app", "window initialize skipped: native surface is invalid");
return false;
}
m_runtime->SetDpiScale(runtimeSurface.dpiScale);
std::ostringstream dpiTrace = {};
dpiTrace << "initial dpiScale=" << runtimeSurface.dpiScale;
AppendUIEditorRuntimeTrace("window", dpiTrace.str());
MarkInitializing();
const bool initialized = m_runtime->Initialize(
2026-04-26 23:30:29 +08:00
Rendering::Host::EditorWindowRenderRuntimeInitializeParams{
.surface = runtimeSurface.renderSurface,
.widthPixels = runtimeSurface.widthPixels,
.heightPixels = runtimeSurface.heightPixels,
},
params.repoRoot,
params.captureRoot,
params.autoCaptureOnStartup);
if (initialized) {
MarkRunning();
} else {
MarkNativeAttached();
}
return initialized;
}
EditorWindowFrameTransferRequests EditorWindowInstance::RenderHostFrame(
bool globalTabDragActive) {
if (m_runtime == nullptr || !m_runtime->IsReady() || m_nativePeer == nullptr) {
return {};
}
EditorNativeWindowFrameSnapshot frameSnapshot = {};
if (!m_nativePeer->CaptureFrameSnapshot(
*this,
m_runtime->GetShellInteractionState(),
frameSnapshot) ||
!frameSnapshot.IsValid()) {
return {};
}
UIDrawData drawData = {};
UIDrawList& backgroundDrawList = drawData.EmplaceDrawList("XCEditorWindow.Surface");
backgroundDrawList.AddFilledRect(
UIRect(0.0f, 0.0f, frameSnapshot.widthDips, frameSnapshot.heightDips),
kShellSurfaceColor);
EditorWindowFrameTransferRequests transferRequests = {};
if (m_runtime->IsEditorContextValid()) {
transferRequests =
RenderRuntimeFrame(globalTabDragActive, frameSnapshot, drawData);
} else {
UIDrawList& invalidDrawList = drawData.EmplaceDrawList("XCEditorWindow.Invalid");
m_runtime->AppendInvalidFrame(invalidDrawList);
}
UIDrawList& windowChromeDrawList = drawData.EmplaceDrawList("XCEditorWindow.Chrome");
m_nativePeer->AppendChrome(*this, windowChromeDrawList, frameSnapshot.widthDips);
const auto presentResult = m_runtime->Present(drawData);
if (!presentResult.warning.empty()) {
AppendUIEditorRuntimeTrace("present", presentResult.warning);
}
return transferRequests;
}
void EditorWindowInstance::ValidateHostFrame() const {
if (m_nativePeer != nullptr) {
m_nativePeer->ValidateHostFrame();
}
}
void EditorWindowInstance::RequestSkipNextSteadyStateFrame() {
if (m_nativePeer != nullptr) {
m_nativePeer->RequestSkipNextSteadyStateFrame();
}
}
bool EditorWindowInstance::ConsumeSkipNextSteadyStateFrame() {
return m_nativePeer != nullptr && m_nativePeer->ConsumeSkipNextSteadyStateFrame();
}
void EditorWindowInstance::Shutdown() {
std::ostringstream trace = {};
trace << "EditorWindowInstance::Shutdown begin windowId='" << GetWindowId()
<< "' primary=" << (IsPrimary() ? 1 : 0)
<< " lifecycle=" << GetEditorWindowLifecycleStateName(GetLifecycleState())
<< " runtimeReady=" << (IsRenderReady() ? 1 : 0);
AppendUIEditorRuntimeTrace("window-close", trace.str());
if (m_nativePeer != nullptr) {
m_nativePeer->ShutdownNativeInteraction();
}
if (m_runtime != nullptr && m_runtime->IsReady()) {
m_runtime->Shutdown();
}
AppendUIEditorRuntimeTrace(
"window-close",
"EditorWindowInstance::Shutdown end windowId='" + std::string(GetWindowId()) + "'");
}
bool EditorWindowInstance::TryGetHostScreenRect(EditorWindowScreenRect& outRect) const {
outRect = {};
return m_nativePeer != nullptr && m_nativePeer->TryGetHostScreenRect(outRect);
}
void EditorWindowInstance::SetHostScreenPosition(const EditorWindowScreenPoint& screenPoint) {
if (m_nativePeer != nullptr) {
m_nativePeer->SetHostScreenPosition(screenPoint);
}
}
void EditorWindowInstance::FocusHostWindow() {
if (m_nativePeer != nullptr) {
m_nativePeer->FocusHostWindow();
}
}
void EditorWindowInstance::PostCloseToHost() {
if (m_nativePeer != nullptr) {
m_nativePeer->PostCloseToHost();
}
}
void EditorWindowInstance::DestroyHostWindow() {
if (m_nativePeer != nullptr) {
m_nativePeer->DestroyHostWindow();
}
}
void EditorWindowInstance::RequestManualScreenshot(std::string reason) {
if (m_runtime != nullptr) {
m_runtime->RequestManualScreenshot(std::move(reason));
}
}
void EditorWindowInstance::UpdateCachedTitleText() {
m_cachedTitleText = WideToUtf8(m_title);
}
EditorWindowFrameTransferRequests EditorWindowInstance::RenderRuntimeFrame(
bool globalTabDragActive,
const EditorNativeWindowFrameSnapshot& frameSnapshot,
UIDrawData& drawData) {
if (m_nativePeer == nullptr) {
return {};
}
m_runtime->PrepareEditorContext();
const auto frameContext = m_runtime->BeginFrame();
if (!frameContext.warning.empty()) {
AppendUIEditorRuntimeTrace("viewport", frameContext.warning);
}
const EditorWindowFrameTransferRequests transferRequests =
m_runtime->UpdateAndAppend(
frameSnapshot.workspaceBounds,
frameSnapshot.inputEvents,
frameSnapshot.cursorScreenPoint,
IsPrimary(),
globalTabDragActive,
frameSnapshot.useDetachedTitleBarTabStrip,
drawData);
if (frameContext.canRenderViewports) {
m_runtime->RenderRequestedViewports(frameContext.renderContext);
}
m_nativePeer->ApplyFrameCommands(
*this,
EditorNativeWindowFrameCommands{
.applyShellRuntimePointerCapture = true,
.applyCurrentCursor = true,
});
return transferRequests;
}
std::unique_ptr<EditorWindowInstance> CreateEditorWindowInstance(
std::string windowId,
std::wstring title,
EditorWindowCategory category,
EditorWindowChromePolicy chromePolicy,
bool primary,
std::unique_ptr<EditorWindowRuntimeController> runtimeController) {
return std::make_unique<EditorWindowInstance>(
std::move(windowId),
std::move(title),
category,
chromePolicy,
primary,
std::move(runtimeController));
}
} // namespace XCEngine::UI::Editor::App