Improved handling and sorting of folders.

This commit is contained in:
Leon Styhre 2020-09-20 20:25:32 +02:00
parent 2b82770e43
commit e56fdf3df6
4 changed files with 99 additions and 23 deletions

View file

@ -151,6 +151,17 @@ const std::vector<FileData*> FileData::getChildrenRecursive() const
return childrenRecursive; return childrenRecursive;
} }
bool FileData::viewHasOnlyFolders()
{
bool onlyFolders = true;
std::vector<FileData*> entrySiblings = this->getParent()->getChildren();
for (auto it = entrySiblings.cbegin(); it != entrySiblings.cend(); it++) {
if ((*it)->getType() != FOLDER)
onlyFolders = false;
}
return onlyFolders;
}
const std::string FileData::getROMDirectory() const std::string FileData::getROMDirectory()
{ {
std::string romDirSetting = Settings::getInstance()->getString("ROMDirectory"); std::string romDirSetting = Settings::getInstance()->getString("ROMDirectory");
@ -463,6 +474,13 @@ void FileData::sort(ComparisonFunction& comparator, bool ascending)
} }
} }
// If descending sorting is requested, always perform a ascending sort by filename first.
// This adds a slight (probably negligible) overhead but it will avoid strange sorting
// issues where the secondary sorting is reversed for some sort types.
if (!ascending)
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
if (foldersOnTop && mOnlyFolders) if (foldersOnTop && mOnlyFolders)
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator); std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator); std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
@ -479,6 +497,10 @@ void FileData::sort(ComparisonFunction& comparator, bool ascending)
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end()); mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());
} }
else { else {
if (!ascending)
std::stable_sort(mChildren.begin(), mChildren.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildren.begin(), mChildren.end(), comparator); std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
if (!ascending) if (!ascending)
std::reverse(mChildren.begin(), mChildren.end()); std::reverse(mChildren.begin(), mChildren.end());
@ -523,6 +545,7 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
mFirstLetterIndex.clear(); mFirstLetterIndex.clear();
mOnlyFolders = true; mOnlyFolders = true;
std::vector<FileData*> mChildrenFolders; std::vector<FileData*> mChildrenFolders;
std::vector<FileData*> mChildrenFavoritesFolders;
std::vector<FileData*> mChildrenFavorites; std::vector<FileData*> mChildrenFavorites;
std::vector<FileData*> mChildrenOthers; std::vector<FileData*> mChildrenOthers;
bool showHiddenGames = Settings::getInstance()->getBool("ShowHiddenGames"); bool showHiddenGames = Settings::getInstance()->getBool("ShowHiddenGames");
@ -538,7 +561,11 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
} }
if (foldersOnTop && mChildren[i]->getType() == FOLDER) { if (foldersOnTop && mChildren[i]->getType() == FOLDER) {
mChildrenFolders.push_back(mChildren[i]); if (!mChildren[i]->getFavorite())
mChildrenFolders.push_back(mChildren[i]);
else
mChildrenFavoritesFolders.push_back(mChildren[i]);
hasFolders = true; hasFolders = true;
} }
else if (mChildren[i]->getFavorite()) { else if (mChildren[i]->getFavorite()) {
@ -555,6 +582,19 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
mOnlyFolders = false; mOnlyFolders = false;
} }
// If there are favorite folders and this is a mixed list, then don't handle these
// separately but instead merge them into the same vector. This is a quite wasteful
// approach but the scenario where a user has a mixed folder and files list and marks
// some folders as favorites is probably a rare situation.
if (!mOnlyFolders && mChildrenFavoritesFolders.size() > 0) {
mChildrenFolders.insert(mChildrenFolders.end(), mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end());
mChildrenFavoritesFolders.erase(mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end());
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
}
// If there are only favorites in the gamelist, it makes sense to still generate // If there are only favorites in the gamelist, it makes sense to still generate
// a letter index. For instance to be able to quick jump in the 'favorites' // a letter index. For instance to be able to quick jump in the 'favorites'
// collection. Doing this additional work here only for the applicable gamelists is // collection. Doing this additional work here only for the applicable gamelists is
@ -584,49 +624,70 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
auto last = std::unique(mFirstLetterIndex.begin(), mFirstLetterIndex.end()); auto last = std::unique(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
mFirstLetterIndex.erase(last, mFirstLetterIndex.end()); mFirstLetterIndex.erase(last, mFirstLetterIndex.end());
// If there were at least one favorite folder in the gamelist, insert the favorite
// unicode character in the first position.
if (foldersOnTop && mOnlyFolders && mChildrenFavoritesFolders.size() > 0)
mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FAVORITE_CHAR);
// If there were at least one favorite in the gamelist, insert the favorite // If there were at least one favorite in the gamelist, insert the favorite
// unicode character in the first position. // unicode character in the first position.
if (mChildrenOthers.size() > 0 && mChildrenFavorites.size() > 0) else if (mChildrenOthers.size() > 0 && mChildrenFavorites.size() > 0)
mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FAVORITE_CHAR); mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FAVORITE_CHAR);
// If it's a mixed list and folders are sorted on top, add a folder icon to the index. // If it's a mixed list and folders are sorted on top, add a folder icon to the index.
if (foldersOnTop && hasFolders && !mOnlyFolders) if (foldersOnTop && hasFolders && !mOnlyFolders)
mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FOLDER_CHAR); mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FOLDER_CHAR);
// If descending sorting is requested, always perform a ascending sort by filename first.
// This adds a slight (probably negligible) overhead but it will avoid strange sorting
// issues where the secondary sorting is reversed for some sort types.
if (!ascending) {
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(),
getSortTypeFromString("filename, ascending").comparisonFunction);
}
// Sort favorite games and the other games separately. // Sort favorite games and the other games separately.
if (foldersOnTop && mOnlyFolders) if (foldersOnTop && mOnlyFolders) {
std::stable_sort(mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end(), comparator);
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator); std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
}
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator); std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator); std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
// Iterate through any child favorite folders.
for (auto it = mChildrenFavoritesFolders.cbegin(); it !=
mChildrenFavoritesFolders.cend(); it++) {
if ((*it)->getChildren().size() > 0)
(*it)->sortFavoritesOnTop(comparator, ascending);
}
// Iterate through any child folders. // Iterate through any child folders.
for (auto it = mChildrenFolders.cbegin(); it != mChildrenFolders.cend(); it++) { for (auto it = mChildrenFolders.cbegin(); it != mChildrenFolders.cend(); it++) {
if ((*it)->getChildren().size() > 0) if ((*it)->getChildren().size() > 0)
(*it)->sortFavoritesOnTop(comparator, ascending); (*it)->sortFavoritesOnTop(comparator, ascending);
} }
// Iterate through any child folders.
for (auto it = mChildrenFavorites.cbegin(); it != mChildrenFavorites.cend(); it++) {
if ((*it)->getChildren().size() > 0)
(*it)->sortFavoritesOnTop(comparator, ascending);
}
// Iterate through any child folders.
for (auto it = mChildrenOthers.cbegin(); it != mChildrenOthers.cend(); it++) {
if ((*it)->getChildren().size() > 0)
(*it)->sortFavoritesOnTop(comparator, ascending);
}
if (!ascending) { if (!ascending) {
if (foldersOnTop && mOnlyFolders) if (foldersOnTop && mOnlyFolders) {
std::reverse(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end());
std::reverse(mChildrenFolders.begin(), mChildrenFolders.end()); std::reverse(mChildrenFolders.begin(), mChildrenFolders.end());
}
std::reverse(mChildrenFavorites.begin(), mChildrenFavorites.end()); std::reverse(mChildrenFavorites.begin(), mChildrenFavorites.end());
std::reverse(mChildrenOthers.begin(), mChildrenOthers.end()); std::reverse(mChildrenOthers.begin(), mChildrenOthers.end());
} }
// Combine the individually sorted favorite games and other games vectors. // Combine the individually sorted favorite games and other games vectors.
mChildren.erase(mChildren.begin(), mChildren.end()); mChildren.erase(mChildren.begin(), mChildren.end());
mChildren.reserve(mChildrenFolders.size() + mChildrenFavorites.size() + mChildrenOthers.size()); mChildren.reserve(mChildrenFavoritesFolders.size() + mChildrenFolders.size() +
mChildrenFavorites.size() + mChildrenOthers.size());
mChildren.insert(mChildren.end(), mChildrenFavoritesFolders.begin(),
mChildrenFavoritesFolders.end());
mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end()); mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end());
mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end()); mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end());
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end()); mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());

