#include "Application.h" #include "Core/EditorLoggingSetup.h" #include "Core/ProjectRootResolver.h" #include "Core/EditorWindowTitle.h" #include "Layers/EditorLayer.h" #include "Core/EditorContext.h" #include "Core/EditorEvents.h" #include "Core/EventBus.h" #include "UI/BuiltInIcons.h" #include "Platform/Win32Utf8.h" #include "Platform/WindowsProcessDiagnostics.h" #include #include namespace XCEngine { namespace Editor { Application& Application::Get() { static Application instance; return instance; } bool Application::InitializeWindowRenderer(HWND hwnd) { RECT clientRect = {}; if (!GetClientRect(hwnd, &clientRect)) { MessageBoxW(hwnd, L"Failed to query editor client area", L"Error", MB_OK | MB_ICONERROR); return false; } const int clientWidth = clientRect.right - clientRect.left; const int clientHeight = clientRect.bottom - clientRect.top; if (clientWidth <= 0 || clientHeight <= 0) { MessageBoxW(hwnd, L"Editor client area is invalid", L"Error", MB_OK | MB_ICONERROR); return false; } if (m_windowRenderer.Initialize(hwnd, clientWidth, clientHeight)) { return true; } MessageBoxW(hwnd, L"Failed to initialize editor window renderer", L"Error", MB_OK | MB_ICONERROR); return false; } void Application::InitializeEditorContext(const std::string& projectPath) { m_editorContext = std::make_shared(); m_editorContext->SetProjectPath(projectPath); m_exitRequestedHandlerId = m_editorContext->GetEventBus().Subscribe( [this](const EditorExitRequestedEvent&) { if (m_hwnd) { PostMessageW(m_hwnd, WM_CLOSE, 0, 0); } }); } void Application::InitializeImGui(HWND hwnd) { m_imguiSession.Initialize( m_editorContext->GetProjectPath(), UI::ImGuiBackendBridge::GetDpiScaleForHwnd(hwnd)); m_imguiBackend.Initialize( hwnd, m_windowRenderer.GetDevice(), m_windowRenderer.GetCommandQueue(), m_windowRenderer.GetSrvHeap(), m_windowRenderer.GetSrvDescriptorSize(), m_windowRenderer.GetSrvDescriptorCount()); UI::InitializeBuiltInIcons( m_imguiBackend, m_windowRenderer.GetDevice(), m_windowRenderer.GetCommandQueue()); m_viewportHostService.Initialize(m_imguiBackend, m_windowRenderer.GetRHIDevice()); static_cast(m_editorContext.get())->SetViewportHostService(&m_viewportHostService); } void Application::AttachEditorLayer() { m_editorLayer = new EditorLayer(); m_editorLayer->SetContext(m_editorContext); m_layerStack.pushLayer(std::unique_ptr(m_editorLayer)); m_layerStack.onAttach(); } void Application::DetachEditorLayer() { m_layerStack.onDetach(); m_editorLayer = nullptr; } void Application::ShutdownEditorContext() { if (m_editorContext && m_exitRequestedHandlerId) { m_editorContext->GetEventBus().Unsubscribe(m_exitRequestedHandlerId); m_exitRequestedHandlerId = 0; } m_editorContext.reset(); } void Application::RenderEditorFrame() { static constexpr float kClearColor[4] = { 0.22f, 0.22f, 0.22f, 1.0f }; m_imguiBackend.BeginFrame(); m_viewportHostService.BeginFrame(); m_layerStack.onImGuiRender(); UpdateWindowTitle(); ImGui::Render(); m_windowRenderer.Render( m_imguiBackend, kClearColor, [this](const Rendering::RenderContext& renderContext) { if (m_editorContext) { m_viewportHostService.RenderRequestedViewports(*m_editorContext, renderContext); } }); } bool Application::Initialize(HWND hwnd) { Platform::InstallCrashExceptionFilter(); Platform::RedirectStderrToExecutableLog(); const std::string exeDir = Platform::GetExecutableDirectoryUtf8(); ConfigureEditorLogging(exeDir); const std::string projectRoot = ResolveEditorProjectRootUtf8(); const bool workingDirectoryChanged = SetEditorWorkingDirectory(projectRoot); auto& logger = Debug::Logger::Get(); const std::string projectRootMessage = "Editor project root: " + projectRoot; logger.Info(Debug::LogCategory::General, projectRootMessage.c_str()); if (!workingDirectoryChanged) { const std::string warningMessage = "Failed to switch editor working directory to project root: " + projectRoot; logger.Warning(Debug::LogCategory::General, warningMessage.c_str()); } m_hwnd = hwnd; if (!InitializeWindowRenderer(hwnd)) { return false; } InitializeEditorContext(projectRoot); InitializeImGui(hwnd); AttachEditorLayer(); m_renderReady = true; return true; } void Application::Shutdown() { m_renderReady = false; DetachEditorLayer(); if (m_editorContext) { static_cast(m_editorContext.get())->SetViewportHostService(nullptr); } m_viewportHostService.Shutdown(); UI::ShutdownBuiltInIcons(); m_imguiBackend.Shutdown(); m_imguiSession.Shutdown(); ShutdownEditorContext(); m_windowRenderer.Shutdown(); } void Application::Render() { if (!m_renderReady) { return; } RenderEditorFrame(); } void Application::UpdateWindowTitle() { if (!m_hwnd || !m_editorContext) { return; } const std::wstring title = Platform::Utf8ToWide(BuildEditorWindowTitle(*m_editorContext)); if (title != m_lastWindowTitle) { SetWindowTextW(m_hwnd, title.c_str()); m_lastWindowTitle = title; } } void Application::OnResize(int width, int height) { m_windowRenderer.Resize(width, height); } bool Application::SwitchProject(const std::string& projectPath) { if (!m_editorContext || projectPath.empty()) { return false; } m_imguiSession.SetProjectPath(projectPath); m_editorContext->SetProjectPath(projectPath); auto& logger = Debug::Logger::Get(); if (!SetEditorWorkingDirectory(projectPath)) { const std::string warningMessage = "Failed to switch editor working directory to project root: " + projectPath; logger.Warning(Debug::LogCategory::General, warningMessage.c_str()); } const std::string infoMessage = "Switched editor project root: " + projectPath; logger.Info(Debug::LogCategory::General, infoMessage.c_str()); m_lastWindowTitle.clear(); UpdateWindowTitle(); return true; } void Application::SaveProjectState() { m_imguiSession.SaveSettings(); } } }