mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-21 21:55:38 +00:00
Added option to sort folders on top of the gamelists.
This commit is contained in:
parent
ea59d9f9c4
commit
b60c86e40f
5
NEWS.md
5
NEWS.md
|
@ -35,7 +35,8 @@ Many bugs have been fixed, and numerous features that were only partially implem
|
|||
* Custom event scripts can now be enabled or disabled with a menu option
|
||||
* Help system updated and expanded to the complete application (previously it was only partially implemented)
|
||||
* Improved input device configuration, and default keyboard mappings are now applied if the keyboard has not been configured by the user
|
||||
* GUI-configurable option to sort favorite games on the top of the game lists (favorites marked with stars)
|
||||
* GUI-configurable option to sort favorite games above non-favorite games (favorites marked with stars)
|
||||
* GUI-configurable option to sort folders on top of the gamelists
|
||||
* Added new component GuiComplexTextEditPopup to handle changes to configuration file entries and similar
|
||||
* Speed improvements and optimizations, the application now starts faster and feels more responsive
|
||||
* Moved all resources to a subdirectory structure and enabled the CMake install prefix variable to generate the resources search path
|
||||
|
@ -65,7 +66,7 @@ Many bugs have been fixed, and numerous features that were only partially implem
|
|||
* The random system selection did not consider the currently selected system
|
||||
* The random game selection did not consider the currently selected game
|
||||
* The random game selection traversed folders, i.e. a game could be selected inside a subdirectory and vice versa
|
||||
* Deleting a game did not delete the game media files or its entry in the gamelist.xml file
|
||||
* Deleting a game from the metadata editor did not delete the game media files or the entry in the gamelist.xml file
|
||||
* SystemView didn't properly loop the systems if only two systems were available
|
||||
* Hidden files still showed up if they had a gamelist.xml entry
|
||||
* On Unix, adding a hidden folder with a game in it crashed the application on startup
|
||||
|
|
|
@ -37,6 +37,7 @@ FileData::FileData(
|
|||
mEnvData(envData),
|
||||
mSourceFileData(nullptr),
|
||||
mParent(nullptr),
|
||||
mOnlyFolders(false),
|
||||
mDeletionFlag(false),
|
||||
// Metadata is REALLY set in the constructor!
|
||||
metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
|
||||
|
@ -368,6 +369,10 @@ void FileData::removeChild(FileData* file)
|
|||
void FileData::sort(ComparisonFunction& comparator, bool ascending)
|
||||
{
|
||||
mFirstLetterIndex.clear();
|
||||
mOnlyFolders = false;
|
||||
bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
|
||||
std::vector<FileData*> mChildrenFolders;
|
||||
std::vector<FileData*> mChildrenOthers;
|
||||
|
||||
// Only run this section of code if the setting to show hidden games has been disabled,
|
||||
// in order to avoid unnecessary processing.
|
||||
|
@ -386,32 +391,69 @@ void FileData::sort(ComparisonFunction& comparator, bool ascending)
|
|||
mChildren.insert(mChildren.end(), mChildrenShown.begin(), mChildrenShown.end());
|
||||
}
|
||||
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
|
||||
if (foldersOnTop) {
|
||||
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||
if (mChildren[i]->getType() == FOLDER)
|
||||
mChildrenFolders.push_back(mChildren[i]);
|
||||
else
|
||||
mChildrenOthers.push_back(mChildren[i]);
|
||||
}
|
||||
|
||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
||||
|
||||
if (!ascending) {
|
||||
std::reverse(mChildrenFolders.begin(), mChildrenFolders.end());
|
||||
std::reverse(mChildrenOthers.begin(), mChildrenOthers.end());
|
||||
}
|
||||
|
||||
mChildren.erase(mChildren.begin(), mChildren.end());
|
||||
mChildren.reserve(mChildrenFolders.size() + mChildrenOthers.size());
|
||||
mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end());
|
||||
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());
|
||||
}
|
||||
else {
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
|
||||
if (!ascending)
|
||||
std::reverse(mChildren.begin(), mChildren.end());
|
||||
}
|
||||
|
||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
||||
// Build mFirstLetterIndex.
|
||||
const char firstChar = toupper((*it)->getSortName().front());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
if (!(foldersOnTop && (*it)->getType() == FOLDER)) {
|
||||
// Build mFirstLetterIndex.
|
||||
const char firstChar = toupper((*it)->getSortName().front());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
}
|
||||
// Iterate through any child folders.
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sort(comparator, ascending);
|
||||
}
|
||||
|
||||
// If there are only folders in the gamelist, then it makes sense to still
|
||||
// generate a letter index.
|
||||
if (mChildrenOthers.size() == 0 && mChildrenFolders.size() > 0) {
|
||||
for (unsigned int i = 0; i < mChildrenFolders.size(); i++) {
|
||||
const char firstChar = toupper(mChildrenFolders[i]->getSortName().front());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
}
|
||||
mOnlyFolders = true;
|
||||
}
|
||||
|
||||
// Sort and make each entry unique in mFirstLetterIndex.
|
||||
std::sort(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
|
||||
auto last = std::unique(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
|
||||
mFirstLetterIndex.erase(last, mFirstLetterIndex.end());
|
||||
|
||||
if (!ascending)
|
||||
std::reverse(mChildren.begin(), mChildren.end());
|
||||
}
|
||||
|
||||
void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending)
|
||||
{
|
||||
mFirstLetterIndex.clear();
|
||||
mOnlyFolders = false;
|
||||
std::vector<FileData*> mChildrenFolders;
|
||||
std::vector<FileData*> mChildrenFavorites;
|
||||
std::vector<FileData*> mChildrenOthers;
|
||||
bool showHiddenGames = Settings::getInstance()->getBool("ShowHiddenGames");
|
||||
bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
|
||||
|
||||
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||
// Exclude game if it's marked as hidden and the hide setting has been set.
|
||||
|
@ -421,7 +463,10 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
|
|||
continue;
|
||||
}
|
||||
|
||||
if (mChildren[i]->getFavorite()) {
|
||||
if (foldersOnTop && mChildren[i]->getType() == FOLDER) {
|
||||
mChildrenFolders.push_back(mChildren[i]);
|
||||
}
|
||||
else if (mChildren[i]->getFavorite()) {
|
||||
mChildrenFavorites.push_back(mChildren[i]);
|
||||
}
|
||||
else {
|
||||
|
@ -438,9 +483,24 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
|
|||
// probably faster than building a redundant index for all gamelists during sorting.
|
||||
if (mChildrenOthers.size() == 0 && mChildrenFavorites.size() > 0) {
|
||||
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||
const char firstChar = toupper(mChildren[i]->getSortName().front());
|
||||
if (foldersOnTop && mChildren[i]->getType() == FOLDER) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
const char firstChar = toupper(mChildren[i]->getSortName().front());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are only folders in the gamelist, then it also makes sense to generate
|
||||
// a letter index.
|
||||
else if (mChildrenOthers.size() == 0 && mChildrenFavorites.size() == 0 &&
|
||||
mChildrenFolders.size() > 0) {
|
||||
for (unsigned int i = 0; i < mChildrenFolders.size(); i++) {
|
||||
const char firstChar = toupper(mChildrenFolders[i]->getSortName().front());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
}
|
||||
mOnlyFolders = true;
|
||||
}
|
||||
|
||||
// Sort and make each entry unique in mFirstLetterIndex.
|
||||
|
@ -454,9 +514,16 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
|
|||
mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FAVORITE_CHAR);
|
||||
|
||||
// Sort favorite games and the other games separately.
|
||||
std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator);
|
||||
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
||||
|
||||
// Iterate through any child folders.
|
||||
for (auto it = mChildrenFolders.cbegin(); it != mChildrenFolders.cend(); it++) {
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sortFavoritesOnTop(comparator, ascending);
|
||||
}
|
||||
|
||||
// Iterate through any child folders.
|
||||
for (auto it = mChildrenFavorites.cbegin(); it != mChildrenFavorites.cend(); it++) {
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
|
@ -470,13 +537,15 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending
|
|||
}
|
||||
|
||||
if (!ascending) {
|
||||
std::reverse(mChildrenFolders.begin(), mChildrenFolders.end());
|
||||
std::reverse(mChildrenFavorites.begin(), mChildrenFavorites.end());
|
||||
std::reverse(mChildrenOthers.begin(), mChildrenOthers.end());
|
||||
}
|
||||
|
||||
// Combine the individually sorted favorite games and other games vectors.
|
||||
mChildren.erase(mChildren.begin(), mChildren.end());
|
||||
mChildren.reserve(mChildrenFavorites.size() + mChildrenOthers.size());
|
||||
mChildren.reserve(mChildrenFolders.size() + mChildrenFavorites.size() + mChildrenOthers.size());
|
||||
mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end());
|
||||
mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end());
|
||||
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
const std::vector<std::string>& getFirstLetterIndex() const
|
||||
{ return mFirstLetterIndex; };
|
||||
const bool getOnlyFoldersFlag() { return mOnlyFolders; }
|
||||
static const std::string getROMDirectory();
|
||||
static const std::string getMediaDirectory();
|
||||
const std::string getMediafilePath(std::string subdirectory, std::string mediatype) const;
|
||||
|
@ -137,6 +138,7 @@ private:
|
|||
std::vector<FileData*> mChildren;
|
||||
std::vector<FileData*> mFilteredChildren;
|
||||
std::vector<std::string> mFirstLetterIndex;
|
||||
bool mOnlyFolders;
|
||||
// Used for flagging a game for deletion from its gamelist.xml file.
|
||||
bool mDeletionFlag;
|
||||
|
||||
|
|
|
@ -39,6 +39,11 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
fromPlaceholder = file->isPlaceHolder();
|
||||
ComponentListRow row;
|
||||
|
||||
// Read the setting for whether folders are sorted on top of the gamelists.
|
||||
// Also check if the gamelist only contains folders, as generated by the FileData sorting.
|
||||
mFoldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
|
||||
mOnlyHasFolders = file->getParent()->getOnlyFoldersFlag();
|
||||
|
||||
// Read the applicable favorite sorting setting depending on whether the
|
||||
// system is a custom collection or not.
|
||||
if (CollectionSystemManager::get()->getIsCustomCollection(file->getSystem()))
|
||||
|
@ -53,11 +58,21 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
// The letter index is generated in FileData during gamelist sorting.
|
||||
mFirstLetterIndex = file->getParent()->getFirstLetterIndex();
|
||||
|
||||
// Set the quick selector to the first character of the selected game.
|
||||
if (mFavoritesSorting && file->getFavorite() && mFirstLetterIndex.front() == FAVORITE_CHAR)
|
||||
mCurrentFirstCharacter = FAVORITE_CHAR;
|
||||
else
|
||||
mCurrentFirstCharacter = toupper(file->getSortName().front());
|
||||
// Don't include the folder name starting characters if folders are sorted on top
|
||||
// unless the list only contains folders.
|
||||
if (!mOnlyHasFolders && mFoldersOnTop && file->getType() == FOLDER) {
|
||||
mCurrentFirstCharacter = mFirstLetterIndex.front();
|
||||
}
|
||||
else {
|
||||
// Set the quick selector to the first character of the selected game.
|
||||
if (mFavoritesSorting && file->getFavorite() &&
|
||||
mFirstLetterIndex.front() == FAVORITE_CHAR) {
|
||||
mCurrentFirstCharacter = FAVORITE_CHAR;
|
||||
}
|
||||
else {
|
||||
mCurrentFirstCharacter = toupper(file->getSortName().front());
|
||||
}
|
||||
}
|
||||
|
||||
mJumpToLetterList = std::make_shared<LetterList>(mWindow, getHelpStyle(),
|
||||
"JUMP TO...", false);
|
||||
|
@ -178,7 +193,8 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
}
|
||||
|
||||
// Has the user changed the letter using the quick selector?
|
||||
if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) {
|
||||
if ((mFoldersOnTop && getGamelist()->getCursor()->getType() == FOLDER) ||
|
||||
mCurrentFirstCharacter != mJumpToLetterList->getSelected()) {
|
||||
if (mJumpToLetterList->getSelected() == FAVORITE_CHAR)
|
||||
jumpToFirstRow();
|
||||
else
|
||||
|
@ -275,14 +291,24 @@ void GuiGamelistOptions::jumpToLetter()
|
|||
if (mFavoritesSorting && mFirstLetterIndex.front() == FAVORITE_CHAR) {
|
||||
if ((char)toupper(files.at(i)->getSortName().front()) ==
|
||||
letter && !files.at(i)->getFavorite()) {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
if (mFoldersOnTop && files.at(i)->getType() == FOLDER) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((char)toupper(files.at(i)->getSortName().front()) == letter) {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -290,8 +316,22 @@ void GuiGamelistOptions::jumpToLetter()
|
|||
|
||||
void GuiGamelistOptions::jumpToFirstRow()
|
||||
{
|
||||
// Get first row of the gamelist.
|
||||
getGamelist()->setCursor(getGamelist()->getFirstEntry());
|
||||
if (mFoldersOnTop) {
|
||||
// Get the gamelist.
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
// Select the first game that is not a folder.
|
||||
for (auto it = files.cbegin(); it != files.cend(); it++) {
|
||||
if ((*it)->getType() == GAME) {
|
||||
getGamelist()->setCursor(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Get first row of the gamelist.
|
||||
getGamelist()->setCursor(getGamelist()->getFirstEntry());
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiGamelistOptions::input(InputConfig* config, Input input)
|
||||
|
|
|
@ -50,7 +50,9 @@ private:
|
|||
|
||||
SystemData* mSystem;
|
||||
IGameListView* getGamelist();
|
||||
bool mFoldersOnTop;
|
||||
bool mFavoritesSorting;
|
||||
bool mOnlyHasFolders;
|
||||
bool fromPlaceholder;
|
||||
bool mFiltersChanged;
|
||||
bool mCancelled;
|
||||
|
|
|
@ -388,12 +388,35 @@ void GuiMenu::openUISettings()
|
|||
}
|
||||
});
|
||||
|
||||
// Sort favorites on top of the gamelists.
|
||||
auto favoritesFirstSwitch = std::make_shared<SwitchComponent>(mWindow);
|
||||
favoritesFirstSwitch->setState(Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
s->addWithLabel("SORT FAVORITES ON TOP OF GAMELISTS", favoritesFirstSwitch);
|
||||
s->addSaveFunc([favoritesFirstSwitch] {
|
||||
if (Settings::getInstance()->setBool("FavoritesFirst", favoritesFirstSwitch->getState()))
|
||||
// Sort folders on top of the gamelists.
|
||||
auto folders_on_top = std::make_shared<SwitchComponent>(mWindow);
|
||||
folders_on_top->setState(Settings::getInstance()->getBool("FoldersOnTop"));
|
||||
s->addWithLabel("SORT FOLDERS ON TOP OF GAMELISTS", folders_on_top);
|
||||
s->addSaveFunc([folders_on_top] {
|
||||
if (Settings::getInstance()->setBool("FoldersOnTop", folders_on_top->getState()))
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it !=
|
||||
SystemData::sSystemVector.cend(); it++) {
|
||||
|
||||
if ((*it)->isCollection())
|
||||
continue;
|
||||
|
||||
FileData* rootFolder = (*it)->getRootFolder();
|
||||
rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
ViewController::get()->reloadGameListView(*it);
|
||||
|
||||
// Jump to the first row of the gamelist.
|
||||
IGameListView* gameList = ViewController::get()->getGameListView((*it)).get();
|
||||
gameList->setCursor(gameList->getFirstEntry());
|
||||
}
|
||||
});
|
||||
|
||||
// Sort favorites on top of non-favorites in the gamelists.
|
||||
auto favorites_first = std::make_shared<SwitchComponent>(mWindow);
|
||||
favorites_first->setState(Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
s->addWithLabel("SORT FAVORITE GAMES ABOVE NON-FAVORITES", favorites_first);
|
||||
s->addSaveFunc([favorites_first] {
|
||||
if (Settings::getInstance()->setBool("FavoritesFirst", favorites_first->getState()))
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it !=
|
||||
SystemData::sSystemVector.cend(); it++) {
|
||||
// The favorites and recent gamelists never sort favorites on top.
|
||||
|
|
|
@ -84,6 +84,7 @@ void Settings::setDefaults()
|
|||
mStringMap["ThemeSet"] = "";
|
||||
mStringMap["UIMode"] = "full";
|
||||
mStringMap["DefaultSortOrder"] = "filename, ascending";
|
||||
mBoolMap["FoldersOnTop"] = true;
|
||||
mBoolMap["FavoritesFirst"] = true;
|
||||
mBoolMap["ForceDisableFilters"] = false;
|
||||
mBoolMap["QuickSystemSelect"] = true;
|
||||
|
|
Loading…
Reference in a new issue