From 6aca12184fbab7dd3e6b40ba6e144209f603365e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 21 Sep 2025 05:48:30 -0500 Subject: [PATCH] new file picker, part 1 prototype --- CMakeLists.txt | 1 + src/gui/gui.cpp | 6 ++ src/gui/gui.h | 2 + src/gui/newFilePicker.cpp | 182 +++++++++++++++++++++++++++++++++++++- src/gui/newFilePicker.h | 20 +++++ 5 files changed, 208 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b78d49ae9..1b22aea39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -894,6 +894,7 @@ src/gui/image.cpp src/gui/debug.cpp src/gui/fileDialog.cpp +src/gui/newFilePicker.cpp src/gui/intConst.cpp src/gui/guiConst.cpp diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f3fff73c8..1a3186e2e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4987,6 +4987,8 @@ bool FurnaceGUI::loop() { MEASURE(log,drawLog()); MEASURE(effectList,drawEffectList()); MEASURE(userPresets,drawUserPresets()); + + newFilePicker->draw(); } // release selection if mouse released @@ -7867,6 +7869,9 @@ bool FurnaceGUI::init() { userEvents=SDL_RegisterEvents(1); + newFilePicker=new FurnaceFilePicker; + newFilePicker->open("New File Picker","/home",false); + e->setMidiCallback([this](const TAMidiMessage& msg) -> int { if (introPos<11.0) return -3; midiLock.lock(); @@ -8511,6 +8516,7 @@ FurnaceGUI::FurnaceGUI(): postWarnAction(GUI_WARN_GENERIC), mobScene(GUI_SCENE_PATTERN), fileDialog(NULL), + newFilePicker(NULL), scrW(GUI_WIDTH_DEFAULT), scrH(GUI_HEIGHT_DEFAULT), scrConfW(GUI_WIDTH_DEFAULT), diff --git a/src/gui/gui.h b/src/gui/gui.h index cdcc132bf..8ee8b9e70 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -38,6 +38,7 @@ #include "../pch.h" #include "fileDialog.h" +#include "newFilePicker.h" #define FURNACE_APP_ID "org.tildearrow.furnace" @@ -1765,6 +1766,7 @@ class FurnaceGUI { FurnaceGUIMobileScenes mobScene; FurnaceGUIFileDialog* fileDialog; + FurnaceFilePicker* newFilePicker; int scrW, scrH, scrConfW, scrConfH, canvasW, canvasH; int scrX, scrY, scrConfX, scrConfY; diff --git a/src/gui/newFilePicker.cpp b/src/gui/newFilePicker.cpp index 129352450..93ece73be 100644 --- a/src/gui/newFilePicker.cpp +++ b/src/gui/newFilePicker.cpp @@ -17,7 +17,183 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - // this is the code to a new file picker using Dear ImGui. - // this will eventually replace ImGuiFileDialog as the built-in file picker. +// this is the code to a new file picker using Dear ImGui. +// this will eventually replace ImGuiFileDialog as the built-in file picker. - \ No newline at end of file +#include "newFilePicker.h" +#include "imgui.h" +#include +#include + +static void _fileThread(void* item) { + ((FurnaceFilePicker*)item)->readDirectorySub(); +} + +void FurnaceFilePicker::readDirectorySub() { + /// STAGE 1: get file list + DIR* dir=opendir(path.c_str()); + + if (dir==NULL) { + failMessage=strerror(errno); + haveFiles=true; + haveStat=true; + return; + } + + struct dirent* entry=NULL; + while (true) { + entry=readdir(dir); + if (entry==NULL) break; + if (strcmp(entry->d_name,".")==0) continue; + if (strcmp(entry->d_name,"..")==0) continue; + + FileEntry* newEntry=new FileEntry; + newEntry->name=entry->d_name; + switch (entry->d_type) { + case DT_REG: + newEntry->type=FP_TYPE_NORMAL; + break; + case DT_DIR: + newEntry->type=FP_TYPE_DIR; + break; + case DT_LNK: + newEntry->type=FP_TYPE_LINK; + break; + case DT_SOCK: + newEntry->type=FP_TYPE_SOCKET; + break; + default: + newEntry->type=FP_TYPE_UNKNOWN; + break; + } + + entries.push_back(newEntry); + } + if (closedir(dir)!=0) { + // ?! + } + + // we're done - this is sufficient to show a file list (and sort by name) + haveFiles=true; + + /// STAGE 2: retrieve file information + + haveStat=true; +} + +void FurnaceFilePicker::readDirectory(String path) { + if (fileThread!=NULL) { + // stop current file thread + stopReading=true; + fileThread->join(); + delete fileThread; + fileThread=NULL; + } + + // clear all entries + sortedEntries.clear(); + for (FileEntry* i: entries) { + delete i; + } + entries.clear(); + + // start new file thread + this->path=path; + failMessage=""; + haveFiles=false; + haveStat=false; + stopReading=false; + fileThread=new std::thread(_fileThread,this); +} + +bool FurnaceFilePicker::draw() { + if (!isOpen) return false; + + String newDir; + + 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_CHEVRON_UP "##ParentDir")) { + size_t pos=path.rfind('/'); + if (pos!=String::npos && path!="/") { + newDir=path.substr(0,pos); + if (newDir.empty()) newDir="/"; + } + } + ImGui::SameLine(); + if (!haveFiles) { + ImGui::Text("Loading... (%s)",path.c_str()); + } else { + if (haveStat) { + ImGui::Text("Hiya! (%s)",path.c_str()); + } else { + ImGui::Text("Loading... (%s)",path.c_str()); + } + + if (entries.empty()) { + if (failMessage.empty()) { + ImGui::Text("This directory is empty!"); + } else { + ImGui::Text("Could not load this directory!\n(%s)",failMessage.c_str()); + } + } else { + if (ImGui::BeginTable("FileList",3,ImGuiTableFlags_Borders)) { + entryLock.lock(); + int index=0; + for (FileEntry* i: entries) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (i->type==FP_TYPE_DIR) { + ImGui::PushStyleColor(ImGuiCol_Text,0xff00ffff); + } + ImGui::PushID(index++); + if (ImGui::Selectable("##File",false)) { + if (i->type==FP_TYPE_DIR || i->type==FP_TYPE_LINK) { + if (*path.rbegin()=='/') { + newDir=path+i->name; + } else { + newDir=path+'/'+i->name; + } + } + } + ImGui::PopID(); + ImGui::SameLine(); + + ImGui::TextUnformatted(i->name.c_str()); + + if (i->type==FP_TYPE_DIR) { + ImGui::PopStyleColor(); + } + } + ImGui::EndTable(); + entryLock.unlock(); + } + } + } + } + ImGui::End(); + + if (!newDir.empty()) { + // change directory + readDirectory(newDir); + } + return false; +} + +bool FurnaceFilePicker::open(String name, String path, bool modal) { + if (isOpen) return false; + + readDirectory(path); + windowName=name; + isOpen=true; + return true; +} + +FurnaceFilePicker::FurnaceFilePicker(): + fileThread(NULL), + haveFiles(false), + haveStat(false), + stopReading(false), + isOpen(false) { + +} \ No newline at end of file diff --git a/src/gui/newFilePicker.h b/src/gui/newFilePicker.h index 319baf99a..ad9285d3a 100644 --- a/src/gui/newFilePicker.h +++ b/src/gui/newFilePicker.h @@ -18,20 +18,40 @@ */ #include "../ta-utils.h" +#include class FurnaceFilePicker { + enum FileType { + FP_TYPE_UNKNOWN=0, + FP_TYPE_NORMAL, + FP_TYPE_DIR, + FP_TYPE_LINK, + FP_TYPE_PIPE, + FP_TYPE_SOCKET, + }; struct FileEntry { String path; String name; + String ext; uint64_t size; struct tm time; + FileType type; }; std::vector entries; std::vector sortedEntries; + std::thread* fileThread; std::mutex entryLock; + String windowName; String path; + String failMessage; + bool haveFiles, haveStat, stopReading, isOpen; + + void clearAllFiles(); + void readDirectory(String path); public: + void readDirectorySub(); bool draw(); bool open(String name, String path, bool modal); + FurnaceFilePicker(); };