mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-17 22:55:38 +00:00
Moved quick selector letter index generation to gamelist sorting loop. This increases (non-optimized) sort time with around 1,5 - 4,5% but the game option GUI opens way faster now due to the caching of the letter index. Also made the quick selector more intuitive.
This commit is contained in:
parent
48d70e9f99
commit
7cefe6a2bd
|
@ -310,38 +310,77 @@ void FileData::removeChild(FileData* file)
|
|||
|
||||
void FileData::sort(ComparisonFunction& comparator, bool ascending)
|
||||
{
|
||||
mFirstLetterIndex.clear();
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
|
||||
|
||||
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));
|
||||
// Iterate through any child folders.
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sort(comparator, ascending);
|
||||
}
|
||||
|
||||
// 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();
|
||||
std::vector<FileData*> mChildrenFavorites;
|
||||
std::vector<FileData*> mChildrenOthers;
|
||||
|
||||
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||
if (mChildren[i]->getFavorite())
|
||||
if (mChildren[i]->getFavorite()) {
|
||||
mChildrenFavorites.push_back(mChildren[i]);
|
||||
else
|
||||
}
|
||||
else {
|
||||
mChildrenOthers.push_back(mChildren[i]);
|
||||
// Build mFirstLetterIndex.
|
||||
const char firstChar = toupper(mChildren[i]->getSortName().front());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
}
|
||||
}
|
||||
|
||||
// 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'
|
||||
// collection. Doing this additional work here only for the applicable gamelists is
|
||||
// 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());
|
||||
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 there were at least one favorite in the gamelist, insert the favorite
|
||||
// unicode character in the first position.
|
||||
if (mChildrenOthers.size() > 0 && mChildrenFavorites.size() > 0)
|
||||
mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FAVORITE_CHAR);
|
||||
|
||||
// Sort favorite games and the other games separately.
|
||||
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
||||
|
||||
// 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);
|
||||
|
|
|
@ -57,6 +57,8 @@ public:
|
|||
inline const std::vector<FileData*>& getChildren() const { return mChildren; }
|
||||
inline SystemData* getSystem() const { return mSystem; }
|
||||
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
const std::vector<std::string>& getFirstLetterIndex() const
|
||||
{ return mFirstLetterIndex; };
|
||||
static const std::string getMediaDirectory();
|
||||
virtual const std::string getMediafilePath(
|
||||
std::string subdirectory, std::string mediatype) const;
|
||||
|
@ -87,8 +89,7 @@ public:
|
|||
virtual FileData* getSourceFileData();
|
||||
inline std::string getSystemName() const { return mSystemName; };
|
||||
|
||||
// Returns our best guess at the "real" name for this file
|
||||
// (will attempt to perform MAME name translation).
|
||||
// Returns our best guess at the "real" name for this file.
|
||||
std::string getDisplayName() const;
|
||||
|
||||
// As above, but also remove parenthesis.
|
||||
|
@ -132,6 +133,9 @@ private:
|
|||
std::unordered_map<std::string,FileData*> mChildrenByFilename;
|
||||
std::vector<FileData*> mChildren;
|
||||
std::vector<FileData*> mFilteredChildren;
|
||||
std::vector<std::string> mFirstLetterIndex;
|
||||
|
||||
const std::string FAVORITE_CHAR = "\uF005";
|
||||
};
|
||||
|
||||
class CollectionFileData : public FileData
|
||||
|
|
|
@ -74,14 +74,14 @@ void GuiCollectionSystemsOptions::initializeMenu()
|
|||
|
||||
mMenu.addRow(row);
|
||||
|
||||
bundleCustomCollections = std::make_shared<SwitchComponent>(mWindow);
|
||||
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
|
||||
mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections);
|
||||
|
||||
sortFavFirstCustomSwitch = std::make_shared<SwitchComponent>(mWindow);
|
||||
sortFavFirstCustomSwitch->setState(Settings::getInstance()->getBool("FavFirstCustom"));
|
||||
mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch);
|
||||
|
||||
bundleCustomCollections = std::make_shared<SwitchComponent>(mWindow);
|
||||
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
|
||||
mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections);
|
||||
|
||||
toggleSystemNameInCollections = std::make_shared<SwitchComponent>(mWindow);
|
||||
toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo"));
|
||||
mMenu.addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", toggleSystemNameInCollections);
|
||||
|
|
|
@ -47,79 +47,30 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
mFavoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
||||
|
||||
if (!fromPlaceholder) {
|
||||
// Jump to letter.
|
||||
// Jump to letter quick selector.
|
||||
row.elements.clear();
|
||||
|
||||
// Define supported character range.
|
||||
// This range includes all numbers, capital letters, and most reasonable symbols.
|
||||
char startChar = '!';
|
||||
char endChar = '_';
|
||||
// The letter index is generated in FileData during game system sorting.
|
||||
mFirstLetterIndex = file->getParent()->getFirstLetterIndex();
|
||||
|
||||
char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]);
|
||||
if (curChar < startChar || curChar > endChar)
|
||||
curChar = startChar;
|
||||
// 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);
|
||||
|
||||
if (mFavoritesSorting && system->getName() != "favorites" &&
|
||||
system->getName() != "recent") {
|
||||
// Check whether the first game in the list is a favorite, if it's
|
||||
// not, then there are no favorites currently visible in this gamelist.
|
||||
if (getGamelist()->getCursor()->getParent()->getChildrenListToDisplay()[0]->
|
||||
getFavorite()) {
|
||||
if (getGamelist()->getCursor()->getFavorite())
|
||||
mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 1);
|
||||
else
|
||||
mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 0);
|
||||
}
|
||||
// Populate the quick selector.
|
||||
for (unsigned int i = 0; i < mFirstLetterIndex.size(); i++) {
|
||||
mJumpToLetterList->add(mFirstLetterIndex[i], mFirstLetterIndex[i], 0);
|
||||
if (mFirstLetterIndex[i] == mCurrentFirstCharacter)
|
||||
mJumpToLetterList->selectEntry(i);
|
||||
}
|
||||
|
||||
for (char c = startChar; c <= endChar; c++) {
|
||||
// Check if c is a valid first letter in the current list.
|
||||
const std::vector<FileData*>& files =
|
||||
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
for (auto file : files) {
|
||||
char candidate = (char)toupper(file->getSortName()[0]);
|
||||
if (c == candidate) {
|
||||
// If the game is a favorite, continue to the next entry in the list.
|
||||
if (mFavoritesSorting && system->getName() != "favorites" &&
|
||||
system->getName() != "recent" && file->getFavorite())
|
||||
continue;
|
||||
|
||||
// If the currently selected game is a favorite, set the character
|
||||
// as not selected so we don't get two current positions.
|
||||
if (mFavoritesSorting && system->getName() != "favorites" &&
|
||||
system->getName() != "recent" &&
|
||||
getGamelist()->getCursor()->getFavorite())
|
||||
mJumpToLetterList->add(std::string(1, c), std::string(1, c), 0);
|
||||
else
|
||||
mJumpToLetterList->add(std::string(1, c), std::string(1, c), c == curChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "JUMP TO...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(mJumpToLetterList, false);
|
||||
row.input_handler = [&](InputConfig* config, Input input) {
|
||||
if (config->isMappedTo("a", input) && input.value) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
if (mJumpToLetterList->getSelected() == FAVORITE_CHAR)
|
||||
jumpToFirstRow();
|
||||
else
|
||||
jumpToLetter();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (mJumpToLetterList->input(config, input)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (system->getName() != "recent")
|
||||
mMenu.addRow(row);
|
||||
mMenu.addWithLabel("JUMP TO..", mJumpToLetterList);
|
||||
|
||||
// Sort list by selected sort type (persistent throughout the program session).
|
||||
mListSort = std::make_shared<SortList>(mWindow, getHelpStyle(), "SORT GAMES BY", false);
|
||||
|
@ -201,26 +152,30 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
|
||||
// If a new sorting type was selected, then sort and update mSortTypeString for the system.
|
||||
if ((*mListSort->getSelected()).description != root->getSortTypeString()) {
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
// This will also recursively sort children.
|
||||
root->sort(*mListSort->getSelected(), mFavoritesSorting);
|
||||
root->setSortTypeString((*mListSort->getSelected()).description);
|
||||
|
||||
// Select the first row of the gamelist.
|
||||
FileData* firstRow = getGamelist()->getCursor()->getParent()->
|
||||
getChildrenListToDisplay()[0];
|
||||
getGamelist()->setCursor(firstRow);
|
||||
|
||||
// Notify that the root folder was sorted.
|
||||
// Notify that the root folder was sorted (refresh).
|
||||
getGamelist()->onFileChanged(root, FILE_SORTED);
|
||||
}
|
||||
|
||||
// Has the user changed the letter using the quick selector?
|
||||
if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) {
|
||||
if (mJumpToLetterList->getSelected() == FAVORITE_CHAR)
|
||||
jumpToFirstRow();
|
||||
else
|
||||
jumpToLetter();
|
||||
}
|
||||
}
|
||||
|
||||
if (mFiltersChanged) {
|
||||
// Only reload full view if we came from a placeholder as we need to
|
||||
// re-display the remaining elements for whatever new game is selected.
|
||||
ViewController::get()->reloadGameListView(mSystem);
|
||||
}
|
||||
|
||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::openGamelistFilter()
|
||||
|
@ -283,29 +238,27 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
|
||||
void GuiGamelistOptions::jumpToLetter()
|
||||
{
|
||||
char letter = mJumpToLetterList->getSelected()[0];
|
||||
char letter = mJumpToLetterList->getSelected().front();
|
||||
|
||||
// Get first row of the gamelist.
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
|
||||
for (unsigned int i = 0; i < files.size(); i++) {
|
||||
if (mFavoritesSorting && mSystem->getName() != "favorites") {
|
||||
if ((char)toupper(files.at(i)->getSortName()[0]) ==
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((char)toupper(files.at(i)->getSortName()[0]) == letter) {
|
||||
if ((char)toupper(files.at(i)->getSortName().front()) == letter) {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::jumpToFirstRow()
|
||||
|
@ -314,8 +267,6 @@ void GuiGamelistOptions::jumpToFirstRow()
|
|||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
getGamelist()->setCursor(files.at(0));
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool GuiGamelistOptions::input(InputConfig* config, Input input)
|
||||
|
|
|
@ -40,8 +40,6 @@ private:
|
|||
void jumpToLetter();
|
||||
void jumpToFirstRow();
|
||||
|
||||
const std::string FAVORITE_CHAR = "\uF005";
|
||||
|
||||
MenuComponent mMenu;
|
||||
|
||||
typedef OptionListComponent<std::string> LetterList;
|
||||
|
@ -56,6 +54,10 @@ private:
|
|||
bool fromPlaceholder;
|
||||
bool mFiltersChanged;
|
||||
bool mCancelled;
|
||||
std::vector<std::string> mFirstLetterIndex;
|
||||
std::string mCurrentFirstCharacter;
|
||||
const std::string FAVORITE_CHAR = "\uF005";
|
||||
|
||||
};
|
||||
|
||||
#endif // ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H
|
||||
|
|
Loading…
Reference in a new issue