465 lines
12 KiB
C++
465 lines
12 KiB
C++
// =====================================================================================================================
|
|
// Copyright 2024 Medusa Slockbower
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// =====================================================================================================================
|
|
|
|
#include <Editor/EditorSystem.h>
|
|
#include <FileSystem/FileManager.h>
|
|
#include <Core/Console.h>
|
|
|
|
|
|
#include <imgui-docking/misc/cpp/imgui_stdlib.h>
|
|
|
|
using namespace OpenShaderDesigner;
|
|
|
|
FileManager::Asset* FileManager::load(const Path& path, FileID id)
|
|
{
|
|
Console::Log(Console::Message, "Loading File {}", path.string());
|
|
|
|
// Create Folder dummy Asset for Folders
|
|
if(is_directory(path))
|
|
{
|
|
Asset* asset = new Folder(path);
|
|
asset->Manager_ = EditorSystem::Get<FileManager>();
|
|
asset->File_ = id;
|
|
return asset;
|
|
}
|
|
|
|
// Get the type and load the asset
|
|
AssetType type = ExtensionMap()[path.extension().string()];
|
|
Asset* asset = AssetMenu()[type].Load(path);
|
|
|
|
// Set the ID and Manager of the file
|
|
if(asset)
|
|
{
|
|
asset->Manager_ = EditorSystem::Get<FileManager>();
|
|
asset->File_ = id;
|
|
}
|
|
return asset;
|
|
}
|
|
|
|
FileManager::Asset* FileManager::import(const Path &src, const Path &dst, FileID id)
|
|
{
|
|
Console::Log(Console::Message, "Importing File {} to {}", src.string(), dst.string());
|
|
|
|
// Get the type and load the asset
|
|
AssetType type = ExtensionMap()[src.extension().string()];
|
|
Asset* asset = AssetMenu()[type].Import(src, dst);
|
|
|
|
if(asset)
|
|
{
|
|
asset->Manager_ = EditorSystem::Get<FileManager>();
|
|
asset->File_ = id;
|
|
}
|
|
|
|
return asset;
|
|
}
|
|
|
|
FileManager::Asset* FileManager::create(const Path &path, FileID id)
|
|
{
|
|
Console::Log(Console::Message, "Creating File {}", path.string());
|
|
|
|
// Create Folder dummy Asset for Folders
|
|
if(is_directory(path))
|
|
{
|
|
Asset* asset = new Folder(path);
|
|
asset->Manager_ = EditorSystem::Get<FileManager>();
|
|
return asset;
|
|
}
|
|
|
|
// Get the type and load the asset
|
|
AssetType type = ExtensionMap()[path.extension().string()];
|
|
Asset* asset = AssetMenu()[type].Create(path);
|
|
|
|
if(asset)
|
|
{
|
|
asset->Manager_ = EditorSystem::Get<FileManager>();
|
|
asset->File_ = id;
|
|
}
|
|
|
|
return asset;
|
|
}
|
|
|
|
FileManager::FileManager()
|
|
: EditorWindow("File Manager", ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar)
|
|
, CurrentDirectory_(FileSystem::root), Selected_(FileSystem::root)
|
|
, Rename_(false), FocusRename_(false)
|
|
{
|
|
//CurrentDirectory_ = Filesystem_.load_directory(std::filesystem::current_path());
|
|
}
|
|
|
|
void FileManager::DrawMenu()
|
|
{
|
|
if(ImGui::BeginMenu("File"))
|
|
{
|
|
if(ImGui::BeginMenu("Create"))
|
|
{
|
|
struct Visitor
|
|
{
|
|
bool operator()(AssetDetail& item, AssetType id)
|
|
{
|
|
AssetMenuHierarchy& AssetMenu = FileManager::AssetMenu();
|
|
const auto depth = AssetMenu.depth(id);
|
|
if(depth > Context.size()) return false;
|
|
if(item.Name == "##") return false;
|
|
|
|
while(depth < Context.size())
|
|
{
|
|
Context.pop();
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if(Context.top() != AssetMenu.parent(id)) return false;
|
|
std::string name = std::format("{}##{}", item.Name, id);
|
|
|
|
if(item.Create != nullptr)
|
|
{
|
|
if(ImGui::MenuItem(name.c_str()))
|
|
{
|
|
Manager.Create(std::format("{}{}", item.Name, item.Extensions[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ImGui::BeginMenu(name.c_str()))
|
|
{
|
|
Context.push(id);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FileManager& Manager;
|
|
std::stack<AssetType> Context;
|
|
} MenuVisitor
|
|
{
|
|
.Manager = *this
|
|
};
|
|
|
|
MenuVisitor.Context.push(0);
|
|
|
|
AssetMenu().traverse<AssetMenuHierarchy::pre_order>(MenuVisitor);
|
|
|
|
MenuVisitor.Context.pop();
|
|
while(MenuVisitor.Context.empty() == false)
|
|
{
|
|
ImGui::EndMenu();
|
|
MenuVisitor.Context.pop();
|
|
}
|
|
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
|
|
void FileManager::DrawWindow()
|
|
{
|
|
// Directory Hierarchy
|
|
ImGui::BeginGroup();
|
|
if (ImGui::BeginChild("##hierarchy", ImVec2(200, 0),
|
|
ImGuiChildFlags_ResizeX | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_Border))
|
|
{
|
|
struct Visitor
|
|
{
|
|
bool operator()(File& file, FileID id)
|
|
{
|
|
const FileSystem& tree = file.system();
|
|
uint32_t depth = tree.depth(id);
|
|
|
|
if(id == FileSystem::root) return false; // Skip root
|
|
if(*file == nullptr) return false; // Skip files without assets
|
|
|
|
// Skip if we are in a closed directory
|
|
if(depth > stack.size()) return false;
|
|
|
|
// Skip non-directories
|
|
const bool is_dir = file.is_directory();
|
|
if(not is_dir) return false;
|
|
|
|
// Pop dead paths
|
|
while(depth < stack.size())
|
|
{
|
|
stack.pop();
|
|
ImGui::TreePop();
|
|
}
|
|
|
|
// Skip if the top of the stack is not the parent of the file
|
|
if(stack.top() != tree.parent(id)) return false;
|
|
|
|
// Generate display name
|
|
const char* icon = "\ued53";
|
|
std::string name = std::format("{} {}##{}", icon, file.path().stem().string(), id);
|
|
|
|
// Display flags
|
|
int flags = 0;
|
|
if(file.has_subdirectory())
|
|
{
|
|
flags |= ImGuiTreeNodeFlags_OpenOnArrow;
|
|
flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
|
flags |= ImGuiTreeNodeFlags_SpanAvailWidth;
|
|
}
|
|
else
|
|
{
|
|
flags |= ImGuiTreeNodeFlags_Leaf;
|
|
}
|
|
|
|
if(id == directory) flags |= ImGuiTreeNodeFlags_Selected;
|
|
|
|
if(ImGui::TreeNodeEx(name.c_str(), flags))
|
|
{
|
|
stack.push(id);
|
|
}
|
|
|
|
if(ImGui::IsItemClicked()) directory = file.get_id();
|
|
|
|
return false;
|
|
}
|
|
|
|
std::stack<FileID> stack;
|
|
FileID directory;
|
|
} visitor;
|
|
|
|
visitor.directory = CurrentDirectory_;
|
|
visitor.stack.push(FileSystem::root);
|
|
Filesystem_.traverse(visitor);
|
|
|
|
while(visitor.stack.size() > 1)
|
|
{
|
|
ImGui::TreePop();
|
|
visitor.stack.pop();
|
|
}
|
|
|
|
CurrentDirectory_ = visitor.directory;
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::EndGroup();
|
|
|
|
ImGui::SameLine();
|
|
|
|
if(ImGui::BeginTable("##directory", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter))
|
|
{
|
|
ImGui::TableSetupScrollFreeze(0, 1);
|
|
|
|
ImGui::TableSetupColumn(" Name");
|
|
ImGui::TableSetupColumn("Type");
|
|
ImGui::TableSetupColumn("Source");
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for(FileID it = Filesystem_.begin(CurrentDirectory_); it != Filesystem_.end(CurrentDirectory_); it = Filesystem_.next(it))
|
|
{
|
|
File& file = Filesystem_[it];
|
|
ImGui::TableNextRow();
|
|
|
|
const bool selected = Selected_ == it;
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
// Display Name
|
|
bool dirty = *file ? file->Dirty() : false;
|
|
std::string name = std::format("{}{}", dirty ? "\uea81 " : "", file.path().stem().string());
|
|
if(ImGui::Selectable(
|
|
std::format(" {}##{}", Rename_ && selected ? "" : name.c_str(), file.get_id()).c_str()
|
|
, selected, ImGuiSelectableFlags_SpanAllColumns
|
|
))
|
|
{
|
|
Selected_ = Rename_ ? Selected_ : it;
|
|
}
|
|
|
|
if(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && selected)
|
|
{
|
|
File& file = Filesystem_[Selected_];
|
|
Asset* asset = *file;
|
|
asset->Open();
|
|
}
|
|
|
|
// Renaming
|
|
if(selected && Rename_)
|
|
{
|
|
ImGui::SameLine();
|
|
if(FocusRename_) ImGui::SetKeyboardFocusHere();
|
|
|
|
if(ImGui::InputText(" ##rename", &RenameBuffer_, ImGuiInputTextFlags_EnterReturnsTrue))
|
|
{
|
|
if(not RenameBuffer_.empty()) Filesystem_.rename(Selected_, RenameBuffer_);
|
|
|
|
Rename_ = false;
|
|
}
|
|
|
|
if(FocusRename_)
|
|
{
|
|
FocusRename_ = false;
|
|
}
|
|
else if(ImGui::IsItemFocused() == false)
|
|
{
|
|
Rename_ = false;
|
|
}
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", file.path().extension().string().c_str());
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", file.path().string().c_str());
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
|
|
bool FileManager::AnyDirty()
|
|
{
|
|
bool res = false;
|
|
struct Visitor
|
|
{
|
|
bool operator()(File& file, FileID id)
|
|
{
|
|
if(id == FileSystem::root) return false; // Continue if root node
|
|
if(*file == nullptr) return false; // Continue if no asset associated with file
|
|
|
|
res |= file->Dirty();
|
|
return false;
|
|
}
|
|
|
|
bool& res;
|
|
} visitor{ res };
|
|
Filesystem_.traverse(visitor);
|
|
return res;
|
|
}
|
|
|
|
void FileManager::SaveAll()
|
|
{
|
|
|
|
}
|
|
|
|
FileManager::Path FileManager::GetHomeDirectory()
|
|
{
|
|
#ifdef WIN32
|
|
return Path(getenv("HOMEDRIVE")) / getenv("HOMEPATH");
|
|
#else
|
|
return getenv("HOME");
|
|
#endif
|
|
}
|
|
|
|
void FileManager::Register(const std::filesystem::path& path,
|
|
const std::vector<std::string>& extensions,
|
|
CreateFunc create, LoadFunc load, ImportFunc import)
|
|
{
|
|
const std::string name = path.filename().string();
|
|
AssetMenuHierarchy& AssetMenu = FileManager::AssetMenu();
|
|
ExtensionMapping& ExtensionMap = FileManager::ExtensionMap();
|
|
|
|
AssetType node = 0;
|
|
for(auto it = path.begin(); it != path.end();)
|
|
{
|
|
AssetType child = AssetMenu.first_child(node);
|
|
|
|
while(child)
|
|
{
|
|
if(AssetMenu[child].Name == it->filename())
|
|
{
|
|
node = child;
|
|
++it;
|
|
break;
|
|
}
|
|
|
|
child = AssetMenu.next_sibling(child);
|
|
}
|
|
|
|
if(node == 0 || node != child)
|
|
{
|
|
node = AssetMenu.insert({ it->string(), {}, nullptr, nullptr, nullptr }, node);
|
|
++it;
|
|
}
|
|
}
|
|
|
|
AssetMenu[node].Create = create;
|
|
AssetMenu[node].Import = import;
|
|
AssetMenu[node].Load = load;
|
|
AssetMenu[node].Extensions = extensions;
|
|
|
|
for(const auto& ext : extensions)
|
|
{
|
|
ExtensionMap[ext] = node;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|