- Created IProjectManager interface - ProjectManager now implements IProjectManager - Removed ProjectManager::Get() singleton - Added IEditorContext::GetProjectManager() - ProjectPanel now uses m_context->GetProjectManager() instead of singleton - EditorContext owns ProjectManager instance
243 lines
7.5 KiB
C++
243 lines
7.5 KiB
C++
#include "ProjectManager.h"
|
|
#include <filesystem>
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <windows.h>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace XCEngine {
|
|
namespace Editor {
|
|
|
|
std::vector<AssetItemPtr>& ProjectManager::GetCurrentItems() {
|
|
if (m_path.empty()) {
|
|
static std::vector<AssetItemPtr> empty;
|
|
return empty;
|
|
}
|
|
return m_path.back()->children;
|
|
}
|
|
|
|
void ProjectManager::NavigateToFolder(const AssetItemPtr& folder) {
|
|
m_path.push_back(folder);
|
|
m_selectedIndex = -1;
|
|
}
|
|
|
|
void ProjectManager::NavigateBack() {
|
|
if (m_path.size() > 1) {
|
|
m_path.pop_back();
|
|
m_selectedIndex = -1;
|
|
}
|
|
}
|
|
|
|
void ProjectManager::NavigateToIndex(size_t index) {
|
|
if (index >= m_path.size()) return;
|
|
while (m_path.size() > index + 1) {
|
|
m_path.pop_back();
|
|
}
|
|
m_selectedIndex = -1;
|
|
}
|
|
|
|
std::string ProjectManager::GetCurrentPath() const {
|
|
if (m_path.empty()) return "Assets";
|
|
std::string result = "Assets";
|
|
for (size_t i = 1; i < m_path.size(); i++) {
|
|
result += "/";
|
|
result += m_path[i]->name;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string ProjectManager::GetPathName(size_t index) const {
|
|
if (index >= m_path.size()) return "";
|
|
return m_path[index]->name;
|
|
}
|
|
|
|
static std::wstring Utf8ToWstring(const std::string& str) {
|
|
if (str.empty()) return L"";
|
|
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
|
|
if (len <= 0) return L"";
|
|
std::wstring result(len - 1, 0);
|
|
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &result[0], len);
|
|
return result;
|
|
}
|
|
|
|
static std::string WstringToUtf8(const std::wstring& wstr) {
|
|
if (wstr.empty()) return "";
|
|
int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
|
|
if (len <= 0) return "";
|
|
std::string result(len - 1, 0);
|
|
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &result[0], len, nullptr, nullptr);
|
|
return result;
|
|
}
|
|
|
|
void ProjectManager::Initialize(const std::string& projectPath) {
|
|
m_projectPath = projectPath;
|
|
|
|
std::wstring projectPathW = Utf8ToWstring(projectPath);
|
|
fs::path assetsPath = fs::path(projectPathW) / L"Assets";
|
|
|
|
try {
|
|
if (!fs::exists(assetsPath)) {
|
|
fs::create_directories(assetsPath);
|
|
fs::create_directories(assetsPath / L"Textures");
|
|
fs::create_directories(assetsPath / L"Models");
|
|
fs::create_directories(assetsPath / L"Scripts");
|
|
fs::create_directories(assetsPath / L"Materials");
|
|
fs::create_directories(assetsPath / L"Scenes");
|
|
|
|
std::ofstream((assetsPath / L"Textures" / L"Grass.png").wstring());
|
|
std::ofstream((assetsPath / L"Textures" / L"Stone.png").wstring());
|
|
std::ofstream((assetsPath / L"Models" / L"Character.fbx").wstring());
|
|
std::ofstream((assetsPath / L"Scripts" / L"PlayerController.cs").wstring());
|
|
std::ofstream((assetsPath / L"Scenes" / L"Main.unity").wstring());
|
|
}
|
|
|
|
m_rootFolder = ScanDirectory(assetsPath.wstring());
|
|
m_rootFolder->name = "Assets";
|
|
m_rootFolder->fullPath = WstringToUtf8(assetsPath.wstring());
|
|
|
|
m_path.clear();
|
|
m_path.push_back(m_rootFolder);
|
|
m_selectedIndex = -1;
|
|
} catch (const std::exception& e) {
|
|
m_rootFolder = std::make_shared<AssetItem>();
|
|
m_rootFolder->name = "Assets";
|
|
m_rootFolder->isFolder = true;
|
|
m_rootFolder->type = "Folder";
|
|
m_path.push_back(m_rootFolder);
|
|
}
|
|
}
|
|
|
|
std::wstring ProjectManager::GetCurrentFullPathW() const {
|
|
if (m_path.empty()) return Utf8ToWstring(m_projectPath);
|
|
|
|
std::wstring fullPath = Utf8ToWstring(m_projectPath);
|
|
for (size_t i = 0; i < m_path.size(); i++) {
|
|
fullPath += L"/" + Utf8ToWstring(m_path[i]->name);
|
|
}
|
|
return fullPath;
|
|
}
|
|
|
|
void ProjectManager::RefreshCurrentFolder() {
|
|
if (m_path.empty()) return;
|
|
|
|
try {
|
|
auto newFolder = ScanDirectory(GetCurrentFullPathW());
|
|
m_path.back()->children = newFolder->children;
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void ProjectManager::CreateFolder(const std::string& name) {
|
|
try {
|
|
std::wstring fullPath = GetCurrentFullPathW();
|
|
fs::path newFolderPath = fs::path(fullPath) / Utf8ToWstring(name);
|
|
fs::create_directory(newFolderPath);
|
|
RefreshCurrentFolder();
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
void ProjectManager::DeleteItem(int index) {
|
|
if (m_path.empty()) return;
|
|
auto& items = m_path.back()->children;
|
|
if (index < 0 || index >= (int)items.size()) return;
|
|
|
|
try {
|
|
std::wstring fullPath = GetCurrentFullPathW();
|
|
fs::path itemPath = fs::path(fullPath) / Utf8ToWstring(items[index]->name);
|
|
fs::remove_all(itemPath);
|
|
m_selectedIndex = -1;
|
|
RefreshCurrentFolder();
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
bool ProjectManager::MoveItem(const std::string& sourceFullPath, const std::string& destFolderFullPath) {
|
|
try {
|
|
fs::path sourcePath = Utf8ToWstring(sourceFullPath);
|
|
fs::path destPath = fs::path(Utf8ToWstring(destFolderFullPath)) / sourcePath.filename();
|
|
|
|
if (!fs::exists(sourcePath)) {
|
|
return false;
|
|
}
|
|
|
|
if (fs::exists(destPath)) {
|
|
return false;
|
|
}
|
|
|
|
fs::rename(sourcePath, destPath);
|
|
RefreshCurrentFolder();
|
|
return true;
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
AssetItemPtr ProjectManager::ScanDirectory(const std::wstring& path) {
|
|
auto folder = std::make_shared<AssetItem>();
|
|
folder->name = WstringToUtf8(fs::path(path).filename().wstring());
|
|
folder->isFolder = true;
|
|
folder->type = "Folder";
|
|
|
|
if (!fs::exists(path)) return folder;
|
|
|
|
std::vector<AssetItemPtr> items;
|
|
|
|
try {
|
|
for (const auto& entry : fs::directory_iterator(path)) {
|
|
std::wstring nameW = entry.path().filename().wstring();
|
|
bool isFolder = entry.is_directory();
|
|
items.push_back(CreateAssetItem(entry.path().wstring(), nameW, isFolder));
|
|
}
|
|
} catch (...) {
|
|
}
|
|
|
|
std::sort(items.begin(), items.end(), [](const AssetItemPtr& a, const AssetItemPtr& b) {
|
|
if (a->isFolder != b->isFolder) return a->isFolder;
|
|
return a->name < b->name;
|
|
});
|
|
|
|
folder->children = items;
|
|
return folder;
|
|
}
|
|
|
|
AssetItemPtr ProjectManager::CreateAssetItem(const std::wstring& path, const std::wstring& nameW, bool isFolder) {
|
|
auto item = std::make_shared<AssetItem>();
|
|
item->name = WstringToUtf8(nameW);
|
|
item->isFolder = isFolder;
|
|
item->fullPath = WstringToUtf8(path);
|
|
|
|
if (isFolder) {
|
|
item->type = "Folder";
|
|
try {
|
|
auto subFolder = ScanDirectory(path);
|
|
item->children = subFolder->children;
|
|
} catch (...) {
|
|
}
|
|
} else {
|
|
std::wstring ext = fs::path(path).extension().wstring();
|
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower);
|
|
|
|
if (ext == L".png" || ext == L".jpg" || ext == L".tga" || ext == L".bmp") {
|
|
item->type = "Texture";
|
|
} else if (ext == L".fbx" || ext == L".obj" || ext == L".gltf" || ext == L".glb") {
|
|
item->type = "Model";
|
|
} else if (ext == L".cs" || ext == L".cpp" || ext == L".h") {
|
|
item->type = "Script";
|
|
} else if (ext == L".mat") {
|
|
item->type = "Material";
|
|
} else if (ext == L".unity" || ext == L".scene") {
|
|
item->type = "Scene";
|
|
} else if (ext == L".prefab") {
|
|
item->type = "Prefab";
|
|
} else {
|
|
item->type = "File";
|
|
}
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
}
|
|
} |