When marking or unmarking a game as favorite, the cursor position is now retained.

This commit is contained in:
Leon Styhre 2020-09-20 10:05:03 +02:00
parent fd92f7f86d
commit 83bae1e963
7 changed files with 170 additions and 10 deletions

View file

@ -1,4 +1,6 @@
// SPDX-License-Identifier: MIT
// //
// EmulationStation Desktop Edition
// BasicGameListView.cpp // BasicGameListView.cpp
// //
// Interface that defines a GameListView of the type 'Basic'. // Interface that defines a GameListView of the type 'Basic'.
@ -46,10 +48,15 @@ void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
void BasicGameListView::populateList(const std::vector<FileData*>& files) void BasicGameListView::populateList(const std::vector<FileData*>& files)
{ {
firstGameEntry = nullptr;
mList.clear(); mList.clear();
mHeaderText.setText(mRoot->getSystem()->getFullName()); mHeaderText.setText(mRoot->getSystem()->getFullName());
if (files.size() > 0) { if (files.size() > 0) {
for (auto it = files.cbegin(); it != files.cend(); it++) { for (auto it = files.cbegin(); it != files.cend(); it++) {
if (!firstGameEntry && (*it)->getType() == GAME)
firstGameEntry = (*it);
if ((*it)->getFavorite() && if ((*it)->getFavorite() &&
mRoot->getSystem()->getName() != "favorites") { mRoot->getSystem()->getName() != "favorites") {
mList.add(FAVORITE_GAME_CHAR + " " + (*it)->getName(), mList.add(FAVORITE_GAME_CHAR + " " + (*it)->getName(),
@ -100,6 +107,16 @@ void BasicGameListView::setCursor(FileData* cursor)
} }
} }
FileData* BasicGameListView::getNextEntry()
{
return mList.getNext();
}
FileData* BasicGameListView::getPreviousEntry()
{
return mList.getPrevious();
}
FileData* BasicGameListView::getFirstEntry() FileData* BasicGameListView::getFirstEntry()
{ {
return mList.getFirst(); return mList.getFirst();
@ -110,6 +127,11 @@ FileData* BasicGameListView::getLastEntry()
return mList.getLast(); return mList.getLast();
} }
FileData* BasicGameListView::getFirstGameEntry()
{
return firstGameEntry;
}
void BasicGameListView::addPlaceholder() void BasicGameListView::addPlaceholder()
{ {
// Empty list - add a placeholder. // Empty list - add a placeholder.

View file

@ -1,4 +1,6 @@
// SPDX-License-Identifier: MIT
// //
// EmulationStation Desktop Edition
// BasicGameListView.h // BasicGameListView.h
// //
// Interface that defines a GameListView of the type 'basic'. // Interface that defines a GameListView of the type 'basic'.
@ -22,8 +24,11 @@ public:
virtual FileData* getCursor() override; virtual FileData* getCursor() override;
virtual void setCursor(FileData* file) override; virtual void setCursor(FileData* file) override;
virtual FileData* getNextEntry() override;
virtual FileData* getPreviousEntry() override;
virtual FileData* getFirstEntry() override; virtual FileData* getFirstEntry() override;
virtual FileData* getLastEntry() override; virtual FileData* getLastEntry() override;
virtual FileData* getFirstGameEntry() override;
virtual const char* getName() const override { return "basic"; } virtual const char* getName() const override { return "basic"; }
@ -42,6 +47,8 @@ protected:
virtual void addPlaceholder(); virtual void addPlaceholder();
TextListComponent<FileData*> mList; TextListComponent<FileData*> mList;
// Points to the first game in the list, i.e. the first entry which is of the type 'GAME'.
FileData* firstGameEntry;
const std::string FAVORITE_GAME_CHAR = "\uF005"; const std::string FAVORITE_GAME_CHAR = "\uF005";
const std::string FAVORITE_FOLDER_CHAR = "\uF07C"; const std::string FAVORITE_FOLDER_CHAR = "\uF07C";

View file

@ -1,4 +1,6 @@
// SPDX-License-Identifier: MIT
// //
// EmulationStation Desktop Edition
// GridGameListView.cpp // GridGameListView.cpp
// //
// Interface that defines a GameListView of the type 'grid'. // Interface that defines a GameListView of the type 'grid'.
@ -11,8 +13,8 @@
#include "views/ViewController.h" #include "views/ViewController.h"
#include "CollectionSystemManager.h" #include "CollectionSystemManager.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h"
#include "Sound.h" #include "Sound.h"
#include "SystemData.h"
#if defined(_RPI_) #if defined(_RPI_)
#include "components/VideoPlayerComponent.h" #include "components/VideoPlayerComponent.h"
#endif #endif
@ -159,6 +161,16 @@ void GridGameListView::setCursor(FileData* file)
} }
} }
FileData* GridGameListView::getNextEntry()
{
return mGrid.getNext();;
}
FileData* GridGameListView::getPreviousEntry()
{
return mGrid.getPrevious();
}
FileData* GridGameListView::getFirstEntry() FileData* GridGameListView::getFirstEntry()
{ {
return mGrid.getFirst();; return mGrid.getFirst();;
@ -169,6 +181,11 @@ FileData* GridGameListView::getLastEntry()
return mGrid.getLast(); return mGrid.getLast();
} }
FileData* GridGameListView::getFirstGameEntry()
{
return firstGameEntry;
}
std::string GridGameListView::getQuickSystemSelectRightButton() std::string GridGameListView::getQuickSystemSelectRightButton()
{ {
return "rightshoulder"; return "rightshoulder";
@ -222,11 +239,16 @@ const std::string GridGameListView::getImagePath(FileData* file)
void GridGameListView::populateList(const std::vector<FileData*>& files) void GridGameListView::populateList(const std::vector<FileData*>& files)
{ {
firstGameEntry = nullptr;
mGrid.clear(); mGrid.clear();
mHeaderText.setText(mRoot->getSystem()->getFullName()); mHeaderText.setText(mRoot->getSystem()->getFullName());
if (files.size() > 0) { if (files.size() > 0) {
for (auto it = files.cbegin(); it != files.cend(); it++) for (auto it = files.cbegin(); it != files.cend(); it++) {
if (!firstGameEntry && (*it)->getType() == GAME)
firstGameEntry = (*it);
mGrid.add((*it)->getName(), getImagePath(*it), *it); mGrid.add((*it)->getName(), getImagePath(*it), *it);
}
} }
else { else {
addPlaceholder(); addPlaceholder();

View file

@ -1,17 +1,18 @@
// SPDX-License-Identifier: MIT
// //
// EmulationStation Desktop Edition
// GridGameListView.h // GridGameListView.h
// //
// Interface that defines a GameListView of the type 'grid'. // Interface that defines a GameListView of the type 'grid'.
// //
#pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
#define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
#include "components/DateTimeComponent.h" #include "components/DateTimeComponent.h"
#include "components/ImageGridComponent.h"
#include "components/RatingComponent.h" #include "components/RatingComponent.h"
#include "components/ScrollableContainer.h" #include "components/ScrollableContainer.h"
#include "components/ImageGridComponent.h"
#include "components/VideoComponent.h" #include "components/VideoComponent.h"
#include "views/gamelist/ISimpleGameListView.h" #include "views/gamelist/ISimpleGameListView.h"
@ -27,8 +28,11 @@ public:
virtual FileData* getCursor() override; virtual FileData* getCursor() override;
virtual void setCursor(FileData*) override; virtual void setCursor(FileData*) override;
virtual FileData* getNextEntry() override;
virtual FileData* getPreviousEntry() override;
virtual FileData* getFirstEntry() override; virtual FileData* getFirstEntry() override;
virtual FileData* getLastEntry() override; virtual FileData* getLastEntry() override;
virtual FileData* getFirstGameEntry() override;
virtual bool input(InputConfig* config, Input input) override; virtual bool input(InputConfig* config, Input input) override;
@ -47,6 +51,8 @@ protected:
virtual void addPlaceholder(); virtual void addPlaceholder();
ImageGridComponent<FileData*> mGrid; ImageGridComponent<FileData*> mGrid;
// Points to the first game in the list, i.e. the first entry which is of the type 'GAME'.
FileData* firstGameEntry;
private: private:
void updateInfoPanel(); void updateInfoPanel();

View file

@ -44,8 +44,11 @@ public:
virtual FileData* getCursor() = 0; virtual FileData* getCursor() = 0;
virtual void setCursor(FileData*) = 0; virtual void setCursor(FileData*) = 0;
virtual FileData* getNextEntry() = 0;
virtual FileData* getPreviousEntry() = 0;
virtual FileData* getFirstEntry() = 0; virtual FileData* getFirstEntry() = 0;
virtual FileData* getLastEntry() = 0; virtual FileData* getLastEntry() = 0;
virtual FileData* getFirstGameEntry() = 0;
virtual bool input(InputConfig* config, Input input) override; virtual bool input(InputConfig* config, Input input) override;
virtual void remove(FileData* game, bool deleteFile) = 0; virtual void remove(FileData* game, bool deleteFile) = 0;

View file

@ -182,36 +182,118 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
if (mRoot->getSystem()->isGameSystem()) { if (mRoot->getSystem()->isGameSystem()) {
if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER) if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER)
NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND);
// When marking or unmarking a game as favorite, don't jump to the new position
// it gets after the gamelist sorting. Instead retain the cursor position in the
// list using the logic below.
FileData* entryToUpdate = getCursor();
bool favoritesSorting;
bool removedLastFavorite = false;
bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
if (CollectionSystemManager::get()->getIsCustomCollection(mRoot->getSystem()))
favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
else
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
if (favoritesSorting && static_cast<std::string>(getName()) != "recent") {
FileData* entryToSelect;
// Add favorite flag.
if (!getCursor()->getFavorite()) {
// If it's a folder and folders are sorted on top, select the current entry.
if (foldersOnTop && getCursor()->getType() == FOLDER)
entryToSelect = getCursor();
// If it's the first entry to be marked as favorite, select the next entry.
else if (getCursor() == getFirstEntry())
entryToSelect = getNextEntry();
// If we are on the favorite marking boundary, select the next entry.
else if (getCursor()->getFavorite() != getPreviousEntry()->getFavorite())
entryToSelect = getNextEntry();
// For all other scenarios try to select the next entry, and if it doesn't
// exist, select the previous entry.
else
entryToSelect = getCursor() != getNextEntry() ?
getNextEntry() : getPreviousEntry();
}
// Remove favorite flag.
else {
// If it's a folder and folders are sorted on top, select the current entry.
if (foldersOnTop && getCursor()->getType() == FOLDER)
entryToSelect = getCursor();
// If it's the last entry, select the previous entry.
else if (getCursor() == getLastEntry())
entryToSelect = getPreviousEntry();
// If we are on the favorite marking boundary, select the previous entry,
// unless folders are sorted on top and the previous entry is a folder.
else if (foldersOnTop &&
getCursor()->getFavorite() != getNextEntry()->getFavorite())
entryToSelect = getPreviousEntry()->getType() == FOLDER ?
getCursor() : getPreviousEntry();
// If we are on the favorite marking boundary, select the previous entry.
else if (getCursor()->getFavorite() != getNextEntry()->getFavorite())
entryToSelect = getPreviousEntry();
// For all other scenarios try to select the next entry, and if it doesn't
// exist, select the previous entry.
else
entryToSelect = getCursor() != getNextEntry() ?
getNextEntry() : getPreviousEntry();
// If we removed the last favorite marking, set the flag to jump to the
// first list entry after the sorting has been performed.
if (foldersOnTop && getCursor() == getFirstGameEntry() &&
!getNextEntry()->getFavorite())
removedLastFavorite = true;
else if (getCursor() == getFirstEntry() && !getNextEntry()->getFavorite())
removedLastFavorite = true;
}
setCursor(entryToSelect);
}
// Marking folders as favorites is only cosmetic as they're not sorted // 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 // 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. // sense to do it here than to add the function to CollectionSystemManager.
if (getCursor()->getType() == FOLDER) { if (entryToUpdate->getType() == FOLDER) {
GuiInfoPopup* s; GuiInfoPopup* s;
MetaDataList* md = &getCursor()->getSourceFileData()->metadata; MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata;
if (md->get("favorite") == "false") { if (md->get("favorite") == "false") {
md->set("favorite", "true"); md->set("favorite", "true");
s = new GuiInfoPopup(mWindow, "Marked folder '" + s = new GuiInfoPopup(mWindow, "Marked folder '" +
Utils::String::removeParenthesis(getCursor()->getName()) + Utils::String::removeParenthesis(entryToUpdate->getName()) +
"' as favorite", 4000); "' as favorite", 4000);
} }
else { else {
md->set("favorite", "false"); md->set("favorite", "false");
s = new GuiInfoPopup(mWindow, "Removed favorite marking for folder '" + s = new GuiInfoPopup(mWindow, "Removed favorite marking for folder '" +
Utils::String::removeParenthesis(getCursor()->getName()) + Utils::String::removeParenthesis(entryToUpdate->getName()) +
"'", 4000); "'", 4000);
} }
mWindow->setInfoPopup(s); mWindow->setInfoPopup(s);
getCursor()->getSourceFileData()->getSystem()->onMetaDataSavePoint(); entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint();
if (!Settings::getInstance()->getBool("FoldersOnTop")) if (!Settings::getInstance()->getBool("FoldersOnTop"))
mRoot->sort(mRoot->getSortTypeFromString(mRoot->getSortTypeString()), mRoot->sort(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);
// Always jump to the first entry in the gamelist if the last favorite
// was unmarked. We couldn't do this earlier as we didn't have the list
// sorted yet.
if (removedLastFavorite) {
ViewController::get()->getGameListView(mRoot->getSystem())->setCursor(
ViewController::get()->getGameListView(mRoot->getSystem())->
getFirstEntry());
}
return true; return true;
} }
else if (CollectionSystemManager::get()->toggleGameInCollection(getCursor())) { else if (CollectionSystemManager::get()->toggleGameInCollection(entryToUpdate)) {
// Jump to the first entry in the gamelist if the last favorite was unmarked.
if (foldersOnTop && removedLastFavorite)
setCursor(getFirstGameEntry());
else if (removedLastFavorite)
setCursor(getFirstEntry());
return true; return true;
} }
} }

View file

@ -144,6 +144,24 @@ public:
return mEntries.at(mCursor).object; return mEntries.at(mCursor).object;
} }
inline const UserData& getNext() const
{
// If there is a next entry, then return it, otherwise return the current entry.
if (mCursor + 1 < mEntries.size())
return mEntries.at(mCursor+1).object;
else
return mEntries.at(mCursor).object;
}
inline const UserData& getPrevious() const
{
// If there is a previous entry, then return it, otherwise return the current entry.
if (mCursor != 0)
return mEntries.at(mCursor-1).object;
else
return mEntries.at(mCursor).object;
}
inline const UserData& getFirst() const inline const UserData& getFirst() const
{ {
assert(size() > 0); assert(size() > 0);