#include "Platform/Win32/WindowManager/Internal.h" #include "Bootstrap/EditorResources.h" #include "State/EditorContext.h" #include "Platform/Win32/EditorWindow.h" #include #include #include namespace XCEngine::UI::Editor::App { EditorWindowManager::EditorWindowManager( EditorWindowHostConfig hostConfig, std::filesystem::path repoRoot, EditorContext& editorContext) : m_hostRuntime(std::make_unique( hostConfig, std::move(repoRoot), editorContext)) { m_workspaceCoordinator = std::make_unique(*m_hostRuntime); } EditorWindowManager::~EditorWindowManager() = default; EditorWindow* EditorWindowManager::CreateEditorWindow( UIEditorWorkspaceController workspaceController, const CreateParams& params) { return m_hostRuntime->CreateEditorWindow(std::move(workspaceController), params); } void EditorWindowManager::HandlePendingNativeWindowCreated(HWND hwnd) { m_hostRuntime->HandlePendingNativeWindowCreated(hwnd); } void EditorWindowManager::Shutdown() { m_workspaceCoordinator->EndGlobalTabDragSession(); m_hostRuntime->Shutdown(); } EditorWindow* EditorWindowManager::FindWindow(HWND hwnd) { return m_hostRuntime->FindWindow(hwnd); } const EditorWindow* EditorWindowManager::FindWindow(HWND hwnd) const { return m_hostRuntime->FindWindow(hwnd); } EditorWindow* EditorWindowManager::FindWindow(std::string_view windowId) { return m_hostRuntime->FindWindow(windowId); } const EditorWindow* EditorWindowManager::FindWindow(std::string_view windowId) const { return m_hostRuntime->FindWindow(windowId); } EditorWindow* EditorWindowManager::FindPrimaryWindow() { return m_hostRuntime->FindPrimaryWindow(); } const EditorWindow* EditorWindowManager::FindPrimaryWindow() const { return m_hostRuntime->FindPrimaryWindow(); } bool EditorWindowManager::HasWindows() const { return m_hostRuntime->HasWindows(); } void EditorWindowManager::DestroyClosedWindows() { m_hostRuntime->DestroyClosedWindows(); } void EditorWindowManager::RenderAllWindows() { m_hostRuntime->RenderAllWindows( m_workspaceCoordinator->IsGlobalTabDragActive(), *m_workspaceCoordinator); } bool EditorWindowManager::IsGlobalTabDragActive() const { return m_workspaceCoordinator->IsGlobalTabDragActive(); } bool EditorWindowManager::OwnsActiveGlobalTabDrag(std::string_view windowId) const { return m_workspaceCoordinator->OwnsActiveGlobalTabDrag(windowId); } void EditorWindowManager::EndGlobalTabDragSession() { m_workspaceCoordinator->EndGlobalTabDragSession(); } void EditorWindowManager::HandleDestroyedWindow(HWND hwnd) { m_hostRuntime->HandleDestroyedWindow(hwnd); } void EditorWindowManager::HandleWindowFrameTransferRequests( EditorWindow& sourceWindow, EditorWindowFrameTransferRequests&& transferRequests) { m_workspaceCoordinator->HandleWindowFrameTransferRequests( sourceWindow, std::move(transferRequests)); } bool EditorWindowManager::HandleGlobalTabDragPointerMove(HWND hwnd) { return m_workspaceCoordinator->HandleGlobalTabDragPointerMove(hwnd); } bool EditorWindowManager::HandleGlobalTabDragPointerButtonUp(HWND hwnd) { return m_workspaceCoordinator->HandleGlobalTabDragPointerButtonUp(hwnd); } } // namespace XCEngine::UI::Editor::App namespace XCEngine::UI::Editor::App::Internal { EditorWindowHostRuntime::EditorWindowHostRuntime( EditorWindowHostConfig hostConfig, std::filesystem::path repoRoot, EditorContext& editorContext) : m_hostConfig(hostConfig), m_repoRoot(std::move(repoRoot)), m_editorContext(editorContext) {} EditorWindowHostRuntime::~EditorWindowHostRuntime() = default; EditorWindow* EditorWindowHostRuntime::CreateEditorWindow( UIEditorWorkspaceController workspaceController, const CreateParams& params) { auto windowPtr = std::make_unique( params.windowId, params.title.empty() ? std::wstring(L"XCEngine Editor") : params.title, params.primary, std::move(workspaceController)); 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& candidate) { return candidate.get() == rawWindow; }); if (it != m_windows.end()) { m_windows.erase(it); } }; m_pendingCreateWindow = rawWindow; const HWND hwnd = CreateWindowExW( WS_EX_APPWINDOW, m_hostConfig.windowClassName, rawWindow->GetTitle().c_str(), m_hostConfig.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)); DestroyEditorWindow(*rawWindow); eraseRawWindow(); return static_cast(nullptr); }; const HICON bigIcon = static_cast( LoadImageW( m_hostConfig.hInstance, MAKEINTRESOURCEW(IDI_APP_ICON), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE)); const HICON smallIcon = static_cast( 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(bigIcon)); } if (smallIcon != nullptr) { SendMessageW(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast(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; } void EditorWindowHostRuntime::HandlePendingNativeWindowCreated(HWND hwnd) { if (m_pendingCreateWindow != nullptr && !m_pendingCreateWindow->HasHwnd()) { m_pendingCreateWindow->AttachHwnd(hwnd); } } void EditorWindowHostRuntime::Shutdown() { for (const std::unique_ptr& window : m_windows) { if (window != nullptr) { DestroyEditorWindow(*window); } } m_windows.clear(); m_pendingCreateWindow = nullptr; } bool EditorWindowHostRuntime::HasWindows() const { return !m_windows.empty(); } void EditorWindowHostRuntime::DestroyEditorWindow(EditorWindow& window) { const HWND hwnd = window.GetHwnd(); if (GetCapture() == hwnd) { ReleaseCapture(); } window.Shutdown(); if (hwnd != nullptr && IsWindow(hwnd)) { DestroyWindow(hwnd); } window.MarkDestroyed(); } void EditorWindowHostRuntime::DestroyClosedWindows() { for (auto it = m_windows.begin(); it != m_windows.end();) { EditorWindow* const window = it->get(); if (window == nullptr || window->GetHwnd() != nullptr) { ++it; continue; } if (m_pendingCreateWindow == window) { m_pendingCreateWindow = nullptr; } window->Shutdown(); it = m_windows.erase(it); } } void EditorWindowHostRuntime::RenderAllWindows( bool globalTabDragActive, EditorWindowWorkspaceCoordinator& workspaceCoordinator) { struct WindowFrameTransferBatch { EditorWindow* sourceWindow = nullptr; EditorWindowFrameTransferRequests requests = {}; }; std::vector transferBatches = {}; transferBatches.reserve(m_windows.size()); for (const std::unique_ptr& window : m_windows) { if (window == nullptr || window->GetHwnd() == nullptr || window->IsClosing()) { continue; } EditorWindowFrameTransferRequests transferRequests = window->RenderFrame(m_editorContext, globalTabDragActive); 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->IsClosing()) { continue; } workspaceCoordinator.HandleWindowFrameTransferRequests( *batch.sourceWindow, std::move(batch.requests)); } } void EditorWindowHostRuntime::HandleDestroyedWindow(HWND hwnd) { if (EditorWindow* window = FindWindow(hwnd); window != nullptr) { window->MarkDestroyed(); if (window->IsPrimary()) { for (const std::unique_ptr& otherWindow : m_windows) { if (otherWindow != nullptr && otherWindow.get() != window && otherWindow->GetHwnd() != nullptr && !otherWindow->IsClosing()) { otherWindow->MarkClosing(); PostMessageW(otherWindow->GetHwnd(), WM_CLOSE, 0, 0); } } } } } EditorWindow* EditorWindowHostRuntime::FindWindow(HWND hwnd) { if (hwnd == nullptr) { return nullptr; } for (const std::unique_ptr& window : m_windows) { if (window != nullptr && window->GetHwnd() == hwnd) { return window.get(); } } return nullptr; } const EditorWindow* EditorWindowHostRuntime::FindWindow(HWND hwnd) const { return const_cast(this)->FindWindow(hwnd); } EditorWindow* EditorWindowHostRuntime::FindWindow(std::string_view windowId) { if (windowId.empty()) { return nullptr; } for (const std::unique_ptr& 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(this)->FindWindow(windowId); } EditorWindow* EditorWindowHostRuntime::FindPrimaryWindow() { for (const std::unique_ptr& window : m_windows) { if (window != nullptr && window->IsPrimary()) { return window.get(); } } return nullptr; } const EditorWindow* EditorWindowHostRuntime::FindPrimaryWindow() const { return const_cast(this)->FindPrimaryWindow(); } void EditorWindowHostRuntime::LogRuntimeTrace( std::string_view channel, std::string_view message) const { AppendUIEditorRuntimeTrace(channel, message); } EditorWindowWorkspaceCoordinator::EditorWindowWorkspaceCoordinator( EditorWindowHostRuntime& hostRuntime) : m_hostRuntime(hostRuntime) {} EditorWindowWorkspaceCoordinator::~EditorWindowWorkspaceCoordinator() = default; UIEditorWindowWorkspaceSet EditorWindowWorkspaceCoordinator::BuildWindowWorkspaceSet( std::string_view activeWindowId) const { UIEditorWindowWorkspaceSet windowSet = {}; if (const EditorWindow* primaryWindow = m_hostRuntime.FindPrimaryWindow(); primaryWindow != nullptr && primaryWindow->GetHwnd() != nullptr && !primaryWindow->IsClosing()) { windowSet.primaryWindowId = std::string(primaryWindow->GetWindowId()); } for (const std::unique_ptr& window : m_hostRuntime.GetWindows()) { if (window == nullptr || window->GetHwnd() == nullptr || window->IsClosing()) { continue; } UIEditorWindowWorkspaceState entry = {}; entry.windowId = std::string(window->GetWindowId()); entry.workspace = window->GetWorkspaceController().GetWorkspace(); entry.session = window->GetWorkspaceController().GetSession(); windowSet.windows.push_back(std::move(entry)); } windowSet.activeWindowId = !activeWindowId.empty() && ([this, activeWindowId]() { const EditorWindow* activeWindow = m_hostRuntime.FindWindow(activeWindowId); return activeWindow != nullptr && activeWindow->GetHwnd() != nullptr && !activeWindow->IsClosing(); })() ? std::string(activeWindowId) : windowSet.primaryWindowId; return windowSet; } UIEditorWindowWorkspaceController EditorWindowWorkspaceCoordinator::BuildLiveWindowWorkspaceController( std::string_view activeWindowId) const { return UIEditorWindowWorkspaceController( m_hostRuntime.GetEditorContext().GetShellAsset().panelRegistry, BuildWindowWorkspaceSet(activeWindowId)); } UIEditorWorkspaceController EditorWindowWorkspaceCoordinator::BuildWorkspaceControllerForWindow( const UIEditorWindowWorkspaceState& windowState) const { return UIEditorWorkspaceController( m_hostRuntime.GetEditorContext().GetShellAsset().panelRegistry, windowState.workspace, windowState.session); } void EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests( EditorWindow& sourceWindow, EditorWindowFrameTransferRequests&& transferRequests) { if (!m_globalTabDragSession.active && transferRequests.beginGlobalTabDrag.has_value() && transferRequests.beginGlobalTabDrag->IsValid()) { TryStartGlobalTabDrag(sourceWindow, *transferRequests.beginGlobalTabDrag); } if (!m_globalTabDragSession.active && transferRequests.detachPanel.has_value() && transferRequests.detachPanel->IsValid()) { TryProcessDetachRequest(sourceWindow, *transferRequests.detachPanel); } } } // namespace XCEngine::UI::Editor::App::Internal