From c321277bc961c8803499ccec78a61779768f86e1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Sep 2025 19:40:42 -0500 Subject: [PATCH] colors, icons, types and more next up: mkdir --- src/gui/gui.cpp | 7 +- src/gui/newFilePicker.cpp | 217 +++++++++++++++++++++++++++++++------- src/gui/newFilePicker.h | 34 ++++-- src/gui/settings.cpp | 56 ++++++++++ 4 files changed, 269 insertions(+), 45 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 1354aa394..4ad31179d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -7770,7 +7770,12 @@ bool FurnaceGUI::init() { newFilePicker=new FurnaceFilePicker; newFilePicker->setHomeDir(getHomeDir()); - newFilePicker->open("New File Picker","/home",false,{}); + newFilePicker->open("New File Picker","/home",false, + {_("songs"), "*.fur *.dmf *.mod *.s3m *.xm *.it *.fc13 *.fc14 *.smod *.fc *.ftm *.0cc *.dnm *.eft *.fub *.tfe", + _("instruments"), "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.gyb *.opm *.wopl *.wopn", + _("audio"), "*.wav", + _("all files"), "*"} + ); updateWindowTitle(); updateROMExportAvail(); diff --git a/src/gui/newFilePicker.cpp b/src/gui/newFilePicker.cpp index 1f5b30727..0045667da 100644 --- a/src/gui/newFilePicker.cpp +++ b/src/gui/newFilePicker.cpp @@ -198,10 +198,53 @@ void FurnaceFilePicker::updateEntryName() { } } +// the name of this function is somewhat misleading. +// it filters files by type, then sorts them. void FurnaceFilePicker::sortFiles() { std::chrono::high_resolution_clock::time_point timeStart=std::chrono::high_resolution_clock::now(); entryLock.lock(); - sortedEntries=entries; + // check for "no filter" + if (filterOptions[curFilterType+1]=="*") { + // copy entire list + sortedEntries=entries; + } else { + // sort by extension + std::vector parsedSort; + String nextType; + for (char i: filterOptions[curFilterType+1]) { + switch (i) { + case '*': // ignore + break; + case ' ': // separator + if (!nextType.empty()) { + parsedSort.push_back(nextType); + nextType=""; + } + break; + default: // push + nextType.push_back(i); + break; + } + } + if (!nextType.empty()) { + parsedSort.push_back(nextType); + nextType=""; + } + + sortedEntries.clear(); + for (FileEntry* i: entries) { + if (i->isDir) { + sortedEntries.push_back(i); + continue; + } + for (const String& j: parsedSort) { + if (i->ext==j) { + sortedEntries.push_back(i); + break; + } + } + } + } // sort by name std::sort(sortedEntries.begin(),sortedEntries.end(),[this](const FileEntry* a, const FileEntry* b) -> bool { @@ -276,12 +319,7 @@ void FurnaceFilePicker::filterFiles() { } 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) { + if (i->nameLower.find(lowerFilter)!=String::npos) { filteredEntries.push_back(i); } } @@ -291,6 +329,7 @@ bool FurnaceFilePicker::draw() { if (!isOpen) return false; String newDir; + String tempID; ImGui::SetNextWindowSizeConstraints(ImVec2(800.0,600.0),ImVec2(8000.0,6000.0)); if (ImGui::Begin(windowName.c_str(),NULL,ImGuiWindowFlags_NoSavedSettings)) { @@ -335,23 +374,39 @@ bool FurnaceFilePicker::draw() { ImVec2 tableSize=ImGui::GetContentRegionAvail(); tableSize.y-=ImGui::GetFrameHeightWithSpacing()*2.0f; + // display a message on empty dir, no matches or error if (filteredEntries.empty()) { - if (sortedEntries.empty()) { - if (failMessage.empty()) { - ImGui::Text("This directory is empty!"); + if (ImGui::BeginTable("NoFiles",3,ImGuiTableFlags_BordersOuter,tableSize)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.5f); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.5f); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::TableNextColumn(); + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(tableSize.y-ImGui::GetTextLineHeight())*0.5); + if (sortedEntries.empty()) { + if (failMessage.empty()) { + ImGui::Text("This directory is empty!"); + } else { + ImGui::Text("%s!",failMessage.c_str()); + } } else { - ImGui::Text("Could not load this directory!\n(%s)",failMessage.c_str()); - } - } else { - if (failMessage.empty()) { - ImGui::Text("No results"); - } else { - ImGui::Text("Could not load this directory!\n(%s)",failMessage.c_str()); + if (failMessage.empty()) { + ImGui::Text("No results"); + } else { + ImGui::Text("%s!",failMessage.c_str()); + } } + + ImGui::TableNextColumn(); + ImGui::EndTable(); } } else { // this is the list view. I might add other view modes in the future... - if (ImGui::BeginTable("FileList",4,ImGuiTableFlags_Borders|ImGuiTableFlags_ScrollY,tableSize)) { + if (ImGui::BeginTable("FileList",4,ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_RowBg,tableSize)) { + float rowHeight=ImGui::GetTextLineHeight()+ImGui::GetStyle().CellPadding.y*2.0f; ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,ImGui::CalcTextSize(" .eeee").x); ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,ImGui::CalcTextSize(" 999.99G").x); @@ -359,9 +414,45 @@ bool FurnaceFilePicker::draw() { ImGui::TableSetupScrollFreeze(0,1); // header (sort options) - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + const char* nameHeader="Name##SortName"; + const char* typeHeader="Type##SortType"; + const char* sizeHeader="Size##SortSize"; + const char* dateHeader="Date##SortDate"; + + switch (sortMode) { + case FP_SORT_NAME: + if (sortInvert) { + nameHeader=ICON_FA_CHEVRON_UP "Name##SortName"; + } else { + nameHeader=ICON_FA_CHEVRON_DOWN "Name##SortName"; + } + break; + case FP_SORT_EXT: + if (sortInvert) { + typeHeader=ICON_FA_CHEVRON_UP "Type##SortType"; + } else { + typeHeader=ICON_FA_CHEVRON_DOWN "Type##SortType"; + } + break; + case FP_SORT_SIZE: + if (sortInvert) { + sizeHeader=ICON_FA_CHEVRON_UP "Size##SortSize"; + } else { + sizeHeader=ICON_FA_CHEVRON_DOWN "Size##SortSize"; + } + break; + case FP_SORT_DATE: + if (sortInvert) { + dateHeader=ICON_FA_CHEVRON_UP "Date##SortDate"; + } else { + dateHeader=ICON_FA_CHEVRON_DOWN "Date##SortDate"; + } + break; + } + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers,rowHeight); ImGui::TableNextColumn(); - if (ImGui::Selectable("Name##SortName")) { + if (ImGui::Selectable(nameHeader)) { if (sortMode==FP_SORT_NAME) { sortInvert=!sortInvert; } else { @@ -370,7 +461,7 @@ bool FurnaceFilePicker::draw() { } } ImGui::TableNextColumn(); - if (ImGui::Selectable("Type##SortType")) { + if (ImGui::Selectable(typeHeader)) { if (sortMode==FP_SORT_EXT) { sortInvert=!sortInvert; } else { @@ -379,7 +470,7 @@ bool FurnaceFilePicker::draw() { } } ImGui::TableNextColumn(); - if (ImGui::Selectable("Size##SortSize")) { + if (ImGui::Selectable(sizeHeader)) { if (sortMode==FP_SORT_SIZE) { sortInvert=!sortInvert; } else { @@ -388,7 +479,7 @@ bool FurnaceFilePicker::draw() { } } ImGui::TableNextColumn(); - if (ImGui::Selectable("Date##SortDate")) { + if (ImGui::Selectable(dateHeader)) { if (sortMode==FP_SORT_DATE) { sortInvert=!sortInvert; } else { @@ -400,18 +491,28 @@ bool FurnaceFilePicker::draw() { // file list entryLock.lock(); int index=0; - listClipper.Begin(filteredEntries.size()); + listClipper.Begin(filteredEntries.size(),rowHeight); while (listClipper.Step()) { for (int _i=listClipper.DisplayStart; _itype]; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (i->type==FP_TYPE_DIR) { - ImGui::PushStyleColor(ImGuiCol_Text,0xff00ffff); + // get style for this entry + if (!i->ext.empty()) { + for (FileTypeStyle& j: fileTypeRegistry) { + if (i->ext==j.ext) { + style=&j; + break; + } + } } + + // draw + ImGui::TableNextRow(0,rowHeight); + ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(style->color)); ImGui::PushID(index++); - if (ImGui::Selectable("##File",i->isSelected,ImGuiSelectableFlags_AllowDoubleClick|ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_SpanAvailWidth)) { + if (ImGui::Selectable(style->icon.c_str(),i->isSelected,ImGuiSelectableFlags_AllowDoubleClick|ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_SpanAvailWidth)) { for (FileEntry* j: chosenEntries) { j->isSelected=false; } @@ -463,9 +564,7 @@ bool FurnaceFilePicker::draw() { ImGui::Text("%d/%02d/%02d %02d:%02d",i->time.tm_year+1900,i->time.tm_mon+1,i->time.tm_mday,i->time.tm_hour,i->time.tm_min); } - if (i->type==FP_TYPE_DIR) { - ImGui::PopStyleColor(); - } + ImGui::PopStyleColor(); } } ImGui::EndTable(); @@ -473,16 +572,27 @@ bool FurnaceFilePicker::draw() { } } + // file name input ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Name: "); ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x*0.68f); if (ImGui::InputText("##EntryName",&entryName)) { // find an entry with this name - - + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##FilterType",filterOptions[curFilterType].c_str())) { + for (size_t i=0; i& filter) { if (isOpen) return false; + if (filter.size()&1) { + logE("invalid filter data! it should be an even-sized vector with even elements containing names and odd ones being the filters."); + return false; + } + + filterOptions=filter; + + if (filterOptions.size()<2) { + filterOptions.push_back("all files"); + filterOptions.push_back("*"); + } + curFilterType=0; readDirectory(path); windowName=name; @@ -553,6 +675,25 @@ void FurnaceFilePicker::saveSettings(DivConfig& conf) { } +void FurnaceFilePicker::setTypeStyle(FileType type, ImVec4 color, String icon) { + // "##File" is appended here for performance. + defaultTypeStyle[type].icon=icon+"##File"; + defaultTypeStyle[type].color=color; +} + +void FurnaceFilePicker::registerType(String ext, ImVec4 color, String icon) { + FileTypeStyle t; + t.ext=ext; + // "##File" is appended here for performance. + t.icon=icon+"##File"; + t.color=color; + fileTypeRegistry.push_back(t); +} + +void FurnaceFilePicker::clearTypes() { + fileTypeRegistry.clear(); +} + FurnaceFilePicker::FurnaceFilePicker(): fileThread(NULL), haveFiles(false), @@ -562,7 +703,11 @@ FurnaceFilePicker::FurnaceFilePicker(): isMobile(false), sortInvert(false), scheduledSort(0), + curFilterType(0), sortMode(FP_SORT_NAME), curStatus(FP_STATUS_WAITING) { - + for (int i=0; i sortedEntries; std::vector filteredEntries; std::vector chosenEntries; + std::vector filterOptions; std::thread* fileThread; std::mutex entryLock; String windowName; @@ -71,9 +82,13 @@ class FurnaceFilePicker { ImGuiListClipper listClipper; bool haveFiles, haveStat, stopReading, isOpen, isMobile, sortInvert; int scheduledSort; + size_t curFilterType; SortModes sortMode; FilePickerStatus curStatus; + std::vector fileTypeRegistry; + FileTypeStyle defaultTypeStyle[FP_TYPE_MAX]; + void sortFiles(); void filterFiles(); void clearAllFiles(); @@ -91,5 +106,8 @@ class FurnaceFilePicker { bool open(String name, String path, bool modal, const std::vector& filter); void loadSettings(DivConfig& conf); void saveSettings(DivConfig& conf); + void setTypeStyle(FileType type, ImVec4 color, String icon); + void registerType(String ext, ImVec4 color, String icon); + void clearTypes(); FurnaceFilePicker(); }; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 595f7330d..66f7c5f35 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -6812,6 +6812,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { //mainFont->EllipsisCharCount=3; } + // set built-in file picker up (OLD) ImGuiFileDialog::Instance()->okButtonString=_("OK"); ImGuiFileDialog::Instance()->cancelButtonString=_("Cancel"); ImGuiFileDialog::Instance()->searchString=_("Search"); @@ -6885,6 +6886,61 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opm",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + // set built-in file picker up (NEW) + newFilePicker->setTypeStyle(FP_TYPE_UNKNOWN,uiColors[GUI_COLOR_FILE_OTHER],ICON_FA_QUESTION); + newFilePicker->setTypeStyle(FP_TYPE_NORMAL,uiColors[GUI_COLOR_FILE_OTHER],ICON_FA_FILE_O); + newFilePicker->setTypeStyle(FP_TYPE_DIR,uiColors[GUI_COLOR_FILE_DIR],ICON_FA_FOLDER_O); + newFilePicker->setTypeStyle(FP_TYPE_LINK,uiColors[GUI_COLOR_FILE_OTHER],ICON_FA_FILE_O); + newFilePicker->setTypeStyle(FP_TYPE_PIPE,uiColors[GUI_COLOR_FILE_OTHER],ICON_FA_FILE_O); + newFilePicker->setTypeStyle(FP_TYPE_SOCKET,uiColors[GUI_COLOR_FILE_OTHER],ICON_FA_FILE_O); + + newFilePicker->registerType(".fur",uiColors[GUI_COLOR_FILE_SONG_NATIVE],ICON_FA_FILE); + newFilePicker->registerType(".fui",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".fuw",uiColors[GUI_COLOR_FILE_WAVE],ICON_FA_FILE); + newFilePicker->registerType(".dmp",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".dmw",uiColors[GUI_COLOR_FILE_WAVE],ICON_FA_FILE); + newFilePicker->registerType(".wav",uiColors[GUI_COLOR_FILE_AUDIO],ICON_FA_FILE_AUDIO_O); + newFilePicker->registerType(".dmc",uiColors[GUI_COLOR_FILE_AUDIO],ICON_FA_FILE_AUDIO_O); + newFilePicker->registerType(".brr",uiColors[GUI_COLOR_FILE_AUDIO],ICON_FA_FILE_AUDIO_O); + newFilePicker->registerType(".vgm",uiColors[GUI_COLOR_FILE_VGM],ICON_FA_FILE_AUDIO_O); + newFilePicker->registerType(".zsm",uiColors[GUI_COLOR_FILE_ZSM],ICON_FA_FILE_AUDIO_O); + newFilePicker->registerType(".ttf",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + newFilePicker->registerType(".otf",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + newFilePicker->registerType(".ttc",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + newFilePicker->registerType(".dfont",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + newFilePicker->registerType(".fon",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + newFilePicker->registerType(".pcf",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + newFilePicker->registerType(".psf",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); + + newFilePicker->registerType(".dmf",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".mod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".s3m",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".xm",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".it",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".fc13",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".fc14",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".fc",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".smod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".ftm",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".0cc",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".dnm",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + newFilePicker->registerType(".eft",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + + newFilePicker->registerType(".fub",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + + newFilePicker->registerType(".tfi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".opli",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".opni",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".y12",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + newFilePicker->registerType(".opm",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + if (updateFonts) { if (fileDialog!=NULL) delete fileDialog; #ifdef FLATPAK_WORKAROUNDS