Adds background asset scanning and loading to improve editor responsiveness. Updates the file browser with grid and list views, filtering, and callbacks for file selection. Fixes an issue where the asset manager would block the main thread during asset loading.
188 lines
6.3 KiB
C++
188 lines
6.3 KiB
C++
// File: src/FileBrowser.cpp
|
|
#include "FileBrowser.h"
|
|
#include <filesystem>
|
|
|
|
namespace OX
|
|
{
|
|
FileBrowser::FileBrowser() = default;
|
|
|
|
FileBrowser::~FileBrowser() = default;
|
|
|
|
void FileBrowser::SetFilter(const std::string &f) { _filter = f; }
|
|
void FileBrowser::SetFileSelectedCallback(FileSelectedCallback cb) { _onFileSelected = std::move(cb); }
|
|
|
|
void FileBrowser::Draw(const char *title)
|
|
{
|
|
ImGui::Begin(title);
|
|
|
|
// --- toolbar now contains back button, inline path, filter & view toggle all on one row ---
|
|
DrawToolbar();
|
|
|
|
// main content
|
|
auto root = AssetManager::GetFileTree();
|
|
std::function<std::shared_ptr<ResourceTreeNode>(std::shared_ptr<ResourceTreeNode>, const std::string &)> find =
|
|
[&](auto node, const std::string &path)-> std::shared_ptr<ResourceTreeNode>
|
|
{
|
|
if (node->path == path) return node;
|
|
for (auto &c: node->children) {
|
|
if (c->isDirectory) {
|
|
if (auto f = find(c, path)) return f;
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
auto node = find(root, _currentPath);
|
|
if (!node) {
|
|
ImGui::TextColored(ImGui::GetStyle().Colors[ImGuiCol_TextDisabled],
|
|
"Path not found: %s", _currentPath.c_str());
|
|
} else if (_gridMode) {
|
|
DrawGridView(node);
|
|
} else {
|
|
DrawListView(node);
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
// ——— Combined toolbar & inline path ———
|
|
void FileBrowser::DrawToolbar()
|
|
{
|
|
// back button
|
|
if (_currentPath != "res://") {
|
|
if (ImGui::Button("Back")) {
|
|
auto pos = _currentPath.find_last_of('/');
|
|
_currentPath = (pos != std::string::npos && pos > 6)
|
|
? _currentPath.substr(0, pos)
|
|
: "res://";
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
// inline, non-interactive path text
|
|
ImGui::Text("%s", _currentPath.c_str());
|
|
|
|
ImGui::SameLine();
|
|
|
|
// filter input takes all remaining space
|
|
float avail = ImGui::GetContentRegionAvail().x;
|
|
ImGui::PushItemWidth(avail * 0.5f);
|
|
//ImGui::InputTextWithHint("##filter", "Filter files...", _filter.c_str(), ImGuiInputTextFlags_AutoSelectAll);
|
|
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::SameLine();
|
|
|
|
// grid/list toggle
|
|
if (_gridMode) {
|
|
if (ImGui::Button("List View")) _gridMode = false;
|
|
} else {
|
|
if (ImGui::Button("Grid View")) _gridMode = true;
|
|
}
|
|
|
|
ImGui::Separator();
|
|
}
|
|
|
|
// ——— Polished grid view ———
|
|
void FileBrowser::DrawGridView(const std::shared_ptr<ResourceTreeNode> &node)
|
|
{
|
|
const float cellW = _cfg.thumbnailSize + _cfg.padding * 2;
|
|
ImVec2 avail = ImGui::GetContentRegionAvail();
|
|
int cols = std::max(1, int(avail.x / cellW));
|
|
|
|
ImGui::BeginChild("GridRegion", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysUseWindowPadding);
|
|
ImGui::Columns(cols, nullptr, false);
|
|
|
|
for (auto &c: node->children) {
|
|
if (!_filter.empty() && !PassesFilter(c->name)) continue;
|
|
if (!c) return;
|
|
ImGui::PushID(c->path.c_str());
|
|
ImGui::BeginGroup();
|
|
|
|
// invisible button as hit target
|
|
ImVec2 btnSize(cellW, _cfg.thumbnailSize + _cfg.labelHeight + _cfg.padding);
|
|
ImGui::InvisibleButton("cell", btnSize);
|
|
bool hovered = ImGui::IsItemHovered();
|
|
bool clicked = ImGui::IsItemClicked();
|
|
|
|
// background rect
|
|
ImVec2 mn = ImGui::GetItemRectMin();
|
|
ImVec2 mx = ImGui::GetItemRectMax();
|
|
ImU32 bg = hovered ? _cfg.highlightColor : _cfg.bgColor;
|
|
ImGui::GetWindowDrawList()
|
|
->AddRectFilled(mn, mx, bg, 4.0f);
|
|
|
|
// thumbnail or icon
|
|
ImGui::SetCursorScreenPos({mn.x + _cfg.padding, mn.y + _cfg.padding});
|
|
if (c->isDirectory) {
|
|
ImGui::Image(GetIconTexture(*c),
|
|
{_cfg.thumbnailSize, _cfg.thumbnailSize});
|
|
} else {
|
|
auto asset = AssetManager::Get(c->path);
|
|
if (asset && asset->GetTypeName() == "texture2D") {
|
|
auto tex = std::static_pointer_cast<Texture2D>(asset);
|
|
ImGui::Image((ImTextureID) (intptr_t) tex->GetID(),
|
|
{_cfg.thumbnailSize, _cfg.thumbnailSize},
|
|
{0, 1}, {1, 0});
|
|
} else {
|
|
ImGui::Dummy({_cfg.thumbnailSize, _cfg.thumbnailSize});
|
|
}
|
|
}
|
|
|
|
// label
|
|
ImGui::SetCursorScreenPos({mn.x + _cfg.padding, mx.y - _cfg.labelHeight});
|
|
ImGui::PushTextWrapPos(mx.x);
|
|
ImGui::TextUnformatted(c->name.c_str());
|
|
ImGui::PopTextWrapPos();
|
|
|
|
// click handling
|
|
if (clicked) {
|
|
if (c->isDirectory) {
|
|
_currentPath = c->path;
|
|
} else if (_onFileSelected) {
|
|
_onFileSelected(c->path);
|
|
}
|
|
}
|
|
|
|
ImGui::EndGroup();
|
|
ImGui::NextColumn();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::Columns(1);
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
// ——— Simple list view ———
|
|
void FileBrowser::DrawListView(const std::shared_ptr<ResourceTreeNode> &node)
|
|
{
|
|
ImGui::BeginChild("ListRegion", ImVec2(0, 0), false);
|
|
|
|
for (auto &c: node->children) {
|
|
if (!_filter.empty() && !PassesFilter(c->name)) continue;
|
|
if (ImGui::Selectable(c->name.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick)) {
|
|
if (ImGui::IsMouseDoubleClicked(0)) {
|
|
if (c->isDirectory) {
|
|
_currentPath = c->path;
|
|
} else if (_onFileSelected) {
|
|
_onFileSelected(c->path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
bool FileBrowser::PassesFilter(const std::string &name) const
|
|
{
|
|
return _filter.empty() ||
|
|
(name.find(_filter) != std::string::npos);
|
|
}
|
|
|
|
ImTextureID FileBrowser::GetIconTexture(const ResourceTreeNode &node)
|
|
{
|
|
return 0;
|
|
}
|
|
} // namespace OX
|