Multiple improvements and bugfixes to the custom collections handling.

This commit is contained in:
Leon Styhre 2020-10-20 21:01:24 +02:00
parent 3e6f3487c9
commit 87bd205a3f
7 changed files with 113 additions and 106 deletions

View file

@ -1047,9 +1047,20 @@ The file contents is simply a list of ROM files, such as the following:
Any changes to custom collections (for example adding or removing a game) will be immediately written to the corresponding collection configuration file.
>>>
Note that if you for example copy or migrate a collection from a previous version of EmulationStation or if you're setting up EmulationStation Desktop Edition on a new computer, even though you copy the files into the collections directory, they will not show up in the application. You always need to enable the collection in the menu. ES looks inside the es_settings.cfg file during startup to see which collections should be shown.
If you're migrating from a previous version of EmulationStation that has absolute paths in the collection files, these will be rewritten with the %ROMPATH% variable the first time you make a change to the collection.
>>>
It's also possible to add media files to the custom collections entries if they are grouped under the _collections_ system (this is enabled by default). Simply create a directory under your media folder, for example `~/.emulationstation/downloaded_media`, which corresponds to the collection name. For our example it would be _1980s_. The media files themselves should also be named after the collection. This is an example of what this could look like:
```
~/.emulationstation/downloaded_media/1980s/covers/1980s.png
~/.emulationstation/downloaded_media/1980s/videos/1980s.mp4
```
For more details on how to manually copy media files, see the section [Manually copying game media files](USERGUIDE.md#manually-copying-game-media-files) earlier in this guide.
## Themes

View file

@ -272,6 +272,11 @@ void CollectionSystemManager::updateSystemsList()
if (rootFolder->getChildren().size() > 0) {
rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->
getSortTypeString()), Settings::getInstance()->getBool("FavFirstCustom"));
// Update the custom collections metadata now that the sorting has been done.
std::vector<FileData*> customCollections = rootFolder->getChildren();
for (auto it = customCollections.cbegin(); it != customCollections.cend(); it++)
updateCollectionFolderMetadata((*it)->getSystem());
SystemData::sSystemVector.push_back(mCustomCollectionsBundle);
}
}
@ -588,17 +593,17 @@ void CollectionSystemManager::setEditMode(std::string collectionName)
// If it's bundled, this needs to be the bundle system.
mEditingCollectionSystemData = sysData;
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Editing the '" +
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "EDITING THE '" +
Utils::String::toUpper(collectionName) +
"' Collection. Add/remove games with Y.", 10000);
"' COLLECTION, ADD/REMOVE GAMES WITH 'Y'", 10000);
mWindow->setInfoPopup(s);
}
void CollectionSystemManager::exitEditMode()
{
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Finished editing the '" +
mEditingCollection + "' Collection.", 4000);
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "FINISHED EDITING THE '" +
Utils::String::toUpper(mEditingCollection) + "' COLLECTION", 4000);
mWindow->setInfoPopup(s);
mIsEditingCustom = false;
@ -685,11 +690,13 @@ bool CollectionSystemManager::toggleGameInCollection(FileData* file)
refreshCollectionSystems(file->getSourceFileData());
}
if (adding)
s = new GuiInfoPopup(mWindow, "Added '" + Utils::String::removeParenthesis(name) +
"' to '" + Utils::String::toUpper(sysName) + "'", 4000);
s = new GuiInfoPopup(mWindow, "ADDED '" +
Utils::String::toUpper(Utils::String::removeParenthesis(name)) +
"' TO '" + Utils::String::toUpper(sysName) + "'", 4000);
else
s = new GuiInfoPopup(mWindow, "Removed '" + Utils::String::removeParenthesis(name) +
"' from '" + Utils::String::toUpper(sysName) + "'", 4000);
s = new GuiInfoPopup(mWindow, "REMOVED '" +
Utils::String::toUpper(Utils::String::removeParenthesis(name)) +
"' FROM '" + Utils::String::toUpper(sysName) + "'", 4000);
mWindow->setInfoPopup(s);
return true;
}
@ -729,87 +736,42 @@ void CollectionSystemManager::initAutoCollectionSystems()
}
}
// This may come in handy if at any point in time in the future we want to
// automatically generate metadata for a folder.
// Used to generate a description of the collection, all other metadata fields are hidden.
void CollectionSystemManager::updateCollectionFolderMetadata(SystemData* sys)
{
FileData* rootFolder = sys->getRootFolder();
rootFolder->metadata.set("hidemetadata", "true");
std::string desc = "This collection is empty.";
std::string rating = "0";
std::string players = "1";
std::string releasedate = "N/A";
std::string developer = "None";
std::string genre = "None";
std::string video = "";
std::string thumbnail = "";
std::string image = "";
std::vector<FileData*> gamesList = rootFolder->getChildren();
unsigned int gameCount = gamesList.size();
std::unordered_map<std::string, FileData*> games = rootFolder->getChildrenByFilename();
if (games.size() > 0) {
std::string games_list = "";
int games_counter = 0;
for (std::unordered_map<std::string, FileData*>::const_iterator
iter = games.cbegin(); iter != games.cend(); ++iter) {
games_counter++;
FileData* file = iter->second;
std::string new_rating = file->metadata.get("rating");
std::string new_releasedate = file->metadata.get("releasedate");
std::string new_developer = file->metadata.get("developer");
std::string new_genre = file->metadata.get("genre");
std::string new_players = file->metadata.get("players");
rating = (new_rating > rating ? (new_rating != "" ?
new_rating : rating) : rating);
players = (new_players > players ? (new_players != "" ?
new_players : players) : players);
releasedate = (new_releasedate < releasedate ? (new_releasedate != "" ?
new_releasedate : releasedate) : releasedate);
developer = (developer == "None" ? new_developer : (new_developer != developer ?
"Various" : new_developer));
genre = (genre == "None" ? new_genre : (new_genre != genre ?
"Various" : new_genre));
switch (games_counter) {
case 2:
case 3:
games_list += ", ";
case 1:
games_list += "'" + file->getName() + "'";
break;
case 4:
games_list += " among other titles.";
}
}
desc = "This collection contains " + std::to_string(games_counter) +
" games, including " + games_list;
FileData* randomGame = sys->getRandomGame();
if (randomGame) {
video = randomGame->getVideoPath();
thumbnail = randomGame->getThumbnailPath();
image = randomGame->getImagePath();
if (gameCount > 0) {
switch (gameCount) {
case 1:
desc = "This collection contains 1 game: '" + gamesList[0]->metadata.get("name") +
" [" + gamesList[0]->getSourceFileData()->getSystem()->getName() + "]'.";
break;
case 2:
desc = "This collection contains 2 games: '" + gamesList[0]->metadata.get("name") +
" [" + gamesList[0]->getSourceFileData()->getSystem()->getName() +
"]' and '" + gamesList[1]->metadata.get("name") + " [" +
gamesList[1]->getSourceFileData()->getSystem()->getName() + "]'.";
break;
default:
desc = "This collection contains " + std::to_string(gameCount) +
" games: '" + gamesList[0]->metadata.get("name") +
" [" + gamesList[0]->getSourceFileData()->getSystem()->getName() +
"]', '" + gamesList[1]->metadata.get("name") + " [" +
gamesList[1]->getSourceFileData()->getSystem()->getName() + "]' and '" +
gamesList[2]->metadata.get("name") + " [" +
gamesList[2]->getSourceFileData()->getSystem()->getName() + "]'";
desc += (gameCount == 3 ? "." : ", among others.");
break;
}
}
rootFolder->metadata.set("desc", desc);
rootFolder->metadata.set("rating", rating);
rootFolder->metadata.set("players", players);
rootFolder->metadata.set("genre", genre);
rootFolder->metadata.set("releasedate", releasedate);
rootFolder->metadata.set("developer", developer);
rootFolder->metadata.set("video", video);
rootFolder->metadata.set("thumbnail", thumbnail);
rootFolder->metadata.set("image", image);
}
void CollectionSystemManager::initCustomCollectionSystems()
@ -962,11 +924,10 @@ void CollectionSystemManager::populateCustomCollection(CollectionSystemData* sys
index->addToIndex(newGame);
}
else {
LOG(LogWarning) << "File \"" << gameKey << "\" does not exist, ignoring entry";
LOG(LogWarning) << "File \"" << gameKey <<
"\" does not exist, is hidden, or is not counted as a game, ignoring entry";
}
}
updateCollectionFolderMetadata(newSys);
}
// Functions below to handle System View removal and insertion of collections.

