331 lines
11 KiB
C++
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
|