Skip to content

Instantly share code, notes, and snippets.

@OverShifted
Last active August 18, 2024 06:06
Show Gist options
  • Save OverShifted/13aec504dfe376dcc2171e8b7451c5b5 to your computer and use it in GitHub Desktop.
Save OverShifted/13aec504dfe376dcc2171e8b7451c5b5 to your computer and use it in GitHub Desktop.
#include <filesystem>
#include <imgui.h>
#define BIT(x) (1 << x)
std::pair<bool, uint32_t> DirectoryTreeViewRecursive(const std::filesystem::path& path, uint32_t* count, int* selection_mask)
{
ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth;
bool any_node_clicked = false;
uint32_t node_clicked = 0;
for (const auto& entry : std::filesystem::directory_iterator(path))
{
ImGuiTreeNodeFlags node_flags = base_flags;
const bool is_selected = (*selection_mask & BIT(*count)) != 0;
if (is_selected)
node_flags |= ImGuiTreeNodeFlags_Selected;
std::string name = entry.path().string();
auto lastSlash = name.find_last_of("/\\");
lastSlash = lastSlash == String::npos ? 0 : lastSlash + 1;
name = name.substr(lastSlash, name.size() - lastSlash);
bool entryIsFile = !std::filesystem::is_directory(entry.path());
if (entryIsFile)
node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)(*count), node_flags, name.c_str());
if (ImGui::IsItemClicked())
{
node_clicked = *count;
any_node_clicked = true;
}
(*count)--;
if (!entryIsFile)
{
if (node_open)
{
auto clickState = DirectoryTreeViewRecursive(entry.path(), count, selection_mask);
if (!any_node_clicked)
{
any_node_clicked = clickState.first;
node_clicked = clickState.second;
}
ImGui::TreePop();
}
else
{
for (const auto& e : std::filesystem::recursive_directory_iterator(entry.path()))
(*count)--;
}
}
}
return { any_node_clicked, node_clicked };
}
void OnImGui(std::string directoryPath)
{
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0.0f, 0.0f });
ImGui::Begin("Assets");
if (ImGui::CollapsingHeader("Assets"))
{
uint32_t count = 0;
for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath))
count++;
static int selection_mask = 0;
auto clickState = DirectoryTreeViewRecursive(directoryPath, &count, &selection_mask);
if (clickState.first)
{
// Update selection state
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= BIT(clickState.second); // CTRL+click to toggle
else //if (!(selection_mask & (1 << clickState.second))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = BIT(clickState.second); // Click to single-select
}
}
ImGui::End();
ImGui::PopStyleVar();
}
@OverShifted
Copy link
Author

@Stanlyhalo We can also just move the blocking IO to a separate thread. As I think that's what most editors do, Assuming how complicated maintaining a valid cache of the filesystem can be. (Not to mention most OSes do that built-in)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment