From ad436512580ab56a623834e223eef11cf3084bb3 Mon Sep 17 00:00:00 2001 From: TSOLfan Date: Tue, 17 Mar 2026 13:31:14 -0400 Subject: [PATCH] feat: Collection Display Customizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Includes the following new settings under Appearance: - Show Collections Promotion If all systems are hidden, the files in the Collection folder are force promoted to the main screen. This setting controls whether or not that will happen if you want to keep them behind a Collections menu item. - Sort Collections Entries This setting will force entries in a Collection file to be displayed in the order they are in the file. A use case would be to list games in a specific order, such as order of release for games in a series. - Use Collections Nested Map Collections only support a single map.txt file in the root of the Collections folder. This will check for a map.txt file in a Collections subfolder and use it for entries in that subfolder. If one doesn’t exist, it will fall back and look for one in the Collections folder root. A use case would be adding platform tags to games in a Franchise specific collection folder, and have the custom names appear only there and not in a separate collection the same game may appear in such as another collection of just games for a single platform. - Show Quickswitcher UI Games Icon If all systems are hidden for a “Collections Only” setup, the Games icon will still display in the Menu alongside the Collections icon. This allows you to control whether or not you want it to still display. --- workspace/all/common/config.c | 92 +++++++++++++++++++++++++++++ workspace/all/common/config.h | 20 +++++++ workspace/all/nextui/nextui.c | 30 ++++++++-- workspace/all/settings/settings.cpp | 16 +++++ 4 files changed, 152 insertions(+), 6 deletions(-) diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index f47c4e32d..9f34d8b00 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -50,10 +50,14 @@ void CFG_defaults(NextUISettings *cfg) .showRecents = CFG_DEFAULT_SHOWRECENTS, .showTools = CFG_DEFAULT_SHOWTOOLS, .showCollections = CFG_DEFAULT_SHOWCOLLECTIONS, + .showCollectionsPromotion = CFG_DEFAULT_SHOWCOLLECTIONSPROMOTION, + .sortCollectionsEntries = CFG_DEFAULT_SORTCOLLECTIONSENTRIES, + .useCollectionsNestedMap = CFG_DEFAULT_USECOLLECTIONSNESTEDMAP, .showGameArt = CFG_DEFAULT_SHOWGAMEART, .gameSwitcherScaling = CFG_DEFAULT_GAMESWITCHERSCALING, .defaultView = CFG_DEFAULT_VIEW, .showQuickSwitcherUi = CFG_DEFAULT_SHOWQUICKWITCHERUI, + .showQuickSwitcherUiGames = CFG_DEFAULT_SHOWQUICKWITCHERUIGAMES, .muteLeds = CFG_DEFAULT_MUTELEDS, @@ -205,6 +209,21 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) CFG_setShowCollections((bool)temp_value); continue; } + if (sscanf(line, "collectionspromotion=%i", &temp_value) == 1) + { + CFG_setShowCollectionsPromotion((bool)temp_value); + continue; + } + if (sscanf(line, "collectionsentriessort=%i", &temp_value) == 1) + { + CFG_setSortCollectionsEntries((bool)temp_value); + continue; + } + if (sscanf(line, "usecollectionsnestedmap=%i", &temp_value) == 1) + { + CFG_setUseCollectionsNestedMap((bool)temp_value); + continue; + } if (sscanf(line, "gameart=%i", &temp_value) == 1) { CFG_setShowGameArt((bool)temp_value); @@ -285,6 +304,11 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) CFG_setShowQuickswitcherUI(temp_value); continue; } + if (sscanf(line, "quickSwitcherUiGames=%i", &temp_value) == 1) + { + CFG_setShowQuickswitcherUIGames(temp_value); + continue; + } if (sscanf(line, "wifiDiagnostics=%i", &temp_value) == 1) { CFG_setWifiDiagnostics(temp_value); @@ -638,6 +662,39 @@ void CFG_setShowCollections(bool show) CFG_sync(); } +bool CFG_getShowCollectionsPromotion(void) +{ + return settings.showCollectionsPromotion; +} + +void CFG_setShowCollectionsPromotion(bool show) +{ + settings.showCollectionsPromotion = show; + CFG_sync(); +} + +bool CFG_getSortCollectionsEntries(void) +{ + return settings.sortCollectionsEntries; +} + +void CFG_setSortCollectionsEntries(bool show) +{ + settings.sortCollectionsEntries = show; + CFG_sync(); +} + +bool CFG_getUseCollectionsNestedMap(void) +{ + return settings.useCollectionsNestedMap; +} + +void CFG_setUseCollectionsNestedMap(bool show) +{ + settings.useCollectionsNestedMap = show; + CFG_sync(); +} + bool CFG_getShowGameArt(void) { return settings.showGameArt; @@ -770,6 +827,17 @@ void CFG_setShowQuickswitcherUI(bool on) CFG_sync(); } +bool CFG_getShowQuickswitcherUIGames(void) +{ + return settings.showQuickSwitcherUiGames; +} + +void CFG_setShowQuickswitcherUIGames(bool on) +{ + settings.showQuickSwitcherUiGames = on; + CFG_sync(); +} + bool CFG_getWifiDiagnostics(void) { return settings.wifiDiagnostics; @@ -1085,6 +1153,18 @@ void CFG_get(const char *key, char *value) else if (strcmp(key, "collections") == 0) { sprintf(value, "%i", CFG_getShowCollections()); + } + else if (strcmp(key, "collectionspromotion") == 0) + { + sprintf(value, "%i", CFG_getShowCollectionsPromotion()); + } + else if (strcmp(key, "collectionsentriessort") == 0) + { + sprintf(value, "%i", CFG_getSortCollectionsEntries()); + } + else if (strcmp(key, "usecollectionsnestedmap") == 0) + { + sprintf(value, "%i", CFG_getUseCollectionsNestedMap()); } else if (strcmp(key, "gameart") == 0) { @@ -1146,6 +1226,10 @@ void CFG_get(const char *key, char *value) { sprintf(value, "%i", (int)(CFG_getShowQuickswitcherUI())); } + else if (strcmp(key, "quickSwitcherUiGames") == 0) + { + sprintf(value, "%i", (int)(CFG_getShowQuickswitcherUIGames())); + } else if (strcmp(key, "wifiDiagnostics") == 0) { sprintf(value, "%i", (int)(CFG_getWifiDiagnostics())); @@ -1221,6 +1305,9 @@ void CFG_sync(void) fprintf(file, "recents=%i\n", settings.showRecents); fprintf(file, "tools=%i\n", settings.showTools); fprintf(file, "collections=%i\n", settings.showCollections); + fprintf(file, "collectionspromotion=%i\n", settings.showCollectionsPromotion); + fprintf(file, "collectionsentriessort=%i\n", settings.sortCollectionsEntries); + fprintf(file, "usecollectionsnestedmap=%i\n", settings.useCollectionsNestedMap); fprintf(file, "gameart=%i\n", settings.showGameArt); fprintf(file, "showfoldernamesatroot=%i\n", settings.showFolderNamesAtRoot); fprintf(file, "screentimeout=%i\n", settings.screenTimeoutSecs); @@ -1237,6 +1324,7 @@ void CFG_sync(void) fprintf(file, "wifi=%i\n", settings.wifi); fprintf(file, "defaultView=%i\n", settings.defaultView); fprintf(file, "quickSwitcherUi=%i\n", settings.showQuickSwitcherUi); + fprintf(file, "quickSwitcherUiGames=%i\n", settings.showQuickSwitcherUiGames); fprintf(file, "wifiDiagnostics=%i\n", settings.wifiDiagnostics); fprintf(file, "bluetooth=%i\n", settings.bluetooth); fprintf(file, "btDiagnostics=%i\n", settings.bluetoothDiagnostics); @@ -1282,6 +1370,9 @@ void CFG_print(void) printf("\t\"recents\": %i,\n", settings.showRecents); printf("\t\"tools\": %i,\n", settings.showTools); printf("\t\"collections\": %i,\n", settings.showCollections); + printf("\t\"collectionspromotion\": %i,\n", settings.showCollectionsPromotion); + printf("\t\"collectionsentriessort\": %i,\n", settings.sortCollectionsEntries); + printf("\t\"usecollectionsnestedmap\": %i,\n", settings.useCollectionsNestedMap); printf("\t\"gameart\": %i,\n", settings.showGameArt); printf("\t\"showfoldernamesatroot\": %i,\n", settings.showFolderNamesAtRoot); printf("\t\"screentimeout\": %i,\n", settings.screenTimeoutSecs); @@ -1298,6 +1389,7 @@ void CFG_print(void) printf("\t\"wifi\": %i,\n", settings.wifi); printf("\t\"defaultView\": %i,\n", settings.defaultView); printf("\t\"quickSwitcherUi\": %i,\n", settings.showQuickSwitcherUi); + printf("\t\"quickSwitcherUiGames\": %i,\n", settings.showQuickSwitcherUiGames); printf("\t\"wifiDiagnostics\": %i,\n", settings.wifiDiagnostics); printf("\t\"bluetooth\": %i,\n", settings.bluetooth); printf("\t\"btDiagnostics\": %i,\n", settings.bluetoothDiagnostics); diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index 56c74ffaf..7ad1874e2 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -101,10 +101,14 @@ typedef struct bool showRecents; bool showTools; bool showCollections; + bool showCollectionsPromotion; + bool sortCollectionsEntries; + bool useCollectionsNestedMap; bool showGameArt; bool showFolderNamesAtRoot; bool romsUseFolderBackground; bool showQuickSwitcherUi; + bool showQuickSwitcherUiGames; int defaultView; // Mute switch @@ -169,6 +173,9 @@ typedef struct #define CFG_DEFAULT_SHOWMENUTRANSITIONS true #define CFG_DEFAULT_SHOWRECENTS true #define CFG_DEFAULT_SHOWCOLLECTIONS true +#define CFG_DEFAULT_SHOWCOLLECTIONSPROMOTION true +#define CFG_DEFAULT_SORTCOLLECTIONSENTRIES true +#define CFG_DEFAULT_USECOLLECTIONSNESTEDMAP false #define CFG_DEFAULT_SHOWGAMEART true #define CFG_DEFAULT_SHOWFOLDERNAMESATROOT true #define CFG_DEFAULT_GAMESWITCHERSCALING GFX_SCALE_FULLSCREEN @@ -185,6 +192,7 @@ typedef struct #define CFG_DEFAULT_WIFI false #define CFG_DEFAULT_VIEW SCREEN_GAMELIST #define CFG_DEFAULT_SHOWQUICKWITCHERUI true +#define CFG_DEFAULT_SHOWQUICKWITCHERUIGAMES true #define CFG_DEFAULT_WIFI_DIAG false #define CFG_DEFAULT_SHOWTOOLS true #define CFG_DEFAULT_BLUETOOTH false @@ -264,6 +272,15 @@ void CFG_setShowTools(bool show); // Show/hide collections in the main menu. bool CFG_getShowCollections(void); void CFG_setShowCollections(bool show); +// Show/hide collections promotion in the main menu. +bool CFG_getShowCollectionsPromotion(void); +void CFG_setShowCollectionsPromotion(bool show); +// Sort collections entries for roms. +bool CFG_getSortCollectionsEntries(void); +void CFG_setSortCollectionsEntries(bool show); +// Use Nested Collections map.txt for roms. +bool CFG_getUseCollectionsNestedMap(void); +void CFG_setUseCollectionsNestedMap(bool show); // Show/hide game art in the main menu. bool CFG_getShowGameArt(void); void CFG_setShowGameArt(bool show); @@ -307,6 +324,9 @@ void CFG_setDefaultView(int view); // Quick switcher UI painting on/off bool CFG_getShowQuickswitcherUI(void); void CFG_setShowQuickswitcherUI(bool on); +// Quick switcher UI Games painting on/off +bool CFG_getShowQuickswitcherUIGames(void); +void CFG_setShowQuickswitcherUIGames(bool on); // WiFi diagnostic logging on/off bool CFG_getWifiDiagnostics(void); void CFG_setWifiDiagnostics(bool on); diff --git a/workspace/all/nextui/nextui.c b/workspace/all/nextui/nextui.c index c2d921059..28fde3490 100644 --- a/workspace/all/nextui/nextui.c +++ b/workspace/all/nextui/nextui.c @@ -242,10 +242,27 @@ static void getUniqueName(Entry* entry, char* out_name) { static void Directory_index(Directory* self) { int is_collection = prefixMatch(COLLECTIONS_PATH, self->path); int skip_index = exactMatch(FAUX_RECENT_PATH, self->path) || is_collection; // not alphabetized - + Hash* map = NULL; char map_path[256]; - sprintf(map_path, "%s/map.txt", is_collection ? COLLECTIONS_PATH : self->path); + char base_path[256]; + + strcpy(base_path, self->path); + char* tmp = strrchr(base_path, '/') + 1; + tmp[0] = '\0'; + + sprintf(map_path, "%s/map.txt", is_collection ? COLLECTIONS_PATH : self->path); // previous logic + + if (is_collection && CFG_getUseCollectionsNestedMap()){ + if(suffixMatch(".txt", self->path)){ + sprintf(map_path, "%smap.txt", base_path); + } else { + sprintf(map_path, "%s/map.txt", self->path); + } + + if (!exists(map_path)) + sprintf(map_path, "%s/map.txt", COLLECTIONS_PATH); + } if (exists(map_path)) { FILE* file = fopen(map_path, "r"); @@ -294,7 +311,7 @@ static void Directory_index(Directory* self) { Array_free(self->entries); self->entries = entries; } - if (resort) EntryArray_sort(self->entries); + if ((resort && !is_collection) || (resort && is_collection && CFG_getSortCollectionsEntries())) EntryArray_sort(self->entries); } } @@ -371,7 +388,7 @@ static Directory* Directory_new(char* path, int selected) { self->entries = getRecents(); } else if (exactMatch(path, ROMS_PATH)) { - self->entries = getRoms(); + self->entries = getRoot(); } else if (!exactMatch(path, COLLECTIONS_PATH) && prefixMatch(COLLECTIONS_PATH, path) && suffixMatch(".txt", path)) { self->entries = getCollection(path); @@ -815,7 +832,8 @@ static Array* getQuickEntries(void) { Array_push(entries, Entry_new(COLLECTIONS_PATH, ENTRY_DIR)); // Not sure we need this, its just a button press away (B) - Array_push(entries, Entry_newNamed(ROMS_PATH, ENTRY_DIR, "Games")); + if(CFG_getShowQuickswitcherUIGames()) + Array_push(entries, Entry_newNamed(ROMS_PATH, ENTRY_DIR, "Games")); // Add tools if applicable if (hasTools() && !simple_mode) { @@ -861,7 +879,7 @@ static Array* getRoot(void) { // Handle collections if (hasCollections() && CFG_getShowCollections()) { - if (entries->count) { + if (entries->count || !CFG_getShowCollectionsPromotion()) { Array_push(root, Entry_new(COLLECTIONS_PATH, ENTRY_DIR)); } else { // No visible systems, promote collections to root Array *collections = getCollections(); diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 5717b7790..149575676 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -345,6 +345,18 @@ int main(int argc, char *argv[]) []() -> std::any { return CFG_getShowCollections(); }, [](const std::any &value) { CFG_setShowCollections(std::any_cast(value)); }, []() { CFG_setShowCollections(CFG_DEFAULT_SHOWCOLLECTIONS);}}, + new MenuItem{ListItemType::Generic, "Show Collections Promotion", "Show \"Collections\" menu entries in root game list\nOnly occurs when all Game folders are hidden.", {false, true}, on_off, + []() -> std::any { return CFG_getShowCollectionsPromotion(); }, + [](const std::any &value) { CFG_setShowCollectionsPromotion(std::any_cast(value)); }, + []() { CFG_setShowCollectionsPromotion(CFG_DEFAULT_SHOWCOLLECTIONSPROMOTION);}}, + new MenuItem{ListItemType::Generic, "Sort Collections Entries", "Sort \"Collections\" entries alphabetically.\nOtherwise uses order listed in Collection file.", {false, true}, on_off, + []() -> std::any { return CFG_getSortCollectionsEntries(); }, + [](const std::any &value) { CFG_setSortCollectionsEntries(std::any_cast(value)); }, + []() { CFG_setSortCollectionsEntries(CFG_DEFAULT_SORTCOLLECTIONSENTRIES);}}, + new MenuItem{ListItemType::Generic, "Use Collections Nested Map", "Use map.txt contained within \"Collections\" subfolders.\nFalls back to map.txt in root Collections folder if not found.", {false, true}, on_off, + []() -> std::any { return CFG_getUseCollectionsNestedMap(); }, + [](const std::any &value) { CFG_setUseCollectionsNestedMap(std::any_cast(value)); }, + []() { CFG_setUseCollectionsNestedMap(CFG_DEFAULT_USECOLLECTIONSNESTEDMAP);}}, new MenuItem{ListItemType::Generic, "Show game art", "Show game artwork in the main menu", {false, true}, on_off, []() -> std::any { return CFG_getShowGameArt(); }, [](const std::any &value) @@ -359,6 +371,10 @@ int main(int argc, char *argv[]) []() -> std::any{ return CFG_getShowQuickswitcherUI(); }, [](const std::any &value){ CFG_setShowQuickswitcherUI(std::any_cast(value)); }, []() { CFG_setShowQuickswitcherUI(CFG_DEFAULT_SHOWQUICKWITCHERUI);}}, + new MenuItem{ListItemType::Generic, "Show Quickswitcher UI Games Icon", "Show/hide Quickswitcher UI Games Icon.\nWhen hidden, the Games Icon won't display.", {false, true}, on_off, + []() -> std::any{ return CFG_getShowQuickswitcherUIGames(); }, + [](const std::any &value){ CFG_setShowQuickswitcherUIGames(std::any_cast(value)); }, + []() { CFG_setShowQuickswitcherUIGames(CFG_DEFAULT_SHOWQUICKWITCHERUIGAMES);}}, // not needed anymore // new MenuItem{ListItemType::Generic, "Game switcher scaling", "The scaling algorithm used to display the savegame image.", scaling, scaling_strings, []() -> std::any // { return CFG_getGameSwitcherScaling(); },