View file

@ -64,6 +64,7 @@ public:
const std::vector<std::string>& getFirstLetterIndex() const const std::vector<std::string>& getFirstLetterIndex() const
{ return mFirstLetterIndex; }; { return mFirstLetterIndex; };
const bool getOnlyFoldersFlag() { return mOnlyFolders; } const bool getOnlyFoldersFlag() { return mOnlyFolders; }
bool viewHasOnlyFolders();
static const std::string getROMDirectory(); static const std::string getROMDirectory();
static const std::string getMediaDirectory(); static const std::string getMediaDirectory();
const std::string getMediafilePath(std::string subdirectory, std::string mediatype) const; const std::string getMediafilePath(std::string subdirectory, std::string mediatype) const;

View file

@ -339,7 +339,7 @@ void GuiGamelistOptions::jumpToLetter()
if (mFavoritesSorting && mFirstLetterIndex.front() == FAVORITE_CHAR) { if (mFavoritesSorting && mFirstLetterIndex.front() == FAVORITE_CHAR) {
if ((char)toupper(files.at(i)->getSortName().front()) == if ((char)toupper(files.at(i)->getSortName().front()) ==
letter && !files.at(i)->getFavorite()) { letter && !files.at(i)->getFavorite()) {
if (mFoldersOnTop && files.at(i)->getType() == FOLDER) { if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) {
continue; continue;
} }
else { else {
@ -368,9 +368,13 @@ void GuiGamelistOptions::jumpToFirstRow()
// Get the gamelist. // Get the gamelist.
const std::vector<FileData*>& files = getGamelist()->getCursor()-> const std::vector<FileData*>& files = getGamelist()->getCursor()->
getParent()->getChildrenListToDisplay(); getParent()->getChildrenListToDisplay();
// Select the first game that is not a folder. // Select the first game that is not a folder, unless it's a folder-only list in
// which case the first line overall is selected.
for (auto it = files.cbegin(); it != files.cend(); it++) { for (auto it = files.cbegin(); it != files.cend(); it++) {
if ((*it)->getType() == GAME) { if (!mOnlyHasFolders && mFoldersOnTop && (*it)->getType() == FOLDER) {
continue;
}
else {
getGamelist()->setCursor(*it); getGamelist()->setCursor(*it);
break; break;
} }

View file

@ -189,6 +189,11 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
bool favoritesSorting; bool favoritesSorting;
bool removedLastFavorite = false; bool removedLastFavorite = false;
bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop"); bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
// If the current list only contains folders, then treat it as if the folders
// are not sorted on top, this way the logic should work exactly as for mixed
// lists or files-only lists.
if (getCursor()->getType() == FOLDER && foldersOnTop == true)
foldersOnTop = !getCursor()->viewHasOnlyFolders();
if (CollectionSystemManager::get()->getIsCustomCollection(mRoot->getSystem())) if (CollectionSystemManager::get()->getIsCustomCollection(mRoot->getSystem()))
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
@ -208,6 +213,11 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
// If we are on the favorite marking boundary, select the next entry. // If we are on the favorite marking boundary, select the next entry.
else if (getCursor()->getFavorite() != getPreviousEntry()->getFavorite()) else if (getCursor()->getFavorite() != getPreviousEntry()->getFavorite())
entryToSelect = getNextEntry(); entryToSelect = getNextEntry();
// If we mark the second entry as favorite and the first entry is not a
// favorite, then select this entry if they are of the same type.
else if (getPreviousEntry() == getFirstEntry() &&
getCursor()->getType() == getPreviousEntry()->getType())
entryToSelect = getPreviousEntry();
// For all other scenarios try to select the next entry, and if it doesn't // For all other scenarios try to select the next entry, and if it doesn't
// exist, select the previous entry. // exist, select the previous entry.
else else
@ -271,9 +281,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
mWindow->setInfoPopup(s); mWindow->setInfoPopup(s);
entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint(); entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint();
if (!Settings::getInstance()->getBool("FoldersOnTop")) getCursor()->getParent()->sort(
mRoot->sort(mRoot->getSortTypeFromString(mRoot->getSortTypeString()), mRoot->getSortTypeFromString(mRoot->getSortTypeString()),
Settings::getInstance()->getBool("FavoritesFirst")); Settings::getInstance()->getBool("FavoritesFirst"));
ViewController::get()->onFileChanged(getCursor(), FILE_METADATA_CHANGED); ViewController::get()->onFileChanged(getCursor(), FILE_METADATA_CHANGED);