LumixEngine/src/editor/asset_browser.cpp
Mikulas Florek 98c4f842db refactor
2018-01-24 17:33:39 +01:00

938 lines
No EOL
23 KiB
C++

#include "asset_browser.h"
#include "editor/render_interface.h"
#include "editor/studio_app.h"
#include "editor/world_editor.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/fs/disk_file_device.h"
#include "engine/log.h"
#include "engine/path_utils.h"
#include "engine/profiler.h"
#include "engine/resource.h"
#include "engine/resource_manager.h"
#include "engine/resource_manager_base.h"
#include "engine/string.h"
#include "file_system_watcher.h"
#include "imgui/imgui.h"
#include "metadata.h"
#include "platform_interface.h"
#include "utils.h"
namespace Lumix
{
static const u32 SOURCE_HASH = crc32("source");
bool AssetBrowser::IPlugin::createTile(const char* in_path, const char* out_path, ResourceType type)
{
return false;
}
ResourceType AssetBrowser::getResourceType(const char* path) const
{
char ext[10];
PathUtils::getExtension(ext, sizeof(ext), path);
auto iter = m_registered_extensions.find(crc32(ext));
if (iter.isValid()) return iter.value();
return INVALID_RESOURCE_TYPE;
}
AssetBrowser::AssetBrowser(StudioApp& app)
: m_editor(app.getWorldEditor())
, m_metadata(app.getMetadata())
, m_resources(app.getWorldEditor().getAllocator())
, m_selected_resource(nullptr)
, m_autoreload_changed_resource(true)
, m_changed_files(app.getWorldEditor().getAllocator())
, m_is_focus_requested(false)
, m_changed_files_mutex(false)
, m_history(app.getWorldEditor().getAllocator())
, m_plugins(app.getWorldEditor().getAllocator())
, m_on_resource_changed(app.getWorldEditor().getAllocator())
, m_app(app)
, m_is_update_enabled(true)
, m_current_type(0)
, m_is_open(false)
, m_activate(false)
, m_is_init_finished(false)
, m_show_thumbnails(true)
, m_history_index(-1)
, m_file_infos(app.getWorldEditor().getAllocator())
, m_filtered_file_infos(app.getWorldEditor().getAllocator())
, m_subdirs(app.getWorldEditor().getAllocator())
, m_registered_extensions(app.getWorldEditor().getAllocator())
{
IAllocator& allocator = m_editor.getAllocator();
m_filter[0] = '\0';
const char* base_path = m_editor.getEngine().getDiskFileDevice()->getBasePath();
StaticString<MAX_PATH_LENGTH> path(base_path, ".lumix");
PlatformInterface::makePath(path);
path << "/asset_tiles";
PlatformInterface::makePath(path);
m_watchers[0] = FileSystemWatcher::create(base_path, allocator);
m_watchers[0]->getCallback().bind<AssetBrowser, &AssetBrowser::onFileChanged>(this);
if (m_editor.getEngine().getPatchFileDevice())
{
base_path = m_editor.getEngine().getPatchFileDevice()->getBasePath();
m_watchers[1] = FileSystemWatcher::create(base_path, allocator);
m_watchers[1]->getCallback().bind<AssetBrowser, &AssetBrowser::onFileChanged>(this);
}
else
{
m_watchers[1] = nullptr;
}
m_auto_reload_action = LUMIX_NEW(allocator, Action)("Auto-reload", "Auto-reload assets", "autoReload");
m_auto_reload_action->is_global = false;
m_auto_reload_action->func.bind<AssetBrowser, &AssetBrowser::toggleAutoreload>(this);
m_auto_reload_action->is_selected.bind<AssetBrowser, &AssetBrowser::isAutoreload>(this);
m_back_action = LUMIX_NEW(allocator, Action)("Back", "Back in asset history", "back");
m_back_action->is_global = false;
m_back_action->func.bind<AssetBrowser, &AssetBrowser::goBack>(this);
m_forward_action = LUMIX_NEW(allocator, Action)("Forward", "Forward in asset history", "forward");
m_forward_action->is_global = false;
m_forward_action->func.bind<AssetBrowser, &AssetBrowser::goForward>(this);
m_refresh_action = LUMIX_NEW(allocator, Action)("Refresh", "Refresh assets", "refresh");
m_refresh_action->is_global = false;
m_refresh_action->func.bind<AssetBrowser, &AssetBrowser::findResources>(this);
m_app.addAction(m_auto_reload_action);
m_app.addAction(m_back_action);
m_app.addAction(m_forward_action);
m_app.addAction(m_refresh_action);
}
void AssetBrowser::toggleAutoreload()
{
m_autoreload_changed_resource = !m_autoreload_changed_resource;
}
AssetBrowser::~AssetBrowser()
{
unloadResource();
RenderInterface* ri = m_app.getWorldEditor().getRenderInterface();
for (FileInfo& info : m_file_infos)
{
ri->unloadTexture(info.tex);
}
m_file_infos.clear();
for (auto* plugin : m_plugins)
{
LUMIX_DELETE(m_editor.getAllocator(), plugin);
}
m_plugins.clear();
FileSystemWatcher::destroy(m_watchers[0]);
FileSystemWatcher::destroy(m_watchers[1]);
}
void AssetBrowser::onFileChanged(const char* path)
{
ResourceType resource_type = getResourceType(path);
if (!isValid(resource_type)) return;
MT::SpinLock lock(m_changed_files_mutex);
m_changed_files.push(Path(path));
}
void AssetBrowser::unloadResource()
{
if (!m_selected_resource) return;
for (auto* plugin : m_plugins)
{
plugin->onResourceUnloaded(m_selected_resource);
}
m_selected_resource->getResourceManager().unload(*m_selected_resource);
m_selected_resource = nullptr;
}
void AssetBrowser::update()
{
PROFILE_FUNCTION();
for (auto* plugin : m_plugins) plugin->update();
auto* patch = m_editor.getEngine().getPatchFileDevice();
if ((patch && !equalStrings(patch->getBasePath(), m_patch_base_path)) ||
(!patch && m_patch_base_path[0] != '\0'))
{
findResources();
}
if (!m_is_update_enabled) return;
bool is_empty;
{
MT::SpinLock lock(m_changed_files_mutex);
is_empty = m_changed_files.empty();
}
while (!is_empty)
{
Path path;
{
MT::SpinLock lock(m_changed_files_mutex);
path = m_changed_files.back();
m_changed_files.pop();
is_empty = m_changed_files.empty();
}
char ext[10];
PathUtils::getExtension(ext, lengthOf(ext), path.c_str());
m_on_resource_changed.invoke(path, ext);
ResourceType resource_type = getResourceType(path.c_str());
if (!isValid(resource_type)) continue;
if (m_autoreload_changed_resource) m_editor.getEngine().getResourceManager().reload(path);
char tmp_path[MAX_PATH_LENGTH];
if (m_editor.getEngine().getPatchFileDevice())
{
copyString(tmp_path, m_editor.getEngine().getPatchFileDevice()->getBasePath());
catString(tmp_path, path.c_str());
}
if (!m_editor.getEngine().getPatchFileDevice() || !PlatformInterface::fileExists(tmp_path))
{
copyString(tmp_path, m_editor.getEngine().getDiskFileDevice()->getBasePath());
catString(tmp_path, path.c_str());
if (!PlatformInterface::fileExists(tmp_path))
{
m_resources[resource_type].eraseItemFast(path);
continue;
}
}
char dir[MAX_PATH_LENGTH];
char filename[MAX_PATH_LENGTH];
PathUtils::getDir(dir, sizeof(dir), path.c_str());
PathUtils::getFilename(filename, sizeof(filename), path.c_str());
addResource(dir, filename);
}
m_changed_files.clear();
}
static void clampText(char* text, int width)
{
char* end = text + stringLength(text);
ImVec2 size = ImGui::CalcTextSize(text);
if (size.x <= width) return;
do
{
*(end - 1) = '\0';
*(end - 2) = '.';
*(end - 3) = '.';
*(end - 4) = '.';
--end;
size = ImGui::CalcTextSize(text);
} while (size.x > width && end - text > 4);
}
void AssetBrowser::changeDir(const char* path)
{
RenderInterface* ri = m_app.getWorldEditor().getRenderInterface();
for (FileInfo& info : m_file_infos)
{
ri->unloadTexture(info.tex);
}
m_file_infos.clear();
m_dir = path;
int len = stringLength(m_dir);
if (len > 0 && (m_dir[len - 1] == '/' || m_dir[len - 1] == '\\'))
{
m_dir.data[len - 1] = '\0';
}
IAllocator& allocator = m_app.getWorldEditor().getAllocator();
PlatformInterface::FileIterator* iter = PlatformInterface::createFileIterator(m_dir, allocator);
PlatformInterface::FileInfo info;
m_subdirs.clear();
while (PlatformInterface::getNextFile(iter, &info))
{
if (info.is_directory)
{
if(info.filename[0] != '.') m_subdirs.emplace(info.filename);
continue;
}
StaticString<MAX_PATH_LENGTH> file_path_str(m_dir, "/", info.filename);
Path filepath(file_path_str);
ResourceType type = getResourceType(filepath.c_str());
if (type == INVALID_RESOURCE_TYPE) continue;
FileInfo tile;
char filename[MAX_PATH_LENGTH];
PathUtils::getBasename(filename, lengthOf(filename), filepath.c_str());
clampText(filename, TILE_SIZE);
tile.file_path_hash = filepath.getHash();
tile.filepath = filepath.c_str();
tile.clamped_filename = filename;
m_file_infos.push(tile);
}
doFilter();
PlatformInterface::destroyFileIterator(iter);
}
void AssetBrowser::breadcrumbs()
{
const char* c = m_dir.data;
char tmp[MAX_PATH_LENGTH];
while (*c)
{
char* c_out = tmp;
while (*c && *c != '/')
{
*c_out = *c;
++c_out;
++c;
}
*c_out = '\0';
if (*c == '/') ++c;
if (ImGui::Button(tmp))
{
char new_dir[MAX_PATH_LENGTH];
copyNString(new_dir, lengthOf(new_dir), m_dir, int(c - m_dir.data));
changeDir(new_dir);
}
ImGui::SameLine(0, 1);
ImGui::Text("%s", "/");
ImGui::SameLine(0, 1);
}
ImGui::NewLine();
}
void AssetBrowser::dirColumn()
{
ImVec2 size(m_left_column_width, 0);
ImGui::BeginChild("left_col", size);
ImGui::PushItemWidth(120);
bool b = false;
if (ImGui::Selectable("..", &b))
{
char dir[MAX_PATH_LENGTH];
PathUtils::getDir(dir, lengthOf(dir), m_dir);
changeDir(dir);
}
for (auto& subdir : m_subdirs)
{
if (ImGui::Selectable(subdir, &b))
{
StaticString<MAX_PATH_LENGTH> new_dir(m_dir, "/", subdir);
changeDir(new_dir);
}
}
ImGui::PopItemWidth();
ImGui::EndChild();
}
void AssetBrowser::doFilter()
{
m_filtered_file_infos.clear();
if (!m_filter[0]) return;
for (int i = 0, c = m_file_infos.size(); i < c; ++i)
{
if (stristr(m_file_infos[i].filepath, m_filter)) m_filtered_file_infos.push(i);
}
}
int AssetBrowser::getThumbnailIndex(int i, int j, int columns) const
{
int idx = j * columns + i;
if (!m_filtered_file_infos.empty())
{
if (idx >= m_filtered_file_infos.size()) return -1;
return m_filtered_file_infos[idx];
}
if (idx >= m_file_infos.size())
{
return -1;
}
return idx;
}
void AssetBrowser::createTile(FileInfo& tile, const char* out_path)
{
if (tile.create_called) return;
tile.create_called = true;
for (IPlugin* plugin : m_plugins)
{
ResourceType type = getResourceType(tile.filepath);
if (plugin->createTile(tile.filepath, out_path, type)) break;
}
}
void AssetBrowser::thumbnail(FileInfo& tile)
{
ImGui::BeginGroup();
ImVec2 img_size((float)TILE_SIZE, (float)TILE_SIZE);
if (tile.tex)
{
ImGui::Image(tile.tex, img_size);
}
else
{
ImGui::Rect(img_size.x, img_size.y, 0xffffFFFF);
StaticString<MAX_PATH_LENGTH> path(".lumix/asset_tiles/", tile.file_path_hash, ".dds");
if (PlatformInterface::fileExists(path))
{
RenderInterface* ri = m_app.getWorldEditor().getRenderInterface();
if (PlatformInterface::getLastModified(path) >= PlatformInterface::getLastModified(tile.filepath))
{
tile.tex = ri->loadTexture(Path(path));
}
else
{
createTile(tile, path);
}
}
else
{
createTile(tile, path);
}
}
ImVec2 text_size = ImGui::CalcTextSize(tile.clamped_filename);
ImVec2 pos = ImGui::GetCursorPos();
pos.x += (TILE_SIZE - text_size.x) * 0.5f;
ImGui::SetCursorPos(pos);
ImGui::Text("%s", tile.clamped_filename.data);
ImGui::EndGroup();
}
void AssetBrowser::fileColumn()
{
ImGui::BeginChild("main_col");
float w = ImGui::GetContentRegionAvailWidth();
int columns = m_show_thumbnails ? (int)w / TILE_SIZE : 1;
columns = Math::maximum(columns, 1);
int tile_count = m_filtered_file_infos.empty() ? m_file_infos.size() : m_filtered_file_infos.size();
int row_count = m_show_thumbnails ? (tile_count + columns - 1) / columns : tile_count;
ImGuiListClipper clipper(row_count);
auto callbacks = [this](FileInfo& tile) {
if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", tile.filepath.data);
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
{
ImGui::Text("%s", (const char*)tile.filepath);
ImGui::SetDragDropPayload("path", tile.filepath, stringLength(tile.filepath) + 1, ImGuiCond_Once);
ImGui::EndDragDropSource();
}
else if (ImGui::IsItemHovered() && ImGui::IsMouseReleased(0))
{
selectResource(Path(tile.filepath), true);
}
};
while (clipper.Step())
{
for (int j = clipper.DisplayStart; j < clipper.DisplayEnd; ++j)
{
if (m_show_thumbnails)
{
for (int i = 0; i < columns; ++i)
{
if (i > 0) ImGui::SameLine();
int idx = getThumbnailIndex(i, j, columns);
if (idx < 0) break;
FileInfo& tile = m_file_infos[idx];
thumbnail(tile);
callbacks(tile);
}
}
else
{
if (!m_filtered_file_infos.empty()) j = m_filtered_file_infos[j];
FileInfo& tile = m_file_infos[j];
bool b = m_selected_resource && m_selected_resource->getPath().getHash() == tile.file_path_hash;
ImGui::Selectable(tile.filepath, b);
callbacks(tile);
}
}
}
ImGui::EndChild();
}
void AssetBrowser::detailsGUI()
{
if (ImGui::BeginDock("Asset properties", &m_is_open))
{
ImVec2 pos = ImGui::GetCursorScreenPos();
if (ImGui::BeginToolbar("asset_browser_toolbar", pos, ImVec2(0, 24)))
{
if (m_history_index > 0) m_back_action->toolbarButton();
if (m_history_index < m_history.size() - 1) m_forward_action->toolbarButton();
m_auto_reload_action->toolbarButton();
m_refresh_action->toolbarButton();
}
ImGui::EndToolbar();
if (!m_selected_resource)
{
ImGui::EndDock();
return;
}
const char* path = m_selected_resource->getPath().c_str();
ImGui::Separator();
ImGui::LabelText("Selected resource", "%s", path);
ImGui::Separator();
char source[MAX_PATH_LENGTH];
if (m_metadata.getString(m_selected_resource->getPath().getHash(), SOURCE_HASH, source, lengthOf(source)))
{
ImGui::LabelText("Source", "%s", source);
}
ImGui::LabelText("Status", "%s", m_selected_resource->isFailure() ? "failure" : (m_selected_resource->isReady() ? "Ready" : "Not ready"));
ResourceType resource_type = getResourceType(path);
IPlugin* plugin = m_plugins.get(resource_type);
if (m_selected_resource->isReady()) plugin->onGUI(m_selected_resource);
}
ImGui::EndDock();
}
void AssetBrowser::onGUI()
{
if (m_dir.data[0] == '\0') changeDir(".");
if (m_wanted_resource.isValid())
{
selectResource(m_wanted_resource, true);
m_wanted_resource = "";
}
m_is_open = m_is_open || m_activate;
if (!ImGui::BeginDock("Assets", &m_is_open))
{
if (m_activate) ImGui::SetDockActive();
m_activate = false;
ImGui::EndDock();
detailsGUI();
return;
}
if (m_is_focus_requested)
{
m_is_focus_requested = false;
ImGui::SetWindowFocus();
}
if (m_activate) ImGui::SetDockActive();
m_activate = false;
float checkbox_w = ImGui::GetCursorPosX();
ImGui::Checkbox("Thumbnails", &m_show_thumbnails);
ImGui::SameLine();
checkbox_w = ImGui::GetCursorPosX() - checkbox_w;
if (ImGui::LabellessInputText("Filter", m_filter, sizeof(m_filter), 100)) doFilter();
ImGui::SameLine(130 + checkbox_w);
breadcrumbs();
ImGui::Separator();
float content_w = ImGui::GetContentRegionAvailWidth();
ImVec2 left_size(m_left_column_width, 0);
if (left_size.x < 10) left_size.x = 10;
if (left_size.x > content_w - 10) left_size.x = content_w - 10;
dirColumn();
ImGui::SameLine();
ImGui::VSplitter("vsplit1", &left_size);
m_left_column_width = left_size.x;
ImGui::SameLine();
fileColumn();
ImGui::EndDock();
detailsGUI();
}
void AssetBrowser::selectResource(Resource* resource, bool record_history)
{
if (record_history)
{
while (m_history_index < m_history.size() - 1)
{
m_history.pop();
}
m_history_index++;
m_history.push(resource->getPath());
if (m_history.size() > 20)
{
--m_history_index;
m_history.erase(0);
}
}
m_wanted_resource = "";
unloadResource();
m_selected_resource = resource;
ASSERT(m_selected_resource->getRefCount() > 0);
}
void AssetBrowser::onInitFinished()
{
m_is_init_finished = true;
findResources();
}
void AssetBrowser::addPlugin(IPlugin& plugin)
{
m_plugins.insert(plugin.getResourceType(), &plugin);
if(m_is_init_finished) findResources();
}
void AssetBrowser::selectResource(const Path& resource, bool record_history)
{
m_activate = true;
char ext[30];
PathUtils::getExtension(ext, lengthOf(ext), resource.c_str());
auto& manager = m_editor.getEngine().getResourceManager();
auto* resource_manager = manager.get(getResourceType(resource.c_str()));
if (resource_manager) selectResource(resource_manager->load(resource), record_history);
}
bool AssetBrowser::acceptExtension(const char* ext, ResourceType type)
{
auto iter = m_registered_extensions.find(crc32(ext));
if (!iter.isValid()) return false;
return iter.value() == type;
}
bool AssetBrowser::resourceInput(const char* label, const char* str_id, char* buf, int max_size, ResourceType type)
{
ImGui::PushID(str_id);
float item_w = ImGui::CalcItemWidth();
auto& style = ImGui::GetStyle();
float text_width = Math::maximum(
50.0f, item_w - ImGui::CalcTextSize(" ... ").x - style.FramePadding.x * 2);
char* c = buf + stringLength(buf);
while (c > buf && *c != '/' && *c != '\\') --c;
if (*c == '/' || *c == '\\') ++c;
auto pos = ImGui::GetCursorPos();
pos.x += text_width;
ImGui::BeginGroup();
ImGui::AlignTextToFramePadding();
ImGui::PushTextWrapPos(pos.x);
ImGui::Text("%s", c);
ImGui::PopTextWrapPos();
ImGui::SameLine();
ImGui::SetCursorPos(pos);
if (ImGui::Button(" ... "))
{
ImGui::OpenPopup("popup");
}
ImGui::EndGroup();
if (ImGui::BeginDragDropTarget())
{
if (auto* payload = ImGui::AcceptDragDropPayload("path"))
{
char ext[10];
const char* path = (const char*)payload->Data;
PathUtils::getExtension(ext, lengthOf(ext), path);
if (acceptExtension(ext, type))
{
copyString(buf, max_size, path);
ImGui::EndDragDropTarget();
ImGui::PopID();
return true;
}
}
ImGui::EndDragDropTarget();
}
ImGui::SameLine();
ImGui::Text("%s", label);
if (ImGui::BeginResizablePopup("popup", ImVec2(300, 300)))
{
if (buf[0] != '\0' && ImGui::Button(StaticString<30>("View###go", str_id)))
{
m_is_focus_requested = true;
m_is_open = true;
m_wanted_resource = buf;
}
if (ImGui::Selectable("Empty", false))
{
*buf = '\0';
ImGui::EndPopup();
ImGui::PopID();
return true;
}
if (resourceList(buf, max_size, type, 0))
{
ImGui::EndPopup();
ImGui::PopID();
return true;
}
ImGui::EndPopup();
}
ImGui::PopID();
return false;
}
void AssetBrowser::registerExtension(const char* extension, ResourceType type)
{
u32 hash = crc32(extension);
ASSERT(!m_registered_extensions.find(hash).isValid());
m_registered_extensions.insert(hash, type);
if (!m_resources.find(type).isValid()) m_resources.insert(type, Array<Path>(m_editor.getAllocator()));
}
FS::IFile* AssetBrowser::beginSaveResource(Resource& resource)
{
FS::FileSystem& fs = m_app.getWorldEditor().getEngine().getFileSystem();
// use temporary because otherwise the resource is reloaded during saving
StaticString<MAX_PATH_LENGTH> tmp_path(resource.getPath().c_str(), ".tmp");
FS::IFile* file = fs.open(fs.getDefaultDevice(), Path(tmp_path), FS::Mode::CREATE_AND_WRITE);
if (!file)
{
g_log_error.log("Editor") << "Could not save file " << resource.getPath().c_str();
return nullptr;
}
return file;
}
void AssetBrowser::endSaveResource(Resource& resource, FS::IFile& file, bool success)
{
FS::FileSystem& fs = m_app.getWorldEditor().getEngine().getFileSystem();
fs.close(file);
if (!success) return;
auto& engine = m_app.getWorldEditor().getEngine();
StaticString<MAX_PATH_LENGTH> src_full_path;
StaticString<MAX_PATH_LENGTH> dest_full_path;
StaticString<MAX_PATH_LENGTH> tmp_path(resource.getPath().c_str(), ".tmp");
if (engine.getPatchFileDevice())
{
src_full_path << engine.getPatchFileDevice()->getBasePath() << tmp_path;
dest_full_path << engine.getPatchFileDevice()->getBasePath() << resource.getPath().c_str();
}
if (!engine.getPatchFileDevice() || !PlatformInterface::fileExists(src_full_path))
{
src_full_path.data[0] = 0;
dest_full_path.data[0] = 0;
src_full_path << engine.getDiskFileDevice()->getBasePath() << tmp_path;
dest_full_path << engine.getDiskFileDevice()->getBasePath() << resource.getPath().c_str();
}
PlatformInterface::deleteFile(dest_full_path);
if (!PlatformInterface::moveFile(src_full_path, dest_full_path))
{
g_log_error.log("Editor") << "Could not save file " << resource.getPath().c_str();
}
}
bool AssetBrowser::resourceList(char* buf, int max_size, ResourceType type, float height) const
{
IPlugin* plugin = m_plugins.get(type);
if (plugin->canCreateResource() && ImGui::Selectable("New"))
{
char path[MAX_PATH_LENGTH];
if (plugin->createResource(path, lengthOf(path)))
{
copyString(buf, max_size, path);
return true;
}
}
static char filter[128] = "";
ImGui::LabellessInputText("Filter", filter, sizeof(filter));
ImGui::BeginChild("Resources", ImVec2(0, height));
for (auto& res : getResources(type))
{
if (filter[0] != '\0' && strstr(res.c_str(), filter) == nullptr) continue;
if (ImGui::Selectable(res.c_str(), false))
{
copyString(buf, max_size, res.c_str());
ImGui::EndChild();
return true;
}
}
ImGui::EndChild();
return false;
}
void AssetBrowser::openInExternalEditor(Resource* resource) const
{
openInExternalEditor(resource->getPath().c_str());
}
void AssetBrowser::openInExternalEditor(const char* path) const
{
if (m_editor.getEngine().getPatchFileDevice())
{
StaticString<MAX_PATH_LENGTH> full_path(m_editor.getEngine().getPatchFileDevice()->getBasePath());
full_path << path;
if (PlatformInterface::fileExists(full_path))
{
PlatformInterface::shellExecuteOpen(full_path, nullptr);
return;
}
}
StaticString<MAX_PATH_LENGTH> full_path(m_editor.getEngine().getDiskFileDevice()->getBasePath());
full_path << path;
PlatformInterface::shellExecuteOpen(full_path, nullptr);
}
void AssetBrowser::goBack()
{
if (m_history_index < 1) return;
m_history_index = Math::maximum(0, m_history_index - 1);
selectResource(m_history[m_history_index], false);
}
void AssetBrowser::goForward()
{
m_history_index = Math::minimum(m_history_index + 1, m_history.size() - 1);
selectResource(m_history[m_history_index], false);
}
const Array<Path>& AssetBrowser::getResources(ResourceType type) const
{
return m_resources[type];
}
void AssetBrowser::addResource(const char* path, const char* filename)
{
char ext[10];
PathUtils::getExtension(ext, sizeof(ext), filename);
makeLowercase(ext, lengthOf(ext), ext);
char fullpath[MAX_PATH_LENGTH];
copyString(fullpath, path);
catString(fullpath, "/");
catString(fullpath, filename);
ResourceType type = getResourceType(fullpath);
if (startsWith(path, "/unit_tests") != 0) return;
if (type == INVALID_RESOURCE_TYPE) return;
Path path_obj(fullpath);
Array<Path>& resources = m_resources[type];
if (resources.indexOf(path_obj) == -1)
{
resources.push(path_obj);
}
}
void AssetBrowser::processDir(const char* dir, int base_length)
{
auto* iter = PlatformInterface::createFileIterator(dir, m_editor.getAllocator());
PlatformInterface::FileInfo info;
while (getNextFile(iter, &info))
{
if (info.filename[0] == '.') continue;
if (info.is_directory)
{
char child_path[MAX_PATH_LENGTH];
copyString(child_path, dir);
catString(child_path, "/");
catString(child_path, info.filename);
processDir(child_path, base_length);
}
else
{
addResource(dir + base_length, info.filename);
}
}
destroyFileIterator(iter);
}
void AssetBrowser::findResources()
{
for (auto& resources : m_resources)
{
resources.clear();
}
const char* base_path = m_editor.getEngine().getDiskFileDevice()->getBasePath();
processDir(base_path, stringLength(base_path));
auto* patch_device = m_editor.getEngine().getPatchFileDevice();
if (patch_device)
{
processDir(patch_device->getBasePath(), stringLength(patch_device->getBasePath()));
copyString(m_patch_base_path, patch_device->getBasePath());
}
else
{
m_patch_base_path[0] = '\0';
}
}
} // namespace Lumix