added first selectable, and modyfiable assets to system, Mesh, Material, and Texture. Texture ist WIP, because no subassets are created jet, well technicaly everything is WIP bit nevermind XD
This commit is contained in:
@@ -20,6 +20,7 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem(ConsolView* cvp) : sv(nullptr), ed
|
||||
controller.AddGuiElement("Properties", &pv);
|
||||
controller.AddGuiElement("Debug", &dv);
|
||||
controller.AddGuiElement("Camera", &camv);
|
||||
controller.AddGuiElement("Assets", &av);
|
||||
|
||||
BehaviourScriptRegistry::RegisterBehaviourScript("Image", []() -> BehaviourScript* {return new Image();});
|
||||
BehaviourScriptRegistry::RegisterBehaviourScript("Image Animation", []() -> BehaviourScript* {return new ImageAnimation();});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "UI/windows/HirearchieView.hpp"
|
||||
#include "UI/windows/PropertiesView.hpp"
|
||||
#include "UI/windows/SceneView.hpp"
|
||||
#include "UI/windows/AssetsView.hpp"
|
||||
#include "interfaces/IRenderTexture.hpp"
|
||||
|
||||
namespace TSE::EDITOR
|
||||
@@ -22,6 +23,7 @@ namespace TSE::EDITOR
|
||||
CameraView camv;
|
||||
TSE::IRenderTexture* rt;
|
||||
SceneView sv;
|
||||
AssetsView av;
|
||||
Layer editorLayer;
|
||||
|
||||
EditorSubsystem(ConsolView* cvp);
|
||||
|
||||
@@ -1,12 +1,167 @@
|
||||
#include "ElementDrawer.hpp"
|
||||
#include "BehaviourScriptRegistry.hpp"
|
||||
#include "elements/ShaderRegistry.hpp"
|
||||
#include "elements/AssetLibrary.hpp"
|
||||
#include "BehaviourScripts/Camera.hpp"
|
||||
#include "windows/HirearchieView.hpp"
|
||||
#include "windows/PropertiesView.hpp"
|
||||
#include <algorithm>
|
||||
#include <any>
|
||||
#include <cctype>
|
||||
|
||||
namespace TSE::EDITOR
|
||||
{
|
||||
namespace
|
||||
{
|
||||
char assetPickerSearchBuffer[128] = "";
|
||||
|
||||
std::string ToLowerCopy(std::string value)
|
||||
{
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c)
|
||||
{
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T*> GetAssetsOfType()
|
||||
{
|
||||
std::vector<T*> result;
|
||||
int count = AssetLibrary::GetValueCount();
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
auto [ptr, type, id] = AssetLibrary::GetValueAt(i);
|
||||
if(type != typeid(T).name() && type != typeid(T*).name())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result.push_back(std::any_cast<T*>(ptr));
|
||||
}
|
||||
catch(const std::bad_any_cast&)
|
||||
{
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, typename NameGetter>
|
||||
T* DrawAssetPickerPopup(const char* popupId, const char* searchHint, const char* emptyText, NameGetter getName)
|
||||
{
|
||||
T* selectedAsset = nullptr;
|
||||
|
||||
ImGui::SetNextWindowSize({360.0f, 420.0f}, ImGuiCond_Appearing);
|
||||
if(ImGui::BeginPopup(popupId))
|
||||
{
|
||||
ImGui::PushItemWidth(-1.0f);
|
||||
ImGui::InputTextWithHint("##AssetPickerSearch", searchHint, assetPickerSearchBuffer, IM_ARRAYSIZE(assetPickerSearchBuffer));
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Separator();
|
||||
|
||||
std::string filterText = ToLowerCopy(assetPickerSearchBuffer);
|
||||
std::vector<T*> assets = GetAssetsOfType<T>();
|
||||
|
||||
if(ImGui::BeginChild("##AssetPickerList", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_AlwaysVerticalScrollbar))
|
||||
{
|
||||
int shownCount = 0;
|
||||
for(T* asset : assets)
|
||||
{
|
||||
if(asset == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string assetName = getName(asset);
|
||||
if(!filterText.empty() && ToLowerCopy(assetName).find(filterText) == std::string::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::PushID(asset);
|
||||
if(ImGui::Selectable(assetName.c_str()))
|
||||
{
|
||||
selectedAsset = asset;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
shownCount++;
|
||||
}
|
||||
|
||||
if(shownCount == 0)
|
||||
{
|
||||
ImGui::TextDisabled("%s", emptyText);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
return selectedAsset;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool BeginAssetField(T** element, const char* popupId)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::TextDisabled("No Asset Field");
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::PushID(element);
|
||||
|
||||
float buttonWidth = ImGui::GetFrameHeight();
|
||||
float fieldHeight = std::max(36.0f, ImGui::GetFrameHeightWithSpacing() + ImGui::GetStyle().WindowPadding.y * 2.0f);
|
||||
|
||||
ImGui::BeginChild("##AssetField", {0.0f, fieldHeight}, ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
if(ImGui::Button("+##SelectAsset", {buttonWidth, 0.0f}))
|
||||
{
|
||||
assetPickerSearchBuffer[0] = '\0';
|
||||
ImGui::OpenPopup(popupId);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("##AssetFieldValue", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void EndAssetField()
|
||||
{
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void OpenAssetPropertiesOnDoubleClick(T* asset)
|
||||
{
|
||||
if(asset == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
PropertiesView::SetInspectorElement(InspectableType::IAsset, static_cast<IAsset*>(asset));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename NameGetter>
|
||||
void AssetSelector(T** element, const char* popupId, const char* searchHint, const char* emptyText, NameGetter getName)
|
||||
{
|
||||
T* selectedAsset = DrawAssetPickerPopup<T>(popupId, searchHint, emptyText, getName);
|
||||
if(selectedAsset != nullptr)
|
||||
{
|
||||
*element = selectedAsset;
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region helper
|
||||
bool InputText(const char* label, std::string& str, size_t bufferSize = 256) {
|
||||
std::vector<char> buffer(bufferSize);
|
||||
@@ -222,15 +377,7 @@ namespace TSE::EDITOR
|
||||
void ElementDrawer::Draw(Renderable *element, const bool &debug)
|
||||
{
|
||||
ImGui::SeparatorText("Material");
|
||||
int height = 100;
|
||||
if(element->GetMaterial() == nullptr)
|
||||
{
|
||||
height = 35;
|
||||
}
|
||||
ImVec2 size(0, height);
|
||||
ImGui::BeginChild("MaterialViewer", size, ImGuiChildFlags_Borders);
|
||||
Draw(element->GetMaterial(), debug);
|
||||
ImGui::EndChild();
|
||||
DrawMaterialField(element->GetMaterialRef());
|
||||
}
|
||||
void ElementDrawer::Draw(MeshContainer *element, const bool &debug)
|
||||
{
|
||||
@@ -240,7 +387,7 @@ namespace TSE::EDITOR
|
||||
height = 35;
|
||||
}
|
||||
ImVec2 size(0, height);
|
||||
Draw(element->GetMesh(), debug, "Mesh", true);
|
||||
DrawMeshField(element->GetMeshRef());
|
||||
}
|
||||
void ElementDrawer::Draw(Image *element, const bool &debug)
|
||||
{
|
||||
@@ -444,7 +591,7 @@ namespace TSE::EDITOR
|
||||
Texture* value = element->GetValue<Texture*>(name);
|
||||
Draw(value, debug, name , true);
|
||||
}
|
||||
if (type == typeid(uint).name())
|
||||
else if (type == typeid(uint).name())
|
||||
{
|
||||
int value = element->GetValue<uint>(name);
|
||||
if(ImGui::InputInt(name.c_str(), &value))
|
||||
@@ -481,11 +628,6 @@ namespace TSE::EDITOR
|
||||
}
|
||||
void ElementDrawer::Draw(Mesh *element, const bool &debug, const std::string &label, const bool small)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
if(small) DrawMeshCompact(element, debug, label);
|
||||
else DrawMeshNormal(element, debug, label);
|
||||
}
|
||||
@@ -943,6 +1085,24 @@ namespace TSE::EDITOR
|
||||
ImGui::TextDisabled(("Chunk Count: " + std::to_string(element->GetChunkCount())).c_str());
|
||||
}
|
||||
}
|
||||
void ElementDrawer::Draw(IAsset *element, const bool &debug)
|
||||
{
|
||||
if (element->assetType() == "material")
|
||||
{
|
||||
Draw((Material*)element, debug);
|
||||
}
|
||||
else if (element->assetType() == "texture")
|
||||
{
|
||||
Draw((Texture*)element, debug);
|
||||
}
|
||||
else if (element->assetType() == "mesh")
|
||||
{
|
||||
Draw((Mesh*)element, debug);
|
||||
}
|
||||
|
||||
element->SaveAsset();
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawAudioClipCompact(AudioClip *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
@@ -1034,6 +1194,21 @@ namespace TSE::EDITOR
|
||||
}
|
||||
void ElementDrawer::DrawMeshCompact(Mesh *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
ImGui::Text(element->name.c_str());
|
||||
}
|
||||
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
float item_spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label.c_str());
|
||||
|
||||
@@ -1060,54 +1235,6 @@ namespace TSE::EDITOR
|
||||
cursorCurrent.y += 2;
|
||||
ImGui::SetCursorPos(cursorCurrent);
|
||||
ImGui::TextUnformatted(label.c_str());
|
||||
}
|
||||
void ElementDrawer::DrawMeshNormal(Mesh *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
ImGui::Text(("Name: " + element->name).c_str());
|
||||
if(debug)
|
||||
{
|
||||
//ImGui::TextDisabled(("ID: " + to_string(element->id)).c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text(("Vectex Count: " + std::to_string(element->vertecies.size())).c_str());
|
||||
ImGui::Text(("Index Count: " + std::to_string(element->indecies.size())).c_str());
|
||||
ImGui::Text(("UV Count: " + std::to_string(element->uvs.size())).c_str());
|
||||
ImGui::Indent(20.0f);
|
||||
if(ImGui::CollapsingHeader("Vertecies"))
|
||||
{
|
||||
ImGui::PushID("Verts");
|
||||
ImGui::BeginDisabled();
|
||||
for (int i = 0; i < element->vertecies.size(); i++)
|
||||
{
|
||||
ImGui::InputFloat3(std::to_string(i).c_str(), &element->vertecies[i].x);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if(ImGui::CollapsingHeader("Indecies"))
|
||||
{
|
||||
ImGui::PushID("Inds");
|
||||
ImGui::BeginDisabled();
|
||||
for (int i = 0; i < element->indecies.size(); i++)
|
||||
{
|
||||
int val = element->indecies[i];
|
||||
ImGui::InputInt(std::to_string(i).c_str(), &val);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if(ImGui::CollapsingHeader("UVs"))
|
||||
{
|
||||
ImGui::PushID("Uvs");
|
||||
ImGui::BeginDisabled();
|
||||
for (int i = 0; i < element->uvs.size(); i++)
|
||||
{
|
||||
ImGui::InputFloat2(std::to_string(i).c_str(), &element->uvs[i].x);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::Unindent(20.0f);
|
||||
|
||||
}
|
||||
void ElementDrawer::DrawSpriteCompact(Sprite *element, const bool &debug, const std::string &label)
|
||||
@@ -1215,6 +1342,98 @@ namespace TSE::EDITOR
|
||||
|
||||
ImVec2 texSize (available_width, (available_width) * ymultiplyer);
|
||||
ImGui::Image(element->GetTextureId(), texSize, {0,1}, {1,0});
|
||||
ImGui::Separator();
|
||||
ImGui::SeparatorText("Import Settings");
|
||||
|
||||
TextureImportSettings& settings = element->GetImportSettings();
|
||||
bool importSettingsChanged = false;
|
||||
|
||||
int importMode = static_cast<int>(settings.importMode);
|
||||
const char* importModes[] = { "Raw Texture", "Sprite", "Sprite Set", "Nine Tile" };
|
||||
if (ImGui::Combo("Import Mode", &importMode, importModes, IM_ARRAYSIZE(importModes)))
|
||||
{
|
||||
settings.importMode = static_cast<byte>(std::clamp(importMode, 0, IM_ARRAYSIZE(importModes) - 1));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
int importCount[2] = {
|
||||
static_cast<int>(settings.importCount.x),
|
||||
static_cast<int>(settings.importCount.y)
|
||||
};
|
||||
if (ImGui::InputInt2("Import Count", importCount))
|
||||
{
|
||||
settings.importCount.x = static_cast<float>(std::max(1, importCount[0]));
|
||||
settings.importCount.y = static_cast<float>(std::max(1, importCount[1]));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
int bpp = static_cast<int>(settings.bpp);
|
||||
if (ImGui::InputInt("BPP", &bpp))
|
||||
{
|
||||
settings.bpp = static_cast<uint>(std::max(0, bpp));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
int chanels = static_cast<int>(settings.chanels);
|
||||
if (ImGui::InputInt("Chanels", &chanels))
|
||||
{
|
||||
settings.chanels = static_cast<byte>(std::clamp(chanels, 0, 255));
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
if (InputText("Import Name", settings.name))
|
||||
{
|
||||
importSettingsChanged = true;
|
||||
}
|
||||
|
||||
if (importSettingsChanged)
|
||||
{
|
||||
element->SaveAsset();
|
||||
}
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawMaterialCompact(Material *element, const bool &debug, const std::string &label)
|
||||
{
|
||||
if(element == nullptr)
|
||||
{
|
||||
ImGui::Text("No Mesh Set");
|
||||
return;
|
||||
}
|
||||
ImGui::Text(element->GetName().c_str());
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawMeshField(Mesh **element)
|
||||
{
|
||||
if(!BeginAssetField<Mesh>(element, "MeshAssetPicker"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawMeshCompact(*element, false, "");
|
||||
OpenAssetPropertiesOnDoubleClick(*element);
|
||||
EndAssetField<Mesh>();
|
||||
|
||||
AssetSelector<Mesh>(element, "MeshAssetPicker", "Search Mesh", "No meshes found", [](Mesh* mesh)
|
||||
{
|
||||
return mesh->name.empty() ? std::string("Unnamed Mesh") : mesh->name;
|
||||
});
|
||||
}
|
||||
|
||||
void ElementDrawer::DrawMaterialField(Material **element)
|
||||
{
|
||||
if(!BeginAssetField<Material>(element, "MaterialAssetPicker"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawMaterialCompact(*element, false, "");
|
||||
OpenAssetPropertiesOnDoubleClick(*element);
|
||||
EndAssetField<Material>();
|
||||
|
||||
AssetSelector<Material>(element, "MaterialAssetPicker", "Search Material", "No materials found", [](Material* mat)
|
||||
{
|
||||
return mat->GetName().empty() ? std::string("Unnamed Material") : mat->GetName();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace TSE::EDITOR
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace TSE::EDITOR
|
||||
None,
|
||||
Transformable,
|
||||
Scene,
|
||||
Layer
|
||||
Layer,
|
||||
IAsset
|
||||
};
|
||||
|
||||
struct Inspectable
|
||||
@@ -54,6 +55,9 @@ namespace TSE::EDITOR
|
||||
case InspectableType::Layer:
|
||||
Draw(static_cast<Layer*>(element.ptr), debug);
|
||||
break;
|
||||
case InspectableType::IAsset:
|
||||
Draw(static_cast<IAsset*>(element.ptr), debug);
|
||||
break;
|
||||
default:
|
||||
TSE_WARNING("Draw not implemented for this type.");
|
||||
break;
|
||||
@@ -83,6 +87,7 @@ namespace TSE::EDITOR
|
||||
static void Draw(ParticleSystem* element, const bool& debug);
|
||||
static void Draw(TileMap* element, const bool& debug);
|
||||
static void Draw(OrdererSpriteSet* element, const bool& debug);
|
||||
static void Draw(IAsset* element, const bool& debug);
|
||||
|
||||
static void DrawAudioClipCompact(AudioClip* element, const bool& debug, const std::string& label);
|
||||
static void DrawAudioClipNormal(AudioClip* element, const bool& debug, const std::string& label);
|
||||
@@ -94,5 +99,9 @@ namespace TSE::EDITOR
|
||||
static void DrawSpriteNormal(Sprite* element, const bool& debug, const std::string& label);
|
||||
static void DrawTextureCompact(Texture* element, const bool& debug, const std::string& label);
|
||||
static void DrawTextureNormal(Texture* element, const bool& debug, const std::string& label);
|
||||
static void DrawMaterialCompact(Material* element, const bool& debug, const std::string& label);
|
||||
|
||||
static void DrawMeshField(Mesh** element);
|
||||
static void DrawMaterialField(Material** element);
|
||||
};
|
||||
} // namespace TSE::EDITOR
|
||||
|
||||
751
TSE_Editor/src/UI/windows/AssetsView.cpp
Normal file
751
TSE_Editor/src/UI/windows/AssetsView.cpp
Normal file
@@ -0,0 +1,751 @@
|
||||
#include "AssetsView.hpp"
|
||||
#include "elements/AssetLibrary.hpp"
|
||||
#include "PathHelper.hpp"
|
||||
#include "json.hpp"
|
||||
#include "uuid.h"
|
||||
#include "elements/Material.hpp"
|
||||
#include "elements/Texture.hpp"
|
||||
#include "PropertiesView.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> GetDirectories(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> directories;
|
||||
std::error_code errorCode;
|
||||
|
||||
for(const auto& entry : std::filesystem::directory_iterator(folderPath, errorCode))
|
||||
{
|
||||
std::error_code entryErrorCode;
|
||||
if(entry.is_directory(entryErrorCode))
|
||||
directories.push_back(entry);
|
||||
}
|
||||
|
||||
std::sort(directories.begin(), directories.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return a.path().filename().string() < b.path().filename().string();
|
||||
});
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::directory_entry> GetFiles(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> files;
|
||||
std::error_code errorCode;
|
||||
|
||||
for(const auto& entry : std::filesystem::directory_iterator(folderPath, errorCode))
|
||||
{
|
||||
std::error_code entryErrorCode;
|
||||
if(entry.is_regular_file(entryErrorCode) && entry.path().extension() != ".meta")
|
||||
files.push_back(entry);
|
||||
}
|
||||
|
||||
std::sort(files.begin(), files.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return a.path().filename().string() < b.path().filename().string();
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
bool IsPathInFolder(const std::filesystem::path& path, const std::filesystem::path& folderPath)
|
||||
{
|
||||
if(path.empty() || folderPath.empty())
|
||||
return false;
|
||||
|
||||
const std::filesystem::path relativePath = path.lexically_relative(folderPath);
|
||||
if(relativePath.empty() || relativePath == ".")
|
||||
return true;
|
||||
|
||||
const auto firstPart = relativePath.begin();
|
||||
return firstPart != relativePath.end() && firstPart->string() != "..";
|
||||
}
|
||||
|
||||
bool IsSameOrChildPath(const std::filesystem::path& path, const std::filesystem::path& possibleParentPath)
|
||||
{
|
||||
return IsPathInFolder(path.lexically_normal(), possibleParentPath.lexically_normal());
|
||||
}
|
||||
|
||||
std::string FitTextToWidth(const std::string& text, float maxWidth)
|
||||
{
|
||||
if(ImGui::CalcTextSize(text.c_str()).x <= maxWidth)
|
||||
return text;
|
||||
|
||||
std::string result = text;
|
||||
while(result.size() > 3)
|
||||
{
|
||||
result.pop_back();
|
||||
const std::string truncated = result + "...";
|
||||
if(ImGui::CalcTextSize(truncated.c_str()).x <= maxWidth)
|
||||
return truncated;
|
||||
}
|
||||
|
||||
return "...";
|
||||
}
|
||||
}
|
||||
|
||||
TSE::EDITOR::AssetsView::AssetsView() : GuiWindow("Assets", ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)
|
||||
{
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::Define()
|
||||
{
|
||||
const float splitterWidth = 1.0f;
|
||||
const float minPanelWidth = 100.0f;
|
||||
const ImVec2 availableRegion = ImGui::GetContentRegionAvail();
|
||||
|
||||
if(availableRegion.x <= splitterWidth)
|
||||
return;
|
||||
|
||||
if(leftPanelWidth < 0.0f)
|
||||
leftPanelWidth = availableRegion.x / 3.0f;
|
||||
|
||||
float maxLeftPanelWidth = availableRegion.x - splitterWidth - minPanelWidth;
|
||||
if(maxLeftPanelWidth < minPanelWidth)
|
||||
maxLeftPanelWidth = (availableRegion.x - splitterWidth) * 0.5f;
|
||||
|
||||
if(leftPanelWidth < minPanelWidth)
|
||||
leftPanelWidth = minPanelWidth;
|
||||
if(leftPanelWidth > maxLeftPanelWidth)
|
||||
leftPanelWidth = maxLeftPanelWidth;
|
||||
|
||||
if(leftPanelWidth < 1.0f)
|
||||
leftPanelWidth = 1.0f;
|
||||
|
||||
auto clampLeftPanelWidth = [&]()
|
||||
{
|
||||
if(leftPanelWidth < minPanelWidth)
|
||||
leftPanelWidth = minPanelWidth;
|
||||
if(leftPanelWidth > maxLeftPanelWidth)
|
||||
leftPanelWidth = maxLeftPanelWidth;
|
||||
if(leftPanelWidth < 1.0f)
|
||||
leftPanelWidth = 1.0f;
|
||||
};
|
||||
|
||||
ImGuiWindowFlags childFlags = ImGuiWindowFlags_None;
|
||||
|
||||
if(ImGui::BeginChild("##AssetsTreeView", {leftPanelWidth, 0.0f}, ImGuiChildFlags_Borders, childFlags))
|
||||
{
|
||||
DefineTreeView();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
|
||||
ImGui::InvisibleButton("##AssetsSplitter", {splitterWidth, availableRegion.y});
|
||||
if(ImGui::IsItemActive())
|
||||
{
|
||||
leftPanelWidth += ImGui::GetIO().MouseDelta.x;
|
||||
clampLeftPanelWidth();
|
||||
}
|
||||
|
||||
if(ImGui::IsItemHovered() || ImGui::IsItemActive())
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
|
||||
ImU32 splitterColor = ImGui::GetColorU32(ImGuiCol_Separator);
|
||||
if(ImGui::IsItemHovered())
|
||||
splitterColor = ImGui::GetColorU32(ImGuiCol_SeparatorHovered);
|
||||
if(ImGui::IsItemActive())
|
||||
splitterColor = ImGui::GetColorU32(ImGuiCol_SeparatorActive);
|
||||
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), splitterColor);
|
||||
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
|
||||
if(ImGui::BeginChild("##AssetsItemView", {0.0f, 0.0f}, ImGuiChildFlags_Borders, childFlags))
|
||||
{
|
||||
DefineItemView();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineTreeView()
|
||||
{
|
||||
const bool assetsOpen = ImGui::CollapsingHeader("Assets", ImGuiTreeNodeFlags_DefaultOpen);
|
||||
const std::filesystem::path assetPath = TSE::AssetLibrary::GetCurrentAssetPath();
|
||||
std::error_code errorCode;
|
||||
|
||||
if(assetPath.empty() || !std::filesystem::exists(assetPath, errorCode) || !std::filesystem::is_directory(assetPath, errorCode))
|
||||
{
|
||||
DefineFolderNamePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
AcceptAssetDrop(assetPath);
|
||||
|
||||
if(assetsOpen)
|
||||
{
|
||||
DisplayDirectoryTree(assetPath);
|
||||
|
||||
ImVec2 remainingSpace = ImGui::GetContentRegionAvail();
|
||||
if(remainingSpace.y > 0.0f)
|
||||
{
|
||||
if(remainingSpace.x < 1.0f)
|
||||
remainingSpace.x = 1.0f;
|
||||
|
||||
ImGui::InvisibleButton("##AssetsRootDropArea", remainingSpace);
|
||||
AcceptAssetDrop(assetPath);
|
||||
if(ImGui::BeginPopupContextItem("##AssetsRootContext"))
|
||||
{
|
||||
DefineFolderBackgroundContextMenu(assetPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefineFolderNamePopup();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemView()
|
||||
{
|
||||
DefineItemViewToolbar();
|
||||
|
||||
const std::filesystem::path browserPath = GetCurrentBrowserPath();
|
||||
std::error_code errorCode;
|
||||
if(browserPath.empty() || !std::filesystem::exists(browserPath, errorCode) || !std::filesystem::is_directory(browserPath, errorCode))
|
||||
return;
|
||||
|
||||
if(ImGui::BeginChild("##AssetsItemScroll", {0.0f, 0.0f}, ImGuiChildFlags_None, ImGuiWindowFlags_AlwaysVerticalScrollbar))
|
||||
{
|
||||
if(itemViewMode == ItemViewMode::List)
|
||||
DefineItemListView(browserPath);
|
||||
else
|
||||
DefineItemTileView(browserPath);
|
||||
|
||||
ImVec2 remainingSpace = ImGui::GetContentRegionAvail();
|
||||
if(remainingSpace.y > 0.0f)
|
||||
{
|
||||
if(remainingSpace.x < 1.0f)
|
||||
remainingSpace.x = 1.0f;
|
||||
|
||||
ImGui::InvisibleButton("##AssetsItemRootDropArea", remainingSpace);
|
||||
AcceptAssetDrop(browserPath);
|
||||
if(ImGui::BeginPopupContextItem("##AssetsItemRootContext"))
|
||||
{
|
||||
DefineFolderBackgroundContextMenu(browserPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupContextWindow("##AssetsItemBackgroundContext", ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems))
|
||||
{
|
||||
DefineFolderBackgroundContextMenu(browserPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemViewToolbar()
|
||||
{
|
||||
const float buttonWidth = 64.0f;
|
||||
const float totalWidth = buttonWidth * 2.0f + ImGui::GetStyle().ItemSpacing.x;
|
||||
const float startX = ImGui::GetContentRegionAvail().x - totalWidth;
|
||||
if(startX > 0.0f)
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + startX);
|
||||
|
||||
if(ImGui::Selectable("List", itemViewMode == ItemViewMode::List, 0, {buttonWidth, 0.0f}))
|
||||
itemViewMode = ItemViewMode::List;
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("Tiles", itemViewMode == ItemViewMode::Tiles, 0, {buttonWidth, 0.0f}))
|
||||
itemViewMode = ItemViewMode::Tiles;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemListView(const std::filesystem::path& folderPath)
|
||||
{
|
||||
for(const auto& folderEntry : GetDirectories(folderPath))
|
||||
DefineFolderListItem(folderEntry);
|
||||
|
||||
for(const auto& fileEntry : GetFiles(folderPath))
|
||||
DefineFileListItem(fileEntry);
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineItemTileView(const std::filesystem::path& folderPath)
|
||||
{
|
||||
const float tileSize = 96.0f;
|
||||
const float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
const float availableWidth = ImGui::GetContentRegionAvail().x;
|
||||
int columns = static_cast<int>(availableWidth / (tileSize + spacing));
|
||||
if(columns < 1)
|
||||
columns = 1;
|
||||
|
||||
int index = 0;
|
||||
auto nextTile = [&]()
|
||||
{
|
||||
index++;
|
||||
if(index % columns != 0)
|
||||
ImGui::SameLine();
|
||||
};
|
||||
|
||||
for(const auto& folderEntry : GetDirectories(folderPath))
|
||||
{
|
||||
DefineFolderTileItem(folderEntry);
|
||||
nextTile();
|
||||
}
|
||||
|
||||
for(const auto& fileEntry : GetFiles(folderPath))
|
||||
{
|
||||
DefineFileTileItem(fileEntry);
|
||||
nextTile();
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderListItem(const std::filesystem::directory_entry& folderEntry)
|
||||
{
|
||||
const std::filesystem::path folderPath = folderEntry.path();
|
||||
ImGui::PushID(folderPath.string().c_str());
|
||||
|
||||
ImGui::TextUnformatted("[D]");
|
||||
ImGui::SameLine();
|
||||
|
||||
const bool selected = currentSelectedPath == folderPath;
|
||||
ImGui::Selectable(folderPath.filename().string().c_str(), selected, 0, {ImGui::GetContentRegionAvail().x, 0.0f});
|
||||
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
currentSelectedPath = folderPath;
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = folderPath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", folderPath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
AcceptAssetDrop(folderPath);
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFolderContextMenu(folderPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFileListItem(const std::filesystem::directory_entry& fileEntry)
|
||||
{
|
||||
const std::filesystem::path filePath = fileEntry.path();
|
||||
ImGui::PushID(filePath.string().c_str());
|
||||
|
||||
ImGui::TextUnformatted("[F]");
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Selectable(filePath.filename().string().c_str(), false, 0, {ImGui::GetContentRegionAvail().x, 0.0f});
|
||||
if(ImGui::IsItemClicked(ImGuiMouseButton_Left))
|
||||
OpenFileProperties(filePath.string());
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = filePath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FILE_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", filePath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFileContextMenu(filePath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderTileItem(const std::filesystem::directory_entry& folderEntry)
|
||||
{
|
||||
const std::filesystem::path folderPath = folderEntry.path();
|
||||
const float tileSize = 96.0f;
|
||||
ImGui::PushID(folderPath.string().c_str());
|
||||
|
||||
const bool selected = currentSelectedPath == folderPath;
|
||||
ImGui::Selectable("##FolderTile", selected, 0, {tileSize, tileSize});
|
||||
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
currentSelectedPath = folderPath;
|
||||
|
||||
ImVec2 itemMin = ImGui::GetItemRectMin();
|
||||
ImVec2 itemMax = ImGui::GetItemRectMax();
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const std::string icon = "[D]";
|
||||
const std::string name = FitTextToWidth(folderPath.filename().string(), tileSize - 12.0f);
|
||||
const ImVec2 iconSize = ImGui::CalcTextSize(icon.c_str());
|
||||
const ImVec2 nameSize = ImGui::CalcTextSize(name.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - iconSize.x) * 0.5f, itemMin.y + 22.0f}, ImGui::GetColorU32(ImGuiCol_Text), icon.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - nameSize.x) * 0.5f, itemMax.y - 28.0f}, ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = folderPath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", folderPath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
AcceptAssetDrop(folderPath);
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFolderContextMenu(folderPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFileTileItem(const std::filesystem::directory_entry& fileEntry)
|
||||
{
|
||||
const std::filesystem::path filePath = fileEntry.path();
|
||||
const float tileSize = 96.0f;
|
||||
ImGui::PushID(filePath.string().c_str());
|
||||
|
||||
ImGui::Selectable("##FileTile", false, 0, {tileSize, tileSize});
|
||||
if(ImGui::IsItemClicked(ImGuiMouseButton_Left))
|
||||
OpenFileProperties(filePath.string());
|
||||
|
||||
ImVec2 itemMin = ImGui::GetItemRectMin();
|
||||
ImVec2 itemMax = ImGui::GetItemRectMax();
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const std::string icon = "[F]";
|
||||
const std::string name = FitTextToWidth(filePath.filename().string(), tileSize - 12.0f);
|
||||
const ImVec2 iconSize = ImGui::CalcTextSize(icon.c_str());
|
||||
const ImVec2 nameSize = ImGui::CalcTextSize(name.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - iconSize.x) * 0.5f, itemMin.y + 22.0f}, ImGui::GetColorU32(ImGuiCol_Text), icon.c_str());
|
||||
drawList->AddText({itemMin.x + (tileSize - nameSize.x) * 0.5f, itemMax.y - 28.0f}, ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = filePath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FILE_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", filePath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
DefineFileContextMenu(filePath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DisplayDirectoryTree(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::vector<std::filesystem::directory_entry> childDirectories = GetDirectories(folderPath);
|
||||
for(const auto& childDirectory : childDirectories)
|
||||
{
|
||||
const std::filesystem::path childPath = childDirectory.path();
|
||||
const std::vector<std::filesystem::directory_entry> grandChildDirectories = GetDirectories(childPath);
|
||||
|
||||
ImGui::PushID(childPath.string().c_str());
|
||||
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanFullWidth;
|
||||
if(grandChildDirectories.empty())
|
||||
flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
|
||||
if(currentSelectedPath == childPath)
|
||||
flags |= ImGuiTreeNodeFlags_Selected;
|
||||
|
||||
const bool open = ImGui::TreeNodeEx(childPath.filename().string().c_str(), flags);
|
||||
if(ImGui::IsItemClicked(ImGuiMouseButton_Left) && !ImGui::IsItemToggledOpen())
|
||||
currentSelectedPath = childPath;
|
||||
|
||||
if(ImGui::BeginDragDropSource())
|
||||
{
|
||||
const std::string payloadPath = childPath.string();
|
||||
ImGui::SetDragDropPayload("ASSET_FOLDER_PATH", payloadPath.c_str(), payloadPath.size() + 1);
|
||||
ImGui::Text("%s", childPath.filename().string().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
AcceptAssetDrop(childPath);
|
||||
|
||||
bool folderDeleted = false;
|
||||
if(ImGui::BeginPopupContextItem())
|
||||
{
|
||||
folderDeleted = DefineFolderContextMenu(childPath);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if(open && !grandChildDirectories.empty())
|
||||
{
|
||||
if(!folderDeleted)
|
||||
DisplayDirectoryTree(childPath);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
bool TSE::EDITOR::AssetsView::DefineFolderContextMenu(const std::filesystem::path& folderPath)
|
||||
{
|
||||
if(ImGui::MenuItem("Rename"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::Rename, folderPath);
|
||||
|
||||
bool folderDeleted = false;
|
||||
if(ImGui::MenuItem("Delete"))
|
||||
{
|
||||
DeleteFolder(folderPath);
|
||||
folderDeleted = true;
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("New Folder"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::NewFolder, folderPath);
|
||||
|
||||
return folderDeleted;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderBackgroundContextMenu(const std::filesystem::path& folderPath)
|
||||
{
|
||||
if(ImGui::MenuItem("New Folder"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::NewFolder, folderPath);
|
||||
}
|
||||
|
||||
bool TSE::EDITOR::AssetsView::DefineFileContextMenu(const std::filesystem::path& filePath)
|
||||
{
|
||||
if(ImGui::MenuItem("Rename"))
|
||||
RequestFolderNamePopup(FolderPopupPurpose::Rename, filePath);
|
||||
|
||||
bool fileDeleted = false;
|
||||
if(ImGui::MenuItem("Delete"))
|
||||
{
|
||||
DeleteFile(filePath);
|
||||
fileDeleted = true;
|
||||
}
|
||||
|
||||
return fileDeleted;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::AcceptAssetDrop(const std::filesystem::path& targetFolderPath)
|
||||
{
|
||||
if(ImGui::BeginDragDropTarget())
|
||||
{
|
||||
const ImGuiPayload* folderPayload = ImGui::AcceptDragDropPayload("ASSET_FOLDER_PATH");
|
||||
if(folderPayload != nullptr && folderPayload->Data != nullptr)
|
||||
{
|
||||
const char* payloadPath = static_cast<const char*>(folderPayload->Data);
|
||||
MoveFolder(std::filesystem::path(payloadPath), targetFolderPath);
|
||||
}
|
||||
|
||||
const ImGuiPayload* filePayload = ImGui::AcceptDragDropPayload("ASSET_FILE_PATH");
|
||||
if(filePayload != nullptr && filePayload->Data != nullptr)
|
||||
{
|
||||
const char* payloadPath = static_cast<const char*>(filePayload->Data);
|
||||
MoveFile(std::filesystem::path(payloadPath), targetFolderPath);
|
||||
}
|
||||
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::MoveFolder(const std::filesystem::path& sourceFolderPath, const std::filesystem::path& targetFolderPath)
|
||||
{
|
||||
const std::filesystem::path sourcePath = sourceFolderPath.lexically_normal();
|
||||
const std::filesystem::path targetPath = targetFolderPath.lexically_normal();
|
||||
|
||||
if(sourcePath.empty() || targetPath.empty())
|
||||
return;
|
||||
if(sourcePath.parent_path() == targetPath)
|
||||
return;
|
||||
if(IsSameOrChildPath(targetPath, sourcePath))
|
||||
return;
|
||||
|
||||
const std::filesystem::path newFolderPath = targetPath / sourcePath.filename();
|
||||
std::error_code errorCode;
|
||||
if(std::filesystem::exists(newFolderPath, errorCode))
|
||||
return;
|
||||
|
||||
std::filesystem::rename(sourcePath, newFolderPath, errorCode);
|
||||
if(errorCode)
|
||||
return;
|
||||
|
||||
if(currentSelectedPath == sourcePath)
|
||||
currentSelectedPath = newFolderPath;
|
||||
else if(IsSameOrChildPath(currentSelectedPath, sourcePath))
|
||||
currentSelectedPath = newFolderPath / currentSelectedPath.lexically_relative(sourcePath);
|
||||
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::MoveFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& targetFolderPath)
|
||||
{
|
||||
const std::filesystem::path sourcePath = sourceFilePath.lexically_normal();
|
||||
const std::filesystem::path targetPath = targetFolderPath.lexically_normal();
|
||||
|
||||
if(sourcePath.empty() || targetPath.empty())
|
||||
return;
|
||||
if(sourcePath.parent_path() == targetPath)
|
||||
return;
|
||||
|
||||
const std::filesystem::path newFilePath = targetPath / sourcePath.filename();
|
||||
std::error_code errorCode;
|
||||
if(std::filesystem::exists(newFilePath, errorCode))
|
||||
return;
|
||||
|
||||
std::filesystem::rename(sourcePath, newFilePath, errorCode);
|
||||
if(!errorCode)
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::RequestFolderNamePopup(FolderPopupPurpose purpose, const std::filesystem::path& folderPath)
|
||||
{
|
||||
folderPopupPurpose = purpose;
|
||||
pendingFolderPath = folderPath;
|
||||
openFolderNamePopup = true;
|
||||
focusFolderNameInput = true;
|
||||
|
||||
const std::string defaultName = purpose == FolderPopupPurpose::Rename ? folderPath.filename().string() : "";
|
||||
std::snprintf(folderNameBuffer, sizeof(folderNameBuffer), "%s", defaultName.c_str());
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DefineFolderNamePopup()
|
||||
{
|
||||
if(openFolderNamePopup)
|
||||
{
|
||||
openFolderNamePopup = false;
|
||||
ImGui::OpenPopup("Folder Name");
|
||||
}
|
||||
|
||||
if(ImGui::BeginPopupModal("Folder Name", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
ImGui::Text("Name:");
|
||||
if(focusFolderNameInput)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focusFolderNameInput = false;
|
||||
}
|
||||
ImGui::InputText("##FolderName", folderNameBuffer, sizeof(folderNameBuffer));
|
||||
|
||||
const bool inputValid = folderNameBuffer[0] != '\0';
|
||||
if(!inputValid)
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Not Valid.");
|
||||
|
||||
if(!inputValid)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
|
||||
if(ImGui::Button("OK", ImVec2(120, 0)) && inputValid)
|
||||
{
|
||||
ConfirmFolderNamePopup();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if(!inputValid)
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Cancel", ImVec2(120, 0)))
|
||||
{
|
||||
folderPopupPurpose = FolderPopupPurpose::None;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::ConfirmFolderNamePopup()
|
||||
{
|
||||
const std::string folderName = folderNameBuffer;
|
||||
if(folderName.empty())
|
||||
return;
|
||||
|
||||
std::error_code errorCode;
|
||||
if(folderPopupPurpose == FolderPopupPurpose::Rename)
|
||||
{
|
||||
const std::filesystem::path newFolderPath = pendingFolderPath.parent_path() / folderName;
|
||||
if(newFolderPath != pendingFolderPath && !std::filesystem::exists(newFolderPath, errorCode))
|
||||
{
|
||||
const bool selectedPathIsInRenamedFolder = IsSameOrChildPath(currentSelectedPath, pendingFolderPath);
|
||||
const std::filesystem::path selectedRelativePath = selectedPathIsInRenamedFolder ? currentSelectedPath.lexically_relative(pendingFolderPath) : std::filesystem::path();
|
||||
|
||||
std::filesystem::rename(pendingFolderPath, newFolderPath, errorCode);
|
||||
if(!errorCode)
|
||||
{
|
||||
if(selectedPathIsInRenamedFolder)
|
||||
currentSelectedPath = newFolderPath / selectedRelativePath;
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(folderPopupPurpose == FolderPopupPurpose::NewFolder)
|
||||
{
|
||||
const std::filesystem::path newFolderPath = pendingFolderPath / folderName;
|
||||
if(!std::filesystem::exists(newFolderPath, errorCode))
|
||||
{
|
||||
std::filesystem::create_directory(newFolderPath, errorCode);
|
||||
if(!errorCode)
|
||||
{
|
||||
currentSelectedPath = newFolderPath;
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
folderPopupPurpose = FolderPopupPurpose::None;
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DeleteFolder(const std::filesystem::path& folderPath)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
std::filesystem::remove_all(folderPath, errorCode);
|
||||
|
||||
if(!errorCode && IsPathInFolder(currentSelectedPath, folderPath))
|
||||
currentSelectedPath.clear();
|
||||
if(!errorCode)
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::DeleteFile(const std::filesystem::path& filePath)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
std::filesystem::remove(filePath, errorCode);
|
||||
|
||||
if(!errorCode)
|
||||
TSE::AssetLibrary::RescanAssets();
|
||||
}
|
||||
|
||||
std::filesystem::path TSE::EDITOR::AssetsView::GetCurrentBrowserPath() const
|
||||
{
|
||||
std::error_code errorCode;
|
||||
if(!currentSelectedPath.empty() && std::filesystem::exists(currentSelectedPath, errorCode) && std::filesystem::is_directory(currentSelectedPath, errorCode))
|
||||
return currentSelectedPath;
|
||||
|
||||
return TSE::AssetLibrary::GetCurrentAssetPath();
|
||||
}
|
||||
|
||||
void TSE::EDITOR::AssetsView::OpenFileProperties(string path)
|
||||
{
|
||||
string metapath = path + ".meta";
|
||||
if(FileExists(metapath))
|
||||
{
|
||||
using json = nlohmann::ordered_json;
|
||||
std::ifstream metaStream;
|
||||
OpenFileReading(metaStream, metapath);
|
||||
json meta = json::parse(metaStream);
|
||||
|
||||
uuids::uuid id = uuids::uuid::from_string((string)meta["id"]).value_or(id);
|
||||
|
||||
if(!AssetLibrary::HasValue(id)) return;
|
||||
|
||||
IAsset* assetPtr = nullptr;
|
||||
|
||||
if(meta["assetType"] == "material")
|
||||
{
|
||||
assetPtr = static_cast<IAsset*>(AssetLibrary::GetValue<Material*>(id));
|
||||
}
|
||||
else if(meta["assetType"] == "texture")
|
||||
{
|
||||
assetPtr = static_cast<IAsset*>(AssetLibrary::GetValue<Texture*>(id));
|
||||
}
|
||||
|
||||
if(assetPtr != nullptr)
|
||||
{
|
||||
PropertiesView::SetInspectorElement(InspectableType::IAsset, assetPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
TSE_Editor/src/UI/windows/AssetsView.hpp
Normal file
62
TSE_Editor/src/UI/windows/AssetsView.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "UI/base/GuiWindow.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace TSE::EDITOR
|
||||
{
|
||||
class AssetsView : public GuiWindow
|
||||
{
|
||||
private:
|
||||
enum class FolderPopupPurpose
|
||||
{
|
||||
None,
|
||||
Rename,
|
||||
NewFolder
|
||||
};
|
||||
|
||||
enum class ItemViewMode
|
||||
{
|
||||
List,
|
||||
Tiles
|
||||
};
|
||||
|
||||
float leftPanelWidth = -1.0f;
|
||||
ItemViewMode itemViewMode = ItemViewMode::List;
|
||||
std::filesystem::path currentSelectedPath;
|
||||
std::filesystem::path pendingFolderPath;
|
||||
FolderPopupPurpose folderPopupPurpose = FolderPopupPurpose::None;
|
||||
bool openFolderNamePopup = false;
|
||||
bool focusFolderNameInput = false;
|
||||
char folderNameBuffer[256] = "";
|
||||
|
||||
void DefineTreeView();
|
||||
void DefineItemView();
|
||||
void DefineItemViewToolbar();
|
||||
void DefineItemListView(const std::filesystem::path& folderPath);
|
||||
void DefineItemTileView(const std::filesystem::path& folderPath);
|
||||
void DefineFolderListItem(const std::filesystem::directory_entry& folderEntry);
|
||||
void DefineFileListItem(const std::filesystem::directory_entry& fileEntry);
|
||||
void DefineFolderTileItem(const std::filesystem::directory_entry& folderEntry);
|
||||
void DefineFileTileItem(const std::filesystem::directory_entry& fileEntry);
|
||||
void DisplayDirectoryTree(const std::filesystem::path& folderPath);
|
||||
bool DefineFolderContextMenu(const std::filesystem::path& folderPath);
|
||||
void DefineFolderBackgroundContextMenu(const std::filesystem::path& folderPath);
|
||||
bool DefineFileContextMenu(const std::filesystem::path& filePath);
|
||||
void AcceptAssetDrop(const std::filesystem::path& targetFolderPath);
|
||||
void MoveFolder(const std::filesystem::path& sourceFolderPath, const std::filesystem::path& targetFolderPath);
|
||||
void MoveFile(const std::filesystem::path& sourceFilePath, const std::filesystem::path& targetFolderPath);
|
||||
void RequestFolderNamePopup(FolderPopupPurpose purpose, const std::filesystem::path& folderPath);
|
||||
void DefineFolderNamePopup();
|
||||
void ConfirmFolderNamePopup();
|
||||
void DeleteFolder(const std::filesystem::path& folderPath);
|
||||
void DeleteFile(const std::filesystem::path& filePath);
|
||||
std::filesystem::path GetCurrentBrowserPath() const;
|
||||
void OpenFileProperties(string path);
|
||||
|
||||
public:
|
||||
AssetsView();
|
||||
void Define() override;
|
||||
};
|
||||
} // namespace TSE::EDITOR
|
||||
Reference in New Issue
Block a user