#include "EditorWindowManager.h" #include "State/EditorContext.h" #include "Bootstrap/EditorResources.h" #include "EditorWindow.h" #include #include namespace XCEngine::UI::Editor::App { EditorWindowManager::EditorWindowManager( EditorWindowHostConfig hostConfig, std::filesystem::path repoRoot, EditorContext& editorContext) : m_hostConfig(hostConfig), m_repoRoot(std::move(repoRoot)), m_editorContext(editorContext) {} EditorWindowManager::~EditorWindowManager() = default; EditorWindow* EditorWindowManager::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 EditorWindowManager::HandlePendingNativeWindowCreated(HWND hwnd) { if (m_pendingCreateWindow != nullptr && !m_pendingCreateWindow->HasHwnd()) { m_pendingCreateWindow->AttachHwnd(hwnd); } } void EditorWindowManager::Shutdown() { for (const std::unique_ptr& window : m_windows) { if (window != nullptr) { DestroyEditorWindow(*window); } } m_windows.clear(); m_pendingCreateWindow = nullptr; m_globalTabDragSession = {}; } EditorWindow* EditorWindowManager::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* EditorWindowManager::FindWindow(HWND hwnd) const { return const_cast(this)->FindWindow(hwnd); } EditorWindow* EditorWindowManager::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* EditorWindowManager::FindWindow(std::string_view windowId) const { return const_cast(this)->FindWindow(windowId); } EditorWindow* EditorWindowManager::FindPrimaryWindow() { for (const std::unique_ptr& window : m_windows) { if (window != nullptr && window->IsPrimary()) { return window.get(); } } return nullptr; } const EditorWindow* EditorWindowManager::FindPrimaryWindow() const { return const_cast(this)->FindPrimaryWindow(); } bool EditorWindowManager::HasWindows() const { return !m_windows.empty(); } void EditorWindowManager::DestroyEditorWindow(EditorWindow& window) { const HWND hwnd = window.GetHwnd(); if (GetCapture() == hwnd) { ReleaseCapture(); } window.Shutdown(); if (hwnd != nullptr && IsWindow(hwnd)) { DestroyWindow(hwnd); } window.MarkDestroyed(); } void EditorWindowManager::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 EditorWindowManager::RenderAllWindows() { for (const std::unique_ptr& window : m_windows) { if (window == nullptr || window->GetHwnd() == nullptr) { continue; } window->RenderFrame(m_editorContext, IsGlobalTabDragActive()); } } void EditorWindowManager::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) { PostMessageW(otherWindow->GetHwnd(), WM_CLOSE, 0, 0); } } } } } void EditorWindowManager::LogRuntimeTrace( std::string_view channel, std::string_view message) const { AppendUIEditorRuntimeTrace(channel, message); } } // namespace XCEngine::UI::Editor::App