Added a GameSelectorComponent for displaying game media in SystemView.

This commit is contained in:
Leon Styhre 2022-02-13 20:03:34 +01:00
parent 31c5b200d1
commit cc8123f5a6
6 changed files with 180 additions and 4 deletions

View file

@ -1443,6 +1443,10 @@ void CollectionSystemsManager::trimCollectionCount(FileData* rootFolder, int lim
(CollectionFileData*)rootFolder->getChildrenListToDisplay().back();
ViewController::getInstance()->getGamelistView(curSys).get()->remove(gameToRemove, false);
}
// Also update the lists of last played and most played games as these could otherwise
// contain dangling pointers.
rootFolder->updateLastPlayedList();
rootFolder->updateMostPlayedList();
}
const bool CollectionSystemsManager::themeFolderExists(const std::string& folder)

View file

@ -39,6 +39,8 @@ FileData::FileData(FileType type,
, mEnvData {envData}
, mSystem {system}
, mOnlyFolders {false}
, mUpdateChildrenLastPlayed {false}
, mUpdateChildrenMostPlayed {false}
, mDeletionFlag {false}
{
// Metadata needs at least a name field (since that's what getName() will return).
@ -736,6 +738,9 @@ void FileData::sort(const SortType& type, bool mFavoritesOnTop)
sortFavoritesOnTop(*type.comparisonFunction, mGameCount);
else
sort(*type.comparisonFunction, mGameCount);
updateLastPlayedList();
updateMostPlayedList();
}
void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
@ -743,9 +748,6 @@ void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
bool isKidMode = (Settings::getInstance()->getString("UIMode") == "kid" ||
Settings::getInstance()->getBool("ForceKid"));
(Settings::getInstance()->getString("UIMode") == "kid" ||
Settings::getInstance()->getBool("ForceKid"));
for (unsigned int i = 0; i < mChildren.size(); ++i) {
if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) {
if (!isKidMode || (isKidMode && mChildren[i]->getKidgame())) {
@ -761,6 +763,36 @@ void FileData::countGames(std::pair<unsigned int, unsigned int>& gameCount)
mGameCount = gameCount;
}
void FileData::updateLastPlayedList()
{
if (!mUpdateChildrenLastPlayed)
return;
mChildrenLastPlayed.clear();
mChildrenLastPlayed = getChildrenRecursive();
std::stable_sort(mChildrenLastPlayed.begin(), mChildrenLastPlayed.end());
std::sort(std::begin(mChildrenLastPlayed), std::end(mChildrenLastPlayed),
[](FileData* a, FileData* b) {
return a->metadata.get("lastplayed") > b->metadata.get("lastplayed");
});
}
void FileData::updateMostPlayedList()
{
if (!mUpdateChildrenMostPlayed)
return;
mChildrenMostPlayed.clear();
mChildrenMostPlayed = getChildrenRecursive();
std::stable_sort(mChildrenMostPlayed.begin(), mChildrenMostPlayed.end());
std::sort(std::begin(mChildrenMostPlayed), std::end(mChildrenMostPlayed),
[](FileData* a, FileData* b) {
return a->metadata.getInt("playcount") > b->metadata.getInt("playcount");
});
}
const FileData::SortType& FileData::getSortTypeFromString(const std::string& desc) const
{
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;

View file

@ -56,6 +56,13 @@ public:
const std::vector<FileData*>& getChildren() const { return mChildren; }
SystemData* getSystem() const { return mSystem; }
SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
// These functions are used by GameSelectorComponent.
const std::vector<FileData*>& getChildrenLastPlayed() const { return mChildrenLastPlayed; }
const std::vector<FileData*>& getChildrenMostPlayed() const { return mChildrenMostPlayed; }
void setUpdateChildrenLastPlayed(bool state) { mUpdateChildrenLastPlayed = state; }
void setUpdateChildrenMostPlayed(bool state) { mUpdateChildrenMostPlayed = state; }
const bool getOnlyFoldersFlag() const { return mOnlyFolders; }
const bool getHasFoldersFlag() const { return mHasFolders; }
static const std::string getROMDirectory();
@ -127,7 +134,8 @@ public:
MetaDataList metadata;
// Only count the games, a cheaper alternative to a full sort when that is not required.
void countGames(std::pair<unsigned int, unsigned int>& gameCount);
void updateLastPlayedList();
void updateMostPlayedList();
void setSortTypeString(std::string typestring) { mSortTypeString = typestring; }
const std::string& getSortTypeString() const { return mSortTypeString; }
const FileData::SortType& getSortTypeFromString(const std::string& desc) const;
@ -146,10 +154,14 @@ private:
std::unordered_map<std::string, FileData*> mChildrenByFilename;
std::vector<FileData*> mChildren;
std::vector<FileData*> mFilteredChildren;
std::vector<FileData*> mChildrenLastPlayed;
std::vector<FileData*> mChildrenMostPlayed;
// The pair includes all games, and favorite games.
std::pair<unsigned int, unsigned int> mGameCount;
bool mOnlyFolders;
bool mHasFolders;
bool mUpdateChildrenLastPlayed;
bool mUpdateChildrenMostPlayed;
// Used for flagging a game for deletion from its gamelist.xml file.
bool mDeletionFlag;
};

View file

@ -42,6 +42,7 @@ set(CORE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/DateTimeEditComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/FlexboxComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GameSelectorComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GridTileComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/HelpComponent.h
${CMAKE_CURRENT_SOURCE_DIR}/src/components/IList.h

View file

@ -279,6 +279,9 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"forceUppercase", BOOLEAN}, // For backward compatibility with legacy themes.
{"lineSpacing", FLOAT},
{"zIndex", FLOAT}}},
{"gameselector",
{{"selection", STRING},
{"count", UNSIGNED_INTEGER}}},
{"helpsystem",
{{"pos", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},

View file

@ -0,0 +1,124 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GameSelectorComponent.h
//
// Makes a selection of games based on theme-controlled criteria.
//
#ifndef ES_CORE_COMPONENTS_GAME_SELECTOR_COMPONENT_H
#define ES_CORE_COMPONENTS_GAME_SELECTOR_COMPONENT_H
#include "GuiComponent.h"
#include "Log.h"
#include "ThemeData.h"
class GameSelectorComponent : public GuiComponent
{
public:
GameSelectorComponent(SystemData* system)
: mSystem {system}
, mGameSelection {GameSelection::RANDOM}
, mGameCount {1}
{
}
const std::vector<FileData*>& getGames() const { return mGames; }
void refreshGames()
{
mGames.clear();
bool isKidMode {(Settings::getInstance()->getString("UIMode") == "kid" ||
Settings::getInstance()->getBool("ForceKid"))};
if (mGameSelection == GameSelection::RANDOM) {
for (int i = 0; i < mGameCount; ++i) {
FileData* randomGame {mSystem->getRandomGame()};
if (randomGame != nullptr)
mGames.emplace_back(randomGame);
}
}
else if (mGameSelection == GameSelection::LAST_PLAYED) {
for (auto& child : mSystem->getRootFolder()->getChildrenLastPlayed()) {
if (child->getType() != GAME)
continue;
if (!child->getCountAsGame())
continue;
if (isKidMode && !child->getKidgame())
continue;
if (child->metadata.get("lastplayed") == "0")
continue;
mGames.emplace_back(child);
if (static_cast<int>(mGames.size()) == mGameCount)
break;
}
}
else if (mGameSelection == GameSelection::MOST_PLAYED) {
for (auto& child : mSystem->getRootFolder()->getChildrenMostPlayed()) {
if (child->getType() != GAME)
continue;
if (!child->getCountAsGame())
continue;
if (isKidMode && !child->getKidgame())
continue;
if (child->metadata.get("playcount") == "0")
continue;
mGames.emplace_back(child);
if (static_cast<int>(mGames.size()) == mGameCount)
break;
}
}
}
void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
unsigned int properties)
{
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "gameselector")};
if (!elem)
return;
if (elem->has("selection")) {
const std::string selection {elem->get<std::string>("selection")};
if (selection == "random") {
mGameSelection = GameSelection::RANDOM;
}
else if (selection == "lastplayed") {
mGameSelection = GameSelection::LAST_PLAYED;
mSystem->getRootFolder()->setUpdateChildrenLastPlayed(true);
mSystem->getRootFolder()->updateLastPlayedList();
}
else if (selection == "mostplayed") {
mGameSelection = GameSelection::MOST_PLAYED;
mSystem->getRootFolder()->setUpdateChildrenMostPlayed(true);
mSystem->getRootFolder()->updateMostPlayedList();
}
else {
mGameSelection = GameSelection::RANDOM;
LOG(LogWarning) << "GameSelectorComponent: Invalid theme configuration, property "
"<selection> set to \""
<< selection << "\"";
}
}
if (elem->has("count"))
mGameCount = glm::clamp(static_cast<int>(elem->get<unsigned int>("count")), 1, 30);
}
private:
enum class GameSelection {
RANDOM, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
LAST_PLAYED,
MOST_PLAYED
};
SystemData* mSystem;
std::vector<FileData*> mGames;
GameSelection mGameSelection;
int mGameCount;
};
#endif // ES_CORE_COMPONENTS_GAME_SELECTOR_COMPONENT_H