new file picker, part 1

prototype
This commit is contained in:
tildearrow 2025-09-21 05:48:30 -05:00
parent 134cdde1e5
commit 6aca12184f
5 changed files with 208 additions and 3 deletions

View file

@ -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

View file

@ -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),

View file

@ -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;

View file

@ -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.
#include "newFilePicker.h"
#include "imgui.h"
#include <IconsFontAwesome4.h>
#include <dirent.h>
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) {
}

View file

@ -18,20 +18,40 @@
*/
#include "../ta-utils.h"
#include <thread>
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<FileEntry*> entries;
std::vector<FileEntry*> 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();
};