How to mix ImGui::TreeNode and filesystem to print the current directory recursively


#1

Hi, I’ve tried several things to display my current directory recursively, but I can not understand how to use ImGui::TreeNode in a loop like this:

ImGui::Begin("Project", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
            for (shiva::fs::recursive_directory_iterator it(shiva::fs::current_path());
                 it != shiva::fs::recursive_directory_iterator();
                 ++it) {
                if (shiva::fs::is_directory(it->path())) {
                    for (int i = 0; i < it.depth(); ++i) {
                        ImGui::Indent();
                    }
                    if (ImGui::TreeNode(it->path().filename().string().c_str())) {
                    }
                    for (int i = 0; i < it.depth(); ++i) {
                        ImGui::Unindent();
                    }
                } else {
                    for (int i = 0; i < it.depth(); ++i) {
                        ImGui::Indent();
                    }
                    ImGui::Text(it->path().filename().string().c_str());
                    for (int i = 0; i < it.depth(); ++i) {
                        ImGui::Unindent();
                    }
                }
            }
            ImGui::End();

The recursive_loop is from -> https://en.cppreference.com/w/cpp/filesystem/recursive_directory_iterator

Obviously it does not work at all

I Got something like the image below

For example, I would like the systems folder to be hidden until I open the Release folder knowing that systems is a release subfolder

I would like something like the working tree that can be seen on the left of the image

PS: we can only post one image

Any advice, or example are apreciated ! Thank’s a lot


#2

Update 3.

 ImGui::SetNextWindowPos(ImVec2{0, 0 + height});
            ImGui::SetNextWindowSizeConstraints(ImVec2{win_cfg_.size[0] * 0.1f,
                                                       win_cfg_.size[1] * 0.6f},
                                                ImVec2{win_cfg_.size[0] * 0.7f,
                                                       win_cfg_.size[1] * 0.8f});
            ImGui::Begin("Project", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
            static int selection_mask = (1 << 2);
            int node_clicked = -1;
            ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize());
            std::function<void(const shiva::fs::path &, unsigned int, unsigned int &)> functor = [&](
                const shiva::fs::path &path,
                unsigned int depth, unsigned int &idx) {
                for (auto &&p : shiva::fs::directory_iterator(path)) {
                    ImGuiTreeNodeFlags node_flags =
                        ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick |
                        ((selection_mask & (1 << idx)) ? ImGuiTreeNodeFlags_Selected : 0);
                    if (shiva::fs::is_directory(p.path())) {
                        using namespace std::string_literals;
                        std::string s = ICON_FA_FOLDER + " "s + p.path().filename().string().c_str();
                        if (ImGui::TreeNodeEx((void *)(intptr_t)idx, node_flags, "%s", s.c_str())) {
                            if (ImGui::IsItemClicked() || ImGui::IsItemFocused())
                                node_clicked = idx;
                            functor(p, depth + 1, ++idx);
                            ImGui::TreePop();
                        }
                    } else {
                        node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
                        if (depth > 0) {
                            ImGui::Indent();
                        }
                        ImGui::TreeNodeEx((void *)(intptr_t)idx, node_flags, "%s",
                                          p.path().filename().string().c_str());
                        if (ImGui::IsItemClicked() || ImGui::IsItemFocused())
                            node_clicked = idx;
                        if (depth > 0) {
                            ImGui::Unindent();
                        }
                    }
                    ++idx;
                }
                depth -= 1;
            };
            unsigned int idx = 0u;
            functor(shiva::fs::current_path(), 0u, idx);
            if (node_clicked != -1) {
                // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame.
                if (ImGui::GetIO().KeyCtrl)
                    selection_mask ^= (1 << node_clicked);          // CTRL+click to toggle
                else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection
                    selection_mask = (1 << node_clicked);           // Click to single-select
            }
            ImGui::PopStyleVar();
            ImGui::End();

Which give me this, it’s not too bad but i’m not sure it’s great


#3

It’s not an easy problem. I think your V3 looks alright, you may just remove the Folder icon?
Not sure why you have the Indent/Unindent block.
TreeNodeEx() probably could be improved to make that sort of things easier.

If you want to maintain a full selection you may need to carry e.g. a std::set keyed by the hash of the filename, so every selected folder/file in the folder has an entry in the std::set.