From aa713ed9c9762d5934ce83f5ba4d2b2bfa73b9b7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 23 Sep 2025 01:31:49 -0500 Subject: [PATCH] commit message? why? who cares --- src/gui/gui.cpp | 1 + src/gui/newFilePicker.cpp | 125 +++++++++++++++++++++++++++++++++++--- src/gui/newFilePicker.h | 26 +++++++- 3 files changed, 140 insertions(+), 12 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 12afd63a6..7a367dd75 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -7872,6 +7872,7 @@ bool FurnaceGUI::init() { userEvents=SDL_RegisterEvents(1); newFilePicker=new FurnaceFilePicker; + newFilePicker->setHomeDir(getHomeDir()); newFilePicker->open("New File Picker","/home",false); e->setMidiCallback([this](const TAMidiMessage& msg) -> int { diff --git a/src/gui/newFilePicker.cpp b/src/gui/newFilePicker.cpp index b7b0faeb5..b03f8841d 100644 --- a/src/gui/newFilePicker.cpp +++ b/src/gui/newFilePicker.cpp @@ -21,7 +21,8 @@ // this will eventually replace ImGuiFileDialog as the built-in file picker. #include "newFilePicker.h" -#include +#include "IconsFontAwesome4.h" +#include "misc/cpp/imgui_stdlib.h" #include #include #include @@ -57,9 +58,11 @@ void FurnaceFilePicker::readDirectorySub() { break; case DT_DIR: newEntry->type=FP_TYPE_DIR; + newEntry->isDir=true; break; case DT_LNK: newEntry->type=FP_TYPE_LINK; + // TODO: resolve link break; case DT_SOCK: newEntry->type=FP_TYPE_SOCKET; @@ -134,9 +137,61 @@ void FurnaceFilePicker::readDirectory(String path) { haveFiles=false; haveStat=false; stopReading=false; + scheduledSort=1; fileThread=new std::thread(_fileThread,this); } +void FurnaceFilePicker::setHomeDir(String where) { + homeDir=where; +} + +void FurnaceFilePicker::sortFiles() { + entryLock.lock(); + sortedEntries=entries; + entryLock.unlock(); + + // sort by name + std::sort(sortedEntries.begin(),sortedEntries.end(),[](const FileEntry* a, const FileEntry* b) -> bool { + if (a->isDir && !b->isDir) return true; + if (!a->isDir && b->isDir) return false; + + String aLower=a->name; + for (char& i: aLower) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + String bLower=b->name; + for (char& i: bLower) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + return aLower='A' && i<='Z') i+='a'-'A'; + } + + for (FileEntry* i: sortedEntries) { + String lowerName=i->name; + for (char& j: lowerName) { + if (j>='A' && j<='Z') j+='a'-'A'; + } + + if (lowerName.find(lowerFilter)!=String::npos) { + filteredEntries.push_back(i); + } + } +} + bool FurnaceFilePicker::draw() { if (!isOpen) return false; @@ -144,6 +199,10 @@ bool FurnaceFilePicker::draw() { ImGui::SetNextWindowSizeConstraints(ImVec2(800.0,600.0),ImVec2(8000.0,6000.0)); if (ImGui::Begin(windowName.c_str(),NULL,ImGuiWindowFlags_NoSavedSettings)) { + if (ImGui::Button(ICON_FA_HOME "##HomeDir")) { + newDir=homeDir; + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_CHEVRON_UP "##ParentDir")) { size_t pos=path.rfind('/'); if (pos!=String::npos && path!="/") { @@ -160,21 +219,47 @@ bool FurnaceFilePicker::draw() { } else { ImGui::Text("Loading... (%s)",path.c_str()); } + if (ImGui::Button(ICON_FA_REPEAT "##ClearFilter")) { + filter=""; + filterFiles(); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputTextWithHint("##Filter","Search",&filter)) { + filterFiles(); + } - if (entries.empty()) { - if (failMessage.empty()) { - ImGui::Text("This directory is empty!"); + if (scheduledSort) { + scheduledSort=0; + sortFiles(); + filterFiles(); + } + + ImVec2 tableSize=ImGui::GetContentRegionAvail(); + tableSize.y-=ImGui::GetFrameHeightWithSpacing()*2.0f; + + if (filteredEntries.empty()) { + if (sortedEntries.empty()) { + if (failMessage.empty()) { + ImGui::Text("This directory is empty!"); + } else { + ImGui::Text("Could not load this directory!\n(%s)",failMessage.c_str()); + } } else { - ImGui::Text("Could not load this directory!\n(%s)",failMessage.c_str()); + if (failMessage.empty()) { + ImGui::Text("No results"); + } else { + ImGui::Text("Could not load this directory!\n(%s)",failMessage.c_str()); + } } } else { - if (ImGui::BeginTable("FileList",3,ImGuiTableFlags_Borders|ImGuiTableFlags_ScrollY)) { + if (ImGui::BeginTable("FileList",3,ImGuiTableFlags_Borders|ImGuiTableFlags_ScrollY,tableSize)) { entryLock.lock(); int index=0; - listClipper.Begin(entries.size()); + listClipper.Begin(filteredEntries.size()); while (listClipper.Step()) { for (int _i=listClipper.DisplayStart; _i #include #include "imgui.h" +enum FilePickerStatus { + FP_STATUS_WAITING=0, + FP_STATUS_ACCEPTED, + FP_STATUS_CLOSED +}; + class FurnaceFilePicker { enum FileType { FP_TYPE_UNKNOWN=0, @@ -35,30 +42,43 @@ class FurnaceFilePicker { String path; String name; String ext; - bool hasSize, hasTime; + bool hasSize, hasTime, isDir; uint64_t size; struct tm time; FileType type; FileEntry(): - hasSize(false), hasTime(false), + hasSize(false), hasTime(false), isDir(false), size(0), type(FP_TYPE_UNKNOWN) {} }; std::vector entries; std::vector sortedEntries; + std::vector filteredEntries; + std::vector chosenEntries; std::thread* fileThread; std::mutex entryLock; String windowName; - String path; + String path, filter; String failMessage; + String homeDir; + String entryName; ImGuiListClipper listClipper; bool haveFiles, haveStat, stopReading, isOpen; + int scheduledSort; + FilePickerStatus curStatus; + void sortFiles(); + void filterFiles(); void clearAllFiles(); void readDirectory(String path); public: void readDirectorySub(); + void setHomeDir(String where); + FilePickerStatus getStatus(); + const std::vector& getSelected(); bool draw(); bool open(String name, String path, bool modal); + void loadSettings(DivConfig& conf); + void saveSettings(DivConfig& conf); FurnaceFilePicker(); };