View file

@ -132,6 +132,8 @@ void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color,
}
void GuiCollectionSystemsOptions::createCollection(std::string inName) {
if (CollectionSystemManager::get()->isEditing())
CollectionSystemManager::get()->exitEditMode();
std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName);
SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name);
CollectionSystemManager::get()->saveCustomCollection(newSys);

View file

@ -204,6 +204,14 @@ GuiGamelistOptions::~GuiGamelistOptions()
// Notify that the root folder was sorted (refresh).
getGamelist()->onFileChanged(root, FILE_SORTED);
if (mSystem->isCollection() && mSystem->getFullName() == "collections") {
// Update the custom collections metadata now that we have changed the sorting.
std::vector<FileData*> customCollections = root->getChildren();
for (auto it = customCollections.cbegin(); it != customCollections.cend(); it++)
CollectionSystemManager::get()->
updateCollectionFolderMetadata((*it)->getSystem());
ViewController::get()->reloadGameListView(mSystem);
}
}
// Has the user changed the letter using the quick selector?
@ -270,12 +278,12 @@ void GuiGamelistOptions::openMetaDataEd()
clearGameBtnFunc = [this, file] {
if (file->getType() == FOLDER) {
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder '" <<
file->getFullPath() << "'";
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \"" <<
file->getFullPath() << "\"";
}
else {
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file '" <<
file->getFullPath() << "'";
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \"" <<
file->getFullPath() << "\"";
}
ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file);
@ -298,8 +306,8 @@ void GuiGamelistOptions::openMetaDataEd()
};
deleteGameBtnFunc = [this, file] {
LOG(LogInfo) << "Deleting the game file '" << file->getFullPath() <<
"', all its media files and its gamelist.xml entry.";
LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath() <<
"\", all its media files and its gamelist.xml entry.";
CollectionSystemManager::get()->deleteCollectionFiles(file);
ViewController::get()->getGameListView(
file->getSystem()).get()->removeMedia(file);

View file

@ -362,7 +362,7 @@ void GuiMenu::openUISettings()
defaultSortOrder->add(sort.description, &sort, false);
}
s->addWithLabel("DEFAULT SORT ORDER", defaultSortOrder);
s->addSaveFunc([defaultSortOrder, sortOrder] {
s->addSaveFunc([defaultSortOrder, sortOrder, this] {
std::string selectedSortOrder = defaultSortOrder.get()->getSelected()->description;
if (selectedSortOrder != sortOrder) {
Settings::getInstance()->setString("DefaultSortOrder", selectedSortOrder);
@ -374,11 +374,21 @@ void GuiMenu::openUISettings()
(*it)->sortSystem();
// Update the metadata for any custom collections.
if ((*it)->isCollection() && (*it)->getFullName() == "collections") {
std::vector<FileData*> customCollections =
(*it)->getRootFolder()->getChildren();
for (auto it2 = customCollections.cbegin();
it2 != customCollections.cend(); it2++)
CollectionSystemManager::get()->
updateCollectionFolderMetadata((*it2)->getSystem());
}
// Jump to the first row of the gamelist.
IGameListView* gameList = ViewController::get()->getGameListView((*it)).get();
gameList->setCursor(gameList->getFirstEntry());
}
mWindow->invalidateCachedBackground();
}
});

View file

@ -179,7 +179,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
}
else if (config->isMappedTo("y", input) &&
!UIModeController::getInstance()->isUIModeKid()) {
if (mRoot->getSystem()->isGameSystem()) {
if (mRoot->getSystem()->isGameSystem() &&
getCursor()->getParent()->getPath() != "collections") {
if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER)
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
// When marking or unmarking a game as favorite, don't jump to the new position
@ -201,7 +202,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
if (favoritesSorting && static_cast<std::string>(
mRoot->getSystem()->getName()) != "recent") {
mRoot->getSystem()->getName()) != "recent" &&
!CollectionSystemManager::get()->isEditing()) {
FileData* entryToSelect;
// Add favorite flag.
if (!getCursor()->getFavorite()) {
@ -260,23 +262,29 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
setCursor(entryToSelect);
}
// Marking folders as favorites is only cosmetic as they're not sorted
// differently and they're not part of any collections. So it makes more
// sense to do it here than to add the function to CollectionSystemManager.
// Marking folders as favorites don't make them part of any collections,
// so it makes more sense to handle it here than to add the function to
// CollectionSystemManager.
if (entryToUpdate->getType() == FOLDER) {
GuiInfoPopup* s;
MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata;
if (md->get("favorite") == "false") {
md->set("favorite", "true");
s = new GuiInfoPopup(mWindow, "Marked folder '" +
Utils::String::removeParenthesis(entryToUpdate->getName()) +
"' as favorite", 4000);
if (CollectionSystemManager::get()->isEditing()) {
s = new GuiInfoPopup(mWindow,
"CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS", 4000);
}
else {
md->set("favorite", "false");
s = new GuiInfoPopup(mWindow, "Removed favorite marking for folder '" +
Utils::String::removeParenthesis(entryToUpdate->getName()) +
"'", 4000);
MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata;
if (md->get("favorite") == "false") {
md->set("favorite", "true");
s = new GuiInfoPopup(mWindow, "MARKED FOLDER '" +
Utils::String::toUpper(Utils::String::removeParenthesis(
entryToUpdate->getName())) + "' AS FAVORITE", 4000);
}
else {
md->set("favorite", "false");
s = new GuiInfoPopup(mWindow, "REMOVED FAVORITE MARKING FOR FOLDER '" +
Utils::String::toUpper(Utils::String::removeParenthesis(
entryToUpdate->getName())) + "'", 4000);
}
}
mWindow->setInfoPopup(s);
@ -299,6 +307,13 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
return true;
}
else if (CollectionSystemManager::get()->isEditing() &&
entryToUpdate->metadata.get("nogamecount") == "true") {
GuiInfoPopup* s = new GuiInfoPopup(mWindow,
"CAN'T ADD ENTRIES THAT ARE NOT COUNTED "
"AS GAMES TO CUSTOM COLLECTIONS", 4000);
mWindow->setInfoPopup(s);
}
else if (CollectionSystemManager::get()->toggleGameInCollection(entryToUpdate)) {
// Jump to the first entry in the gamelist if the last favorite was unmarked.
if (foldersOnTop && removedLastFavorite &&

View file

@ -3,7 +3,7 @@
<view name="system">
<text name="info1" extra="true">
<text>Add your own custom collections here.</text>
<text>Find your own custom collections here.</text>
</text>
</view>
</theme>