CoolFace
Datasetpublic

dlxjj/imradv3

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes262downloads
ui_explorer.cpp468 linesDownload Raw Back to src
1#include "ui_explorer.h"2#include "ui_message_box.h"3#include "utils.h"4#include "stx.h"5#include "imrad.h"6#include "cppgen.h"7#include <sstream>8#include <chrono>9#include <IconsFontAwesome6.h>10 11struct ExplorerEntry12{13    std::string path;14    bool folder;15    bool generated;16    std::string fileName;17    std::time_t last_write_time;18    std::string modified;19};20 21std::string explorerPath;22int explorerFilter = 0;23ImGuiTableColumnSortSpecs explorerSorting;24 25static int pathSel = -1;26static std::vector<ExplorerEntry> data;27static std::vector<std::string> suggestions;28static int suggestionSel = -1;29static std::string lastPath;30static bool scrollBack = false;31static bool showSuggestions = false;32static bool autocompleted = false;33enum MoveFocus { None, PathInput, FirstEntry };34static MoveFocus moveFocus = None;35 36bool IsHeaderFile(const fs::path& p)37{38    auto ext = u8string(p.extension());39    return ext == ".h" || ext == ".hpp" || ext == ".hxx";40}41 42bool IsJsonFile(const fs::path& p)43{44    auto ext = u8string(p.extension());45    return ext == ".json" || ext == ".JSON";46}47 48bool IsImageFile(const fs::path& p)49{50    auto ext = u8string(p.extension());51    return ext == ".jpg" || ext == ".JPG" || ext == ".jpeg" || ext == ".JPEG" || ext == ".png" || ext == ".PNG" || ext == ".bmp" || ext == ".BMP";52}53 54bool IsCppFile(const fs::path& p)55{56    auto ext = u8string(p.extension());57    return ext == ".c" || ext == ".cpp" || ext == ".cxx";58}59 60void ReloadExplorer(const CppGen& codeGen)61{62    data.clear();63    auto path = u8path(explorerPath);64    //+= '/' so it works with paths like "c:"65    if (!path.has_root_directory())66        path = u8path(explorerPath + (char)fs::path::preferred_separator);67    path.make_preferred();68    explorerPath = u8string(path);69 70    if (!fs::is_directory(path)) {71        messageBox.title = "Error";72        messageBox.message = "Can't open \"" + explorerPath + "\"";73        messageBox.buttons = ImRad::Ok;74        messageBox.OpenPopup();75        return;76    }77 78    scrollBack = true;79    std::ostringstream os;80    auto fsnow = fs::file_time_type::clock::now();81    auto snow = std::chrono::system_clock::now();82    os.imbue(std::locale(""));83    std::error_code ec;84    for (fs::directory_iterator it(path, ec); it != fs::directory_iterator(); ++it)85    {86        if (u8string(it->path().stem())[0] == '.')87            continue;88        if (!it->is_directory() &&89            //(!explorerFilter && !IsHeaderFile(it->path())))90            (!explorerFilter && !IsImageFile(it->path())))91            continue;92        ExplorerEntry entry;93        entry.path = u8string(it->path());94        entry.folder = it->is_directory();95        entry.generated = (IsHeaderFile(it->path()) || IsCppFile(it->path())) &&96            codeGen.ReadGenVersion(entry.path);97        entry.fileName = u8string(it->path().filename());98        auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(99            it->last_write_time() - fsnow + snow);100        entry.last_write_time = std::chrono::system_clock::to_time_t(sctp);101        auto* tm = std::localtime(&entry.last_write_time);102        os.str("");103        os << std::put_time(tm, "%x %X");104        entry.modified = os.str();105        data.push_back(std::move(entry));106    }107    stx::sort(data, [](const ExplorerEntry& a, const ExplorerEntry& b) {108        if (a.folder != b.folder)109            return a.folder;110        if (!explorerSorting.ColumnIndex) {111            if (explorerSorting.SortDirection == ImGuiSortDirection_Ascending)112                return path_cmp(a.fileName, b.fileName);113            else114                return path_cmp(b.fileName, a.fileName);115        }116        else {117            if (explorerSorting.SortDirection == ImGuiSortDirection_Ascending)118                return a.last_write_time < b.last_write_time;119            else120                return a.last_write_time > b.last_write_time;121        }122        });123    if (!u8path(explorerPath).relative_path().empty()) {124        std::string ppath = u8string(u8path(explorerPath).parent_path());125        ExplorerEntry e;126        e.path = ppath;127        e.folder = true;128        e.fileName = "..";129        data.insert(data.begin(), e);130    }131}132 133void GetSuggestions()134{135    suggestions.clear();136    std::error_code ec;137    auto path = u8path(explorerPath);138    //+= '/' so it works with paths like "c:"139    if (!path.has_root_directory())140        path = u8path(explorerPath + (char)fs::path::preferred_separator);141    std::string match = u8string(path.stem());142    for (fs::directory_iterator it(path.parent_path(), ec); it != fs::directory_iterator(); ++it)143    {144        std::string stem = u8string(it->path().stem());145        if (!it->is_directory() || stem[0] == '.')146            continue;147        if (stem.size() > match.size())148            stem.resize(match.size());149        if (path_cmp(stem, match) || path_cmp(match, stem))150            continue;151        std::string fileName = u8string(it->path().filename());152        suggestions.push_back(std::move(fileName));153    }154    stx::sort(suggestions, [](const std::string& a, const std::string& b) {155        return path_cmp(a, b);156        });157}158 159int ExplorerPathCallback(ImGuiInputTextCallbackData* data)160{161    if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) {162        return DefaultCharFilter(data);163    }164    else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)165    {166        if (data->EventKey == ImGuiKey_UpArrow && suggestions.size()) {167            if (suggestionSel < 0) {168                suggestionSel = (int)suggestions.size() - 1;169                lastPath = explorerPath;170            }171            else if (--suggestionSel < 0)172                suggestionSel = -1;173        }174        else if (data->EventKey == ImGuiKey_DownArrow && suggestions.size()) {175            if (suggestionSel < 0) {176                suggestionSel = 0;177                lastPath = explorerPath;178            }179            else if (++suggestionSel == suggestions.size())180                suggestionSel = -1;181        }182 183        //commit suggestion184        std::string str = lastPath;185        if (suggestionSel >= 0) {186            size_t i = explorerPath.find_last_of("/\\");187            if (i == std::string::npos)188                str = explorerPath + (char)fs::path::preferred_separator;189            else190                str = explorerPath.substr(0, i + 1);191            str += suggestions[suggestionSel];192        }193        if (str.size() + 1 < data->BufSize) { //no reallocation occured194            autocompleted = true;195            data->BufDirty = true;196            strcpy(data->Buf, str.c_str());197            data->BufTextLen = (int)str.size();198            data->SelectionStart = data->SelectionEnd = data->CursorPos = data->BufTextLen;199        }200    }201    return 0;202}203 204void ExplorerUI(const CppGen& codeGen, std::function<void(const std::string& fpath)> openFileFunc)205{206    explorerPath.reserve(1024); //ImGuiInputText_HistoryCallback doesn't allow buffer reallocation??207    if (explorerPath == "")208        explorerPath = u8string(fs::current_path());209 210    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 4, 4 });211    ImGui::Begin("Explorer", 0, ImGuiWindowFlags_NoCollapse);212    if (ImGui::IsWindowAppearing()) {213        ReloadExplorer(codeGen);214        showSuggestions = false;215        autocompleted = false;216        moveFocus = FirstEntry;217    }218 219    //clickable path box220    const int sp = 4;221    const std::string ELIDE_STR = "...";222    std::string inputId = "##explorerCwd";223    if (ImGui::GetFocusID() != ImGui::GetID(inputId.c_str()))224    {225        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImGui::GetStyle().FramePadding);226        ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, ImGui::GetStyle().FrameRounding);227        ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xffd0d0d0);228        ImGui::PushStyleColor(ImGuiCol_Border, 0xffb0b0b0);229        if (ImGui::BeginChild("cwdChild", { -ImGui::GetFrameHeight() - sp, ImGui::GetFrameHeight() }, ImGuiChildFlags_Borders, ImGuiWindowFlags_NavFlattened | ImGuiWindowFlags_NoScrollbar))230        {231            fs::path path = u8path(explorerPath);232            char separator = (char)fs::path::preferred_separator;233            int elideStart = (int)path.has_root_name() + (int)path.has_root_directory();234            int elideN = 0;235            ImVec2 wpad = ImGui::GetStyle().WindowPadding;236            int n = 0;237            for (const auto& p : path)238                ++n;239            for (; elideStart + elideN < n; ++elideN) {240                std::string tmp;241                int i = 0;242                for (const auto& p : path) {243                    if (i < elideStart)244                        tmp += u8string(p);245                    else if (i == elideStart && elideN)246                        tmp += ELIDE_STR + separator;247                    else if (i >= elideStart + elideN)248                        tmp += u8string(p) + separator;249                    ++i;250                }251                if (tmp.back() == separator)252                    tmp.pop_back();253                if (elideStart + elideN + 1 == n ||254                    ImGui::CalcTextSize(tmp.c_str()).x + 2 * wpad.x < ImGui::GetWindowWidth())255                    break;256            }257            int i = 0;258            int rootDirIdx = path.has_root_directory() ? (int)path.has_root_name() : -1;259            int lastPathSel = pathSel;260            pathSel = -1;261            bool sepSel = false;262            fs::path subpath;263            for (const auto& p : path) {264                subpath /= p;265                if (i >= elideStart && i < elideStart + elideN) {266                    ++i;267                    continue;268                }269                if (p.has_root_directory()) {270                    ImGui::SameLine(0, 0);271                    ImGui::Text("%s", u8string(p).c_str());272                    if (ImGui::IsItemHovered())273                        sepSel = true;274                    ++i;275                    continue;276                }277                if (i && i != rootDirIdx + 1) {278                    ImGui::SameLine(0, 0);279                    std::string sep = elideN && i == elideStart + elideN ? ELIDE_STR : "";280                    sep += separator;281                    ImGui::Text("%s", sep.c_str());282                    if (ImGui::IsItemHovered())283                        sepSel = true;284                }285                ImGui::SameLine(0, 0);286                ImGui::PushStyleColor(ImGuiCol_Text, i == lastPathSel ? 0xff0000ff : 0xff000000);287                ImGui::Text("%s", u8string(p).c_str());288                ImGui::PopStyleColor();289                if (ImGui::IsItemHovered() && i + 1 != n)290                    pathSel = i;291                if (ImGui::IsItemClicked() && i + 1 != n) {292                    explorerPath = u8string(subpath);293                    ReloadExplorer(codeGen);294                    moveFocus = FirstEntry;295                }296                ++i;297            }298            if (ImGui::IsWindowHovered() && !sepSel && pathSel < 0)299            {300                ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);301                if (ImGui::IsMouseClicked(ImGuiMouseButton_Left))302                    moveFocus = PathInput;303            }304        }305        ImGui::EndChild();306        ImGui::PopStyleVar(2);307        ImGui::PopStyleColor(2);308    }309 310    ImGui::SameLine(0, 0);311    if (ImGui::GetContentRegionAvail().x - ImGui::GetFrameHeight() - sp <= 0)312        ImGui::SetNextItemWidth(0);313    else314        ImGui::SetNextItemWidth(-ImGui::GetFrameHeight() - sp);315    //ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xffd0d0d0);316    //ImGui::PushStyleColor(ImGuiCol_Border, 0xffb0b0b0);317    ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);318    int fl = ImGuiInputTextFlags_ElideLeft | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCharFilter;319    if (ImGui::InputText(inputId.c_str(), &explorerPath, fl, ExplorerPathCallback)) {320        if (autocompleted)321            autocompleted = false;322        else {323            GetSuggestions();324            showSuggestions = true;325            suggestionSel = -1;326        }327    }328    //ImGui::PopStyleColor(2);329    ImGui::PopStyleVar();330    if (moveFocus == PathInput)331    {332        ImGui::SetKeyboardFocusHere(-1);333        auto* state = ImGui::GetInputTextState(ImGui::GetItemID());334        if (state)335            state->ClearSelection();336        if (ImGui::IsItemFocused())337            moveFocus = None;338    }339    if (ImGui::IsItemDeactivated()) { //AfterEdit())340        ReloadExplorer(codeGen);341        showSuggestions = false;342        moveFocus = FirstEntry;343    }344    if (showSuggestions)345    {346        ImGui::PushFont(ImRad::GetFontByName("imrad.explorer"));347        ImGui::SetNextWindowPos({ ImGui::GetItemRectMin().x, ImGui::GetItemRectMax().y });348        float h = ImGui::GetTextLineHeight() + ImGui::GetStyle().ItemInnerSpacing.y;349        float pad = ImGui::GetStyle().WindowPadding.y;350        const int N = 7;351        ImGui::SetNextWindowSize({ ImGui::GetItemRectSize().x, std::min(N, (int)suggestions.size()) * h + 2 * pad });352        float selY = std::max(suggestionSel, 0) * h + pad;353        if (ImGui::BeginTooltip()) //Tooltip won't steal the focus as BeginPopup does354        {355            if (selY + h > ImGui::GetScrollY() + ImGui::GetWindowSize().y)356                ImGui::SetScrollY(selY - (N - 1) * h - pad);357            if (selY < ImGui::GetScrollY())358                ImGui::SetScrollY(selY - pad);359            for (size_t i = 0; i < suggestions.size(); ++i) {360                /*ImGui::PushStyleColor(ImGuiCol_Text, 0xff40b0b0);361                ImGui::Selectable(ICON_FA_FOLDER, i == suggestionSel, ImGuiSelectableFlags_SpanAvailWidth);362                ImGui::PopStyleColor();363                ImGui::SameLine(0, 4);*/364                ImGui::Selectable(suggestions[i].c_str(), i == suggestionSel, 0);365            }366            ImGui::EndTooltip();367        }368        ImGui::PopFont();369    }370 371    ImGui::SameLine(0, sp);372    ImGui::PushStyleColor(ImGuiCol_Text, 0xff404040);373    ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true);374    if (ImGui::Button(ICON_FA_ROTATE_RIGHT "##ego")) {375        ReloadExplorer(codeGen);376        moveFocus = FirstEntry;377    }378    ImGui::PopItemFlag();379    ImGui::PopStyleColor();380 381    ImGui::PushStyleColor(ImGuiCol_TableRowBg, 0xffffffff);382    ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, 0xffffffff);383    if (scrollBack) {384        scrollBack = false;385        ImGui::SetNextWindowScroll({ 0, 0 });386    }387    float h = -ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.y;388    if (ImGui::BeginTable("files", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY, { -1, h }))389    {390        ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);391        ImGui::TableSetupColumn("Modified", ImGuiTableColumnFlags_WidthFixed, 140);392        ImGui::TableSetupScrollFreeze(0, 1);393        auto* spec = ImGui::TableGetSortSpecs();394        if (spec && spec->SpecsDirty) {395            spec->SpecsDirty = false;396            explorerSorting = *spec->Specs;397            ReloadExplorer(codeGen);398        }399        ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true);400        ImGui::TableHeadersRow();401        ImGui::PopItemFlag();402        ImGui::TableNextRow();403        ImGui::TableSetColumnIndex(0);404 405        ImGui::PushFont(ImRad::GetFontByName("imrad.explorer"));406        int n = 0;407        for (const auto& entry : data)408        {409            ImGui::PushID(n++);410            bool source = IsHeaderFile(entry.path) || IsCppFile(entry.path);411            ImGui::PushStyleColor(ImGuiCol_Text,412                entry.folder ? 0xff40b0b0 :413                entry.generated ? 0xffe0a060 :414                source ? 0xff60a0d0 :415                0xffa0a0a0416            );417            ImGui::Selectable(entry.folder ? ICON_FA_FOLDER :418                entry.generated ? ICON_FA_DICE_D6 :419                source ? ICON_FA_FILE_CODE :420                ICON_FA_FILE,421                false, ImGuiSelectableFlags_SpanAllColumns);422            ImGui::PopStyleColor();423            if (n == 1 && moveFocus == FirstEntry) {424                if (ImGui::IsItemFocused())425                    moveFocus = None;426                ImGui::SetKeyboardFocusHere(-1);427            }428            if (ImRad::IsItemDoubleClicked() ||429                (ImGui::IsItemActive() && ImGui::IsKeyPressed(ImGuiKey_Enter)))430            {431                if (entry.folder) {432                    explorerPath = entry.path;433                    ReloadExplorer(codeGen);434                    ImGui::PopID();435                    break;436                }437                //else if (!codeGen.ReadGenVersion(entry.path)) {438                //    ShellExec(entry.path);439                //}440                else {441                    openFileFunc(entry.path);442                }443            }444            ImGui::PushItemFlag(ImGuiItemFlags_NoNav, true);445            ImGui::SameLine(0, 4);446            ImGui::Selectable(entry.fileName.c_str(), false);447            ImGui::TableNextColumn();448            ImGui::Selectable(entry.modified.c_str(), false, ImGuiSelectableFlags_Disabled);449            ImGui::TableNextColumn();450            ImGui::PopItemFlag();451            ImGui::PopID();452        }453        ImGui::PopFont();454        ImGui::EndTable();455    }456    ImGui::PopStyleColor(2);457 458    ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xffd0d0d0);459    ImGui::PushStyleColor(ImGuiCol_Border, 0xffb0b0b0);460    ImGui::SetNextItemWidth(-1);461    if (ImGui::Combo("##filter", &explorerFilter, "IMAGE Files (*.jpg,*.png,*.bmp)\0All Files (*.*)\0"))462        ReloadExplorer(codeGen);463    ImGui::PopStyleColor(2);464 465    ImGui::End();466    ImGui::PopStyleVar();467}468