#pragma once #include "Core/IEditorContext.h" #include "Core/IProjectManager.h" #include "Core/ISceneManager.h" #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include #include #include namespace XCEngine { namespace Editor { namespace SceneEditorUtils { inline std::wstring Utf8ToWide(const std::string& value) { if (value.empty()) { return {}; } const int length = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0); if (length <= 0) { return {}; } std::wstring result(length - 1, L'\0'); MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, &result[0], length); return result; } inline std::string WideToUtf8(const std::wstring& value) { if (value.empty()) { return {}; } const int length = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, nullptr, 0, nullptr, nullptr); if (length <= 0) { return {}; } std::string result(length - 1, '\0'); WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, &result[0], length, nullptr, nullptr); return result; } inline std::string SanitizeSceneFileName(const std::string& value) { std::string result = value.empty() ? "Untitled Scene" : value; for (char& ch : result) { switch (ch) { case '\\': case '/': case ':': case '*': case '?': case '"': case '<': case '>': case '|': ch = '_'; break; default: if (static_cast(ch) < 32u) { ch = '_'; } break; } } return result; } inline HWND GetDialogOwnerWindow() { HWND owner = GetActiveWindow(); if (!owner) { owner = GetForegroundWindow(); } return owner; } inline const wchar_t* GetSceneFilter() { static const wchar_t filter[] = L"XCEngine Scene (*.xc)\0*.xc\0Legacy Scene (*.scene;*.unity)\0*.scene;*.unity\0All Files (*.*)\0*.*\0\0"; return filter; } inline std::string OpenSceneFileDialog(const std::string& projectPath, const std::string& initialPath = {}) { namespace fs = std::filesystem; const fs::path scenesDir = fs::path(projectPath) / "Assets" / "Scenes"; const std::wstring initialDir = Utf8ToWide(scenesDir.string()); std::array fileBuffer{}; if (!initialPath.empty()) { const std::wstring initialFile = Utf8ToWide(initialPath); wcsncpy_s(fileBuffer.data(), fileBuffer.size(), initialFile.c_str(), _TRUNCATE); } OPENFILENAMEW dialog{}; dialog.lStructSize = sizeof(dialog); dialog.hwndOwner = GetDialogOwnerWindow(); dialog.lpstrFilter = GetSceneFilter(); dialog.lpstrFile = fileBuffer.data(); dialog.nMaxFile = static_cast(fileBuffer.size()); dialog.lpstrInitialDir = initialDir.empty() ? nullptr : initialDir.c_str(); dialog.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR; if (!GetOpenFileNameW(&dialog)) { return {}; } return WideToUtf8(fileBuffer.data()); } inline std::string SaveSceneFileDialog( const std::string& projectPath, const std::string& currentScenePath, const std::string& currentSceneName) { namespace fs = std::filesystem; const fs::path scenesDir = fs::path(projectPath) / "Assets" / "Scenes"; const fs::path suggestedPath = currentScenePath.empty() ? scenesDir / (SanitizeSceneFileName(currentSceneName) + ".xc") : fs::path(currentScenePath).replace_extension(".xc"); std::array fileBuffer{}; const std::wstring suggestedWide = Utf8ToWide(suggestedPath.string()); wcsncpy_s(fileBuffer.data(), fileBuffer.size(), suggestedWide.c_str(), _TRUNCATE); const std::wstring initialDir = Utf8ToWide(suggestedPath.parent_path().string()); OPENFILENAMEW dialog{}; dialog.lStructSize = sizeof(dialog); dialog.hwndOwner = GetDialogOwnerWindow(); dialog.lpstrFilter = GetSceneFilter(); dialog.lpstrFile = fileBuffer.data(); dialog.nMaxFile = static_cast(fileBuffer.size()); dialog.lpstrInitialDir = initialDir.empty() ? nullptr : initialDir.c_str(); dialog.lpstrDefExt = L"xc"; dialog.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR; if (!GetSaveFileNameW(&dialog)) { return {}; } return WideToUtf8(fileBuffer.data()); } inline bool SaveCurrentScene(IEditorContext& context) { auto& sceneManager = context.GetSceneManager(); if (sceneManager.SaveScene()) { return true; } const std::string filePath = SaveSceneFileDialog( context.GetProjectPath(), sceneManager.GetCurrentScenePath(), sceneManager.GetCurrentSceneName()); if (filePath.empty()) { return false; } const bool saved = sceneManager.SaveSceneAs(filePath); if (saved) { context.GetProjectManager().RefreshCurrentFolder(); } return saved; } inline bool ConfirmSceneSwitch(IEditorContext& context) { auto& sceneManager = context.GetSceneManager(); if (!sceneManager.HasActiveScene() || !sceneManager.IsSceneDirty()) { return true; } const int result = MessageBoxW( GetDialogOwnerWindow(), L"Save changes to the current scene before continuing?", L"Unsaved Scene Changes", MB_YESNOCANCEL | MB_ICONWARNING); if (result == IDCANCEL) { return false; } if (result == IDYES) { return SaveCurrentScene(context); } return true; } } // namespace SceneEditorUtils } // namespace Editor } // namespace XCEngine