Files
XCEngine/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp

331 lines
11 KiB
C++

#include "Platform/Win32/Windowing/EditorWindowHostRuntime.h"
#include "Bootstrap/EditorResources.h"
#include "Composition/EditorContext.h"
#include "Platform/Win32/Chrome/EditorWindowChromeController.h"
#include "Platform/Win32/Windowing/EditorWindow.h"
#include "Windowing/Content/EditorWindowContentFactory.h"
#include "Windowing/Content/EditorWindowContentController.h"
#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h"
#include "Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h"
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
#include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h"
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <algorithm>
#include <cstdint>
#include <sstream>
namespace XCEngine::UI::Editor::App {
namespace {
std::string DescribeHwnd(HWND hwnd) {
std::ostringstream stream = {};
stream << "0x" << std::hex << std::uppercase
<< reinterpret_cast<std::uintptr_t>(hwnd);
return stream.str();
}
std::string DescribeHostWindows(
const std::vector<std::unique_ptr<EditorWindow>>& windows) {
std::ostringstream stream = {};
stream << "count=" << windows.size() << " [";
bool first = true;
for (const std::unique_ptr<EditorWindow>& window : windows) {
if (!first) {
stream << ", ";
}
first = false;
if (window == nullptr) {
stream << "<null>";
continue;
}
stream << window->GetWindowId()
<< "{hwnd=" << DescribeHwnd(window->GetHwnd())
<< ",primary=" << (window->IsPrimary() ? '1' : '0')
<< ",state=" << GetEditorWindowLifecycleStateName(window->GetLifecycleState())
<< '}';
}
stream << ']';
return stream.str();
}
} // namespace
EditorWindowHostRuntime::EditorWindowHostRuntime(
EditorWindowHostConfig hostConfig,
std::filesystem::path repoRoot,
EditorContext& editorContext,
EditorWindowContentFactory& contentFactory)
: m_hostConfig(hostConfig),
m_repoRoot(std::move(repoRoot)),
m_editorContext(editorContext),
m_contentFactory(contentFactory) {}
EditorWindowHostRuntime::~EditorWindowHostRuntime() = default;
EditorWindow* EditorWindowHostRuntime::CreateEditorWindow(
std::unique_ptr<EditorWindowContentController> contentController,
const CreateParams& params) {
if (contentController == nullptr) {
LogRuntimeTrace("window", "window creation failed: content controller is null");
return nullptr;
}
const EditorWindowContentCapabilities capabilities =
contentController->GetCapabilities();
if (params.category == EditorWindowCategory::Workspace && !capabilities.workspace) {
LogRuntimeTrace("window", "workspace window creation rejected: content is not workspace");
return nullptr;
}
if (params.category == EditorWindowCategory::Utility && !capabilities.utilityPanel) {
LogRuntimeTrace("window", "utility window creation rejected: content is not utility");
return nullptr;
}
auto windowPtr = std::make_unique<EditorWindow>(
params.windowId,
params.title.empty() ? std::wstring(L"XCEngine Editor") : params.title,
params.category,
params.chromePolicy,
params.primary,
std::move(contentController));
EditorWindow* const rawWindow = windowPtr.get();
m_windows.push_back(std::move(windowPtr));
const auto eraseRawWindow = [this, rawWindow]() {
const auto it = std::find_if(
m_windows.begin(),
m_windows.end(),
[rawWindow](const std::unique_ptr<EditorWindow>& candidate) {
return candidate.get() == rawWindow;
});
if (it != m_windows.end()) {
m_windows.erase(it);
}
};
m_pendingCreateWindow = rawWindow;
const DWORD windowStyle = params.nativeStylePolicy.useHostWindowStyle
? m_hostConfig.windowStyle
: params.nativeStylePolicy.windowStyle;
const HWND hwnd = CreateWindowExW(
params.nativeStylePolicy.extendedWindowStyle,
m_hostConfig.windowClassName,
rawWindow->GetTitle().c_str(),
windowStyle,
params.initialX,
params.initialY,
params.initialWidth,
params.initialHeight,
nullptr,
nullptr,
m_hostConfig.hInstance,
m_hostConfig.windowUserData);
m_pendingCreateWindow = nullptr;
if (hwnd == nullptr) {
eraseRawWindow();
return nullptr;
}
if (!rawWindow->HasHwnd()) {
rawWindow->AttachHwnd(hwnd);
}
auto failWindowInitialization = [&](std::string_view message) {
LogRuntimeTrace("window", std::string(message));
m_lifecycleCoordinator->AbortUnregisteredWindow(*rawWindow);
return static_cast<EditorWindow*>(nullptr);
};
const HICON bigIcon = static_cast<HICON>(
LoadImageW(
m_hostConfig.hInstance,
MAKEINTRESOURCEW(IDI_APP_ICON),
IMAGE_ICON,
0,
0,
LR_DEFAULTSIZE));
const HICON smallIcon = static_cast<HICON>(
LoadImageW(
m_hostConfig.hInstance,
MAKEINTRESOURCEW(IDI_APP_ICON_SMALL),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_DEFAULTCOLOR));
if (bigIcon != nullptr) {
SendMessageW(hwnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(bigIcon));
}
if (smallIcon != nullptr) {
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(smallIcon));
}
if (!rawWindow->Initialize(
m_repoRoot,
m_editorContext,
m_editorContext.GetShellAsset().captureRootPath,
params.autoCaptureOnStartup)) {
return failWindowInitialization("managed window initialization failed");
}
ShowWindow(hwnd, params.showCommand);
UpdateWindow(hwnd);
return rawWindow;
}
EditorWindow* EditorWindowHostRuntime::CreateWorkspaceWindow(
UIEditorWorkspaceController workspaceController,
const CreateParams& params) {
return CreateEditorWindow(
m_contentFactory.CreateWorkspaceContentController(
params.windowId,
std::move(workspaceController)),
params);
}
EditorWindow* EditorWindowHostRuntime::CreateUtilityWindow(
const EditorUtilityWindowDescriptor& descriptor,
const CreateParams& params) {
return CreateEditorWindow(
m_contentFactory.CreateUtilityContentController(descriptor),
params);
}
void EditorWindowHostRuntime::BindLifecycleCoordinator(
EditorWindowLifecycleCoordinator& lifecycleCoordinator) {
m_lifecycleCoordinator = &lifecycleCoordinator;
}
void EditorWindowHostRuntime::HandlePendingNativeWindowCreated(HWND hwnd) {
if (m_pendingCreateWindow != nullptr &&
m_pendingCreateWindow->GetLifecycleState() ==
EditorWindowLifecycleState::PendingNativeCreate &&
!m_pendingCreateWindow->HasHwnd()) {
m_pendingCreateWindow->AttachHwnd(hwnd);
}
}
bool EditorWindowHostRuntime::HasWindows() const {
return !m_windows.empty();
}
void EditorWindowHostRuntime::RenderAllWindows(
bool globalTabDragActive,
EditorWindowWorkspaceCoordinator& workspaceCoordinator,
EditorUtilityWindowCoordinator& utilityCoordinator) {
struct WindowFrameTransferBatch {
EditorWindow* sourceWindow = nullptr;
EditorWindowFrameTransferRequests requests = {};
};
std::vector<WindowFrameTransferBatch> transferBatches = {};
transferBatches.reserve(m_windows.size());
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
if (window == nullptr ||
window->GetHwnd() == nullptr ||
window->GetLifecycleState() != EditorWindowLifecycleState::Running) {
continue;
}
if (window->ConsumeSkipNextSteadyStateFrame()) {
workspaceCoordinator.RefreshWindowPresentation(*window);
continue;
}
EditorWindowFrameTransferRequests transferRequests =
EditorWindowFrameDriver::DriveFrame(
*window,
m_editorContext,
globalTabDragActive);
workspaceCoordinator.RefreshWindowPresentation(*window);
if (!transferRequests.HasPendingRequests()) {
continue;
}
transferBatches.push_back(WindowFrameTransferBatch{
window.get(),
std::move(transferRequests),
});
}
for (WindowFrameTransferBatch& batch : transferBatches) {
if (batch.sourceWindow == nullptr ||
batch.sourceWindow->GetHwnd() == nullptr ||
batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) {
continue;
}
workspaceCoordinator.HandleWindowFrameTransferRequests(
*batch.sourceWindow,
batch.requests);
utilityCoordinator.HandleWindowFrameTransferRequests(
*batch.sourceWindow,
batch.requests);
}
}
EditorWindow* EditorWindowHostRuntime::FindWindow(HWND hwnd) {
if (hwnd == nullptr) {
return nullptr;
}
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
if (window != nullptr && window->GetHwnd() == hwnd) {
return window.get();
}
}
return nullptr;
}
const EditorWindow* EditorWindowHostRuntime::FindWindow(HWND hwnd) const {
return const_cast<EditorWindowHostRuntime*>(this)->FindWindow(hwnd);
}
EditorWindow* EditorWindowHostRuntime::FindWindow(std::string_view windowId) {
if (windowId.empty()) {
return nullptr;
}
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
if (window != nullptr && window->GetWindowId() == windowId) {
return window.get();
}
}
return nullptr;
}
const EditorWindow* EditorWindowHostRuntime::FindWindow(std::string_view windowId) const {
return const_cast<EditorWindowHostRuntime*>(this)->FindWindow(windowId);
}
EditorWindow* EditorWindowHostRuntime::FindPrimaryWindow() {
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
if (window != nullptr && window->IsPrimary()) {
return window.get();
}
}
return nullptr;
}
const EditorWindow* EditorWindowHostRuntime::FindPrimaryWindow() const {
return const_cast<EditorWindowHostRuntime*>(this)->FindPrimaryWindow();
}
void EditorWindowHostRuntime::LogRuntimeTrace(
std::string_view channel,
std::string_view message) const {
AppendUIEditorRuntimeTrace(channel, message);
}
} // namespace XCEngine::UI::Editor::App