Onyx-Engine/src/editor/Windows/FileBrowser.cpp
OusmBlueNinja c177c8b9ea Improves asset loading and file browsing
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.
2025-05-21 16:38:18 -05:00

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