mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 14:15:38 +00:00
Merge pull request #115 from pjft/RetroPie-master-filter-rebase
Adding generic gamelist filter funcionality for ES
This commit is contained in:
commit
a909f10b2d
|
@ -10,6 +10,7 @@ set(ES_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Gamelist.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.h
|
||||
|
||||
# GuiComponents
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.h
|
||||
|
@ -26,6 +27,7 @@ set(ES_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperStart.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistFilter.h
|
||||
|
||||
# Scrapers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.h
|
||||
|
@ -57,6 +59,7 @@ set(ES_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Gamelist.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.cpp
|
||||
|
||||
# GuiComponents
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.cpp
|
||||
|
@ -72,6 +75,7 @@ set(ES_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiSettings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperStart.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistFilter.cpp
|
||||
|
||||
# Scrapers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.cpp
|
||||
|
|
|
@ -81,6 +81,26 @@ const std::string& FileData::getThumbnailPath() const
|
|||
return metadata.get("image");
|
||||
}
|
||||
|
||||
const std::vector<FileData*>& FileData::getChildrenListToDisplay() {
|
||||
|
||||
FileFilterIndex* idx = mSystem->getIndex();
|
||||
if (idx->isFiltered()) {
|
||||
mFilteredChildren.clear();
|
||||
for(auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
{
|
||||
if (idx->showFile((*it))) {
|
||||
mFilteredChildren.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
return mFilteredChildren;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mChildren;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& FileData::getVideoPath() const
|
||||
{
|
||||
return metadata.get("video");
|
||||
|
@ -91,19 +111,22 @@ const std::string& FileData::getMarqueePath() const
|
|||
return metadata.get("marquee");
|
||||
}
|
||||
|
||||
|
||||
std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask) const
|
||||
std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask, bool displayedOnly) const
|
||||
{
|
||||
std::vector<FileData*> out;
|
||||
FileFilterIndex* idx = mSystem->getIndex();
|
||||
|
||||
for(auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
{
|
||||
if((*it)->getType() & typeMask)
|
||||
out.push_back(*it);
|
||||
{
|
||||
if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it))
|
||||
out.push_back(*it);
|
||||
}
|
||||
|
||||
if((*it)->getChildren().size() > 0)
|
||||
{
|
||||
std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask);
|
||||
std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly);
|
||||
out.insert(out.end(), subchildren.cbegin(), subchildren.cend());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ class SystemData;
|
|||
enum FileType
|
||||
{
|
||||
GAME = 1, // Cannot have children.
|
||||
FOLDER = 2
|
||||
FOLDER = 2,
|
||||
PLACEHOLDER = 3
|
||||
};
|
||||
|
||||
enum FileChangeType
|
||||
|
@ -48,11 +49,14 @@ public:
|
|||
virtual const std::string& getVideoPath() const;
|
||||
virtual const std::string& getMarqueePath() const;
|
||||
|
||||
std::vector<FileData*> getFilesRecursive(unsigned int typeMask) const;
|
||||
const std::vector<FileData*>& getChildrenListToDisplay();
|
||||
std::vector<FileData*> getFilesRecursive(unsigned int typeMask, bool displayedOnly = false) const;
|
||||
|
||||
void addChild(FileData* file); // Error if mType != FOLDER
|
||||
void removeChild(FileData* file); //Error if mType != FOLDER
|
||||
|
||||
inline bool isPlaceHolder() { return mType == PLACEHOLDER; };
|
||||
|
||||
// Returns our best guess at the "real" name for this file (will attempt to perform MAME name translation)
|
||||
std::string getDisplayName() const;
|
||||
|
||||
|
@ -82,4 +86,5 @@ private:
|
|||
FileData* mParent;
|
||||
std::unordered_map<std::string,FileData*> mChildrenByFilename;
|
||||
std::vector<FileData*> mChildren;
|
||||
std::vector<FileData*> mFilteredChildren;
|
||||
};
|
||||
|
|
377
es-app/src/FileFilterIndex.cpp
Normal file
377
es-app/src/FileFilterIndex.cpp
Normal file
|
@ -0,0 +1,377 @@
|
|||
#include "FileFilterIndex.h"
|
||||
|
||||
#define UNKNOWN_LABEL "UNKNOWN"
|
||||
#define INCLUDE_UNKNOWN false;
|
||||
|
||||
FileFilterIndex::FileFilterIndex()
|
||||
: filterByGenre(false), filterByPlayers(false), filterByPubDev(false), filterByRatings(false)
|
||||
{
|
||||
FilterDataDecl filterDecls[] = {
|
||||
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
|
||||
{ GENRE_FILTER, &genreIndexAllKeys, &filterByGenre, &genreIndexFilteredKeys, "genre", true, "genre", "GENRE" },
|
||||
{ PLAYER_FILTER, &playersIndexAllKeys, &filterByPlayers, &playersIndexFilteredKeys, "players", false, "", "PLAYERS" },
|
||||
{ PUBDEV_FILTER, &pubDevIndexAllKeys, &filterByPubDev, &pubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" },
|
||||
{ RATINGS_FILTER, &ratingsIndexAllKeys, &filterByRatings, &ratingsIndexFilteredKeys, "rating", false, "", "RATING" }
|
||||
};
|
||||
|
||||
filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0]));
|
||||
}
|
||||
|
||||
FileFilterIndex::~FileFilterIndex()
|
||||
{
|
||||
clearIndex(genreIndexAllKeys);
|
||||
clearIndex(playersIndexAllKeys);
|
||||
clearIndex(pubDevIndexAllKeys);
|
||||
clearIndex(ratingsIndexAllKeys);
|
||||
|
||||
}
|
||||
|
||||
std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
|
||||
{
|
||||
return filterDataDecl;
|
||||
}
|
||||
|
||||
std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary)
|
||||
{
|
||||
std::string key = "";
|
||||
switch(type)
|
||||
{
|
||||
case GENRE_FILTER:
|
||||
{
|
||||
key = strToUpper(game->metadata.get("genre"));
|
||||
boost::trim(key);
|
||||
if (getSecondary && !key.empty()) {
|
||||
std::istringstream f(key);
|
||||
std::string newKey;
|
||||
getline(f, newKey, '/');
|
||||
if (!newKey.empty() && newKey != key)
|
||||
{
|
||||
key = newKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = std::string();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PLAYER_FILTER:
|
||||
{
|
||||
if (getSecondary)
|
||||
break;
|
||||
|
||||
key = game->metadata.get("players");
|
||||
break;
|
||||
}
|
||||
case PUBDEV_FILTER:
|
||||
{
|
||||
key = strToUpper(game->metadata.get("publisher"));
|
||||
boost::trim(key);
|
||||
|
||||
if ((getSecondary && !key.empty()) || (!getSecondary && key.empty()))
|
||||
key = strToUpper(game->metadata.get("developer"));
|
||||
else
|
||||
key = strToUpper(game->metadata.get("publisher"));
|
||||
break;
|
||||
}
|
||||
case RATINGS_FILTER:
|
||||
{
|
||||
int ratingNumber = 0;
|
||||
if (!getSecondary)
|
||||
{
|
||||
std::string ratingString = game->metadata.get("rating");
|
||||
if (!ratingString.empty()) {
|
||||
try {
|
||||
ratingNumber = boost::math::iround(std::stod(ratingString)*5);
|
||||
if (ratingNumber < 0)
|
||||
ratingNumber = 0;
|
||||
|
||||
key = std::to_string(ratingNumber) + " STARS";
|
||||
}
|
||||
catch (int e)
|
||||
{
|
||||
LOG(LogError) << "Error parsing Rating (invalid value, expected decimal): " << ratingString;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
boost::trim(key);
|
||||
if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) {
|
||||
key = UNKNOWN_LABEL;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
void FileFilterIndex::addToIndex(FileData* game)
|
||||
{
|
||||
manageGenreEntryInIndex(game);
|
||||
managePlayerEntryInIndex(game);
|
||||
managePubDevEntryInIndex(game);
|
||||
manageRatingsEntryInIndex(game);
|
||||
}
|
||||
|
||||
void FileFilterIndex::removeFromIndex(FileData* game)
|
||||
{
|
||||
manageGenreEntryInIndex(game, true);
|
||||
managePlayerEntryInIndex(game, true);
|
||||
managePubDevEntryInIndex(game, true);
|
||||
manageRatingsEntryInIndex(game, true);
|
||||
}
|
||||
|
||||
void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>* values)
|
||||
{
|
||||
// test if it exists before setting
|
||||
if(type == NONE)
|
||||
{
|
||||
clearAllFilters();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::vector<FilterDataDecl>::iterator it = filterDataDecl.begin(); it != filterDataDecl.end(); ++it ) {
|
||||
if ((*it).type == type)
|
||||
{
|
||||
FilterDataDecl filterData = (*it);
|
||||
*(filterData.filteredByRef) = values->size() > 0;
|
||||
filterData.currentFilteredKeys->clear();
|
||||
for (std::vector<std::string>::iterator vit = values->begin(); vit != values->end(); ++vit ) {
|
||||
// check if exists
|
||||
if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->end()) {
|
||||
filterData.currentFilteredKeys->push_back(std::string(*vit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FileFilterIndex::clearAllFilters()
|
||||
{
|
||||
for (std::vector<FilterDataDecl>::iterator it = filterDataDecl.begin(); it != filterDataDecl.end(); ++it ) {
|
||||
FilterDataDecl filterData = (*it);
|
||||
*(filterData.filteredByRef) = false;
|
||||
filterData.currentFilteredKeys->clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FileFilterIndex::debugPrintIndexes()
|
||||
{
|
||||
LOG(LogInfo) << "Printing Indexes...";
|
||||
for (auto x: playersIndexAllKeys) {
|
||||
LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: genreIndexAllKeys) {
|
||||
LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: ratingsIndexAllKeys) {
|
||||
LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: pubDevIndexAllKeys) {
|
||||
LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFilterIndex::showFile(FileData* game)
|
||||
{
|
||||
// this shouldn't happen, but just in case let's get it out of the way
|
||||
if (!isFiltered())
|
||||
return true;
|
||||
|
||||
// if folder, needs further inspection - i.e. see if folder contains at least one element
|
||||
// that should be shown
|
||||
if (game->getType() == FOLDER) {
|
||||
std::vector<FileData*> children = game->getChildren();
|
||||
// iterate through all of the children, until there's a match
|
||||
|
||||
for (std::vector<FileData*>::iterator it = children.begin(); it != children.end(); ++it ) {
|
||||
if (showFile(*it))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool keepGoing = false;
|
||||
|
||||
for (std::vector<FilterDataDecl>::iterator it = filterDataDecl.begin(); it != filterDataDecl.end(); ++it ) {
|
||||
FilterDataDecl filterData = (*it);
|
||||
if(*(filterData.filteredByRef)) {
|
||||
// try to find a match
|
||||
std::string key = getIndexableKey(game, filterData.type, false);
|
||||
keepGoing = isKeyBeingFilteredBy(key, filterData.type);
|
||||
|
||||
// if we didn't find a match, try for secondary keys - i.e. publisher and dev, or first genre
|
||||
if (!keepGoing) {
|
||||
if (!filterData.hasSecondaryKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::string secKey = getIndexableKey(game, filterData.type, true);
|
||||
if (secKey != UNKNOWN_LABEL)
|
||||
{
|
||||
keepGoing = isKeyBeingFilteredBy(secKey, filterData.type);
|
||||
}
|
||||
}
|
||||
// if still nothing, then it's not a match
|
||||
if (!keepGoing)
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return keepGoing;
|
||||
}
|
||||
|
||||
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) {
|
||||
const FilterIndexType filterTypes[4] = { PLAYER_FILTER, RATINGS_FILTER, GENRE_FILTER, PUBDEV_FILTER };
|
||||
std::vector<std::string> filterKeysList[4] = { playersIndexFilteredKeys, ratingsIndexFilteredKeys, genreIndexFilteredKeys, pubDevIndexFilteredKeys };
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (filterTypes[i] == type) {
|
||||
for (std::vector<std::string>::iterator it = filterKeysList[i].begin(); it != filterKeysList[i].end(); ++it ) {
|
||||
if (key == (*it))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileFilterIndex::manageGenreEntryInIndex(FileData* game, bool remove)
|
||||
{
|
||||
|
||||
std::string key = getIndexableKey(game, GENRE_FILTER, false);
|
||||
|
||||
// flag for including unknowns
|
||||
bool includeUnknown = INCLUDE_UNKNOWN;
|
||||
|
||||
// only add unknown in pubdev IF both dev and pub are empty
|
||||
if (!includeUnknown && (key == UNKNOWN_LABEL || key == "BIOS")) {
|
||||
// no valid genre info found
|
||||
return;
|
||||
}
|
||||
|
||||
manageIndexEntry(&genreIndexAllKeys, key, remove);
|
||||
|
||||
key = getIndexableKey(game, GENRE_FILTER, true);
|
||||
if (!includeUnknown && key == UNKNOWN_LABEL)
|
||||
{
|
||||
manageIndexEntry(&genreIndexAllKeys, key, remove);
|
||||
}
|
||||
}
|
||||
|
||||
void FileFilterIndex::managePlayerEntryInIndex(FileData* game, bool remove)
|
||||
{
|
||||
// flag for including unknowns
|
||||
bool includeUnknown = INCLUDE_UNKNOWN;
|
||||
std::string key = getIndexableKey(game, PLAYER_FILTER, false);
|
||||
|
||||
// only add unknown in pubdev IF both dev and pub are empty
|
||||
if (!includeUnknown && key == UNKNOWN_LABEL) {
|
||||
// no valid player info found
|
||||
return;
|
||||
}
|
||||
|
||||
manageIndexEntry(&playersIndexAllKeys, key, remove);
|
||||
}
|
||||
|
||||
void FileFilterIndex::managePubDevEntryInIndex(FileData* game, bool remove)
|
||||
{
|
||||
std::string pub = getIndexableKey(game, PUBDEV_FILTER, false);
|
||||
std::string dev = getIndexableKey(game, PUBDEV_FILTER, true);
|
||||
|
||||
// flag for including unknowns
|
||||
bool includeUnknown = INCLUDE_UNKNOWN;
|
||||
bool unknownPub = false;
|
||||
bool unknownDev = false;
|
||||
|
||||
if (pub == UNKNOWN_LABEL) {
|
||||
unknownPub = true;
|
||||
}
|
||||
if (dev == UNKNOWN_LABEL) {
|
||||
unknownDev = true;
|
||||
}
|
||||
|
||||
if (!includeUnknown && unknownDev && unknownPub) {
|
||||
// no valid rating info found
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknownDev && unknownPub) {
|
||||
// if no info at all
|
||||
manageIndexEntry(&pubDevIndexAllKeys, pub, remove);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!unknownDev) {
|
||||
// if no info at all
|
||||
manageIndexEntry(&pubDevIndexAllKeys, dev, remove);
|
||||
}
|
||||
if (!unknownPub) {
|
||||
// if no info at all
|
||||
manageIndexEntry(&pubDevIndexAllKeys, pub, remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileFilterIndex::manageRatingsEntryInIndex(FileData* game, bool remove)
|
||||
{
|
||||
std::string key = getIndexableKey(game, RATINGS_FILTER, false);
|
||||
|
||||
// flag for including unknowns
|
||||
bool includeUnknown = INCLUDE_UNKNOWN;
|
||||
|
||||
if (!includeUnknown && key == UNKNOWN_LABEL) {
|
||||
// no valid rating info found
|
||||
return;
|
||||
}
|
||||
|
||||
manageIndexEntry(&ratingsIndexAllKeys, key, remove);
|
||||
}
|
||||
|
||||
void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove) {
|
||||
bool includeUnknown = INCLUDE_UNKNOWN;
|
||||
if (!includeUnknown && key == UNKNOWN_LABEL)
|
||||
return;
|
||||
if (remove) {
|
||||
// removing entry
|
||||
if (index->find(key) == index->end())
|
||||
{
|
||||
// this shouldn't happen
|
||||
LOG(LogError) << "Couldn't find entry in index! " << key;
|
||||
}
|
||||
else
|
||||
{
|
||||
(index->at(key))--;
|
||||
if(index->at(key) <= 0) {
|
||||
index->erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// adding entry
|
||||
if (index->find(key) == index->end())
|
||||
{
|
||||
(*index)[key] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
(index->at(key))++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileFilterIndex::clearIndex(std::map<std::string, int> indexMap)
|
||||
{
|
||||
indexMap.clear();
|
||||
}
|
79
es-app/src/FileFilterIndex.h
Normal file
79
es-app/src/FileFilterIndex.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "FileData.h"
|
||||
#include "Log.h"
|
||||
#include <boost/math/special_functions/round.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include "Util.h"
|
||||
|
||||
enum FilterIndexType
|
||||
{
|
||||
NONE,
|
||||
GENRE_FILTER,
|
||||
PLAYER_FILTER,
|
||||
PUBDEV_FILTER,
|
||||
RATINGS_FILTER
|
||||
};
|
||||
|
||||
struct FilterDataDecl
|
||||
{
|
||||
FilterIndexType type; // type of filter
|
||||
std::map<std::string, int>* allIndexKeys; // all possible filters for this type
|
||||
bool* filteredByRef; // is it filtered by this type
|
||||
std::vector<std::string>* currentFilteredKeys; // current keys being filtered for
|
||||
std::string primaryKey; // primary key in metadata
|
||||
bool hasSecondaryKey; // has secondary key for comparison
|
||||
std::string secondaryKey; // what's the secondary key
|
||||
std::string menuLabel; // text to show in menu
|
||||
};
|
||||
|
||||
class FileFilterIndex
|
||||
{
|
||||
public:
|
||||
FileFilterIndex();
|
||||
~FileFilterIndex();
|
||||
void addToIndex(FileData* game);
|
||||
void removeFromIndex(FileData* game);
|
||||
void setFilter(FilterIndexType type, std::vector<std::string>* values);
|
||||
void clearAllFilters();
|
||||
void debugPrintIndexes();
|
||||
bool showFile(FileData* game);
|
||||
bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || filterByRatings); };
|
||||
bool isKeyBeingFilteredBy(std::string key, FilterIndexType type);
|
||||
std::map<std::string, int>* getGenreAllIndexedKeys() { return &genreIndexAllKeys; };
|
||||
std::vector<std::string>* getGenreFilteredKeys() { return &genreIndexFilteredKeys; };
|
||||
std::vector<FilterDataDecl>& getFilterDataDecls();
|
||||
private:
|
||||
std::vector<FilterDataDecl> filterDataDecl;
|
||||
std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary);
|
||||
|
||||
void manageGenreEntryInIndex(FileData* game, bool remove = false);
|
||||
void managePlayerEntryInIndex(FileData* game, bool remove = false);
|
||||
void managePubDevEntryInIndex(FileData* game, bool remove = false);
|
||||
void manageRatingsEntryInIndex(FileData* game, bool remove = false);
|
||||
|
||||
void manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove);
|
||||
|
||||
void clearIndex(std::map<std::string, int> indexMap);
|
||||
|
||||
bool filterByGenre;
|
||||
bool filterByPlayers;
|
||||
bool filterByPubDev;
|
||||
bool filterByRatings;
|
||||
|
||||
std::map<std::string, int> genreIndexAllKeys;
|
||||
std::map<std::string, int> playersIndexAllKeys;
|
||||
std::map<std::string, int> pubDevIndexAllKeys;
|
||||
std::map<std::string, int> ratingsIndexAllKeys;
|
||||
|
||||
std::vector<std::string> genreIndexFilteredKeys;
|
||||
std::vector<std::string> playersIndexFilteredKeys;
|
||||
std::vector<std::string> pubDevIndexFilteredKeys;
|
||||
std::vector<std::string> ratingsIndexFilteredKeys;
|
||||
|
||||
FileData* mRootFolder;
|
||||
|
||||
};
|
|
@ -141,6 +141,14 @@ void parseGamelist(SystemData* system)
|
|||
file->metadata.set("name", defaultName);
|
||||
|
||||
file->metadata.resetChangedFlag();
|
||||
|
||||
// index if it's a game!
|
||||
if(type == GAME)
|
||||
{
|
||||
FileFilterIndex* index = system->getIndex();
|
||||
index->addToIndex(file);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con
|
|||
mPlatformIds = platformIds;
|
||||
mThemeFolder = themeFolder;
|
||||
|
||||
mFilterIndex = new FileFilterIndex();
|
||||
|
||||
mRootFolder = new FileData(FOLDER, mStartPath, this);
|
||||
mRootFolder->metadata.set("name", mFullName);
|
||||
|
||||
|
@ -59,6 +61,7 @@ SystemData::~SystemData()
|
|||
}
|
||||
|
||||
delete mRootFolder;
|
||||
delete mFilterIndex;
|
||||
}
|
||||
|
||||
|
||||
|
@ -429,6 +432,11 @@ unsigned int SystemData::getGameCount() const
|
|||
return mRootFolder->getFilesRecursive(GAME).size();
|
||||
}
|
||||
|
||||
unsigned int SystemData::getDisplayedGameCount() const
|
||||
{
|
||||
return mRootFolder->getFilesRecursive(GAME, true).size();
|
||||
}
|
||||
|
||||
void SystemData::loadTheme()
|
||||
{
|
||||
mTheme = std::make_shared<ThemeData>();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "MetaData.h"
|
||||
#include "PlatformId.h"
|
||||
#include "ThemeData.h"
|
||||
#include "FileFilterIndex.h"
|
||||
|
||||
class SystemData
|
||||
{
|
||||
|
@ -32,6 +33,7 @@ public:
|
|||
std::string getThemePath() const;
|
||||
|
||||
unsigned int getGameCount() const;
|
||||
unsigned int getDisplayedGameCount() const;
|
||||
|
||||
void launchGame(Window* window, FileData* game);
|
||||
|
||||
|
@ -64,6 +66,8 @@ public:
|
|||
// Load or re-load theme.
|
||||
void loadTheme();
|
||||
|
||||
FileFilterIndex* getIndex() { return mFilterIndex; };
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
std::string mFullName;
|
||||
|
@ -76,5 +80,7 @@ private:
|
|||
|
||||
void populateFolder(FileData* folder);
|
||||
|
||||
FileFilterIndex* mFilterIndex;
|
||||
|
||||
FileData* mRootFolder;
|
||||
};
|
||||
|
|
112
es-app/src/guis/GuiGamelistFilter.cpp
Normal file
112
es-app/src/guis/GuiGamelistFilter.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#include "guis/GuiGamelistFilter.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#include "components/TextComponent.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
|
||||
GuiGamelistFilter::GuiGamelistFilter(Window* window, SystemData* system) : GuiComponent(window), mMenu(window, "FILTER GAMELIST BY"), mSystem(system)
|
||||
{
|
||||
initializeMenu();
|
||||
}
|
||||
|
||||
void GuiGamelistFilter::initializeMenu()
|
||||
{
|
||||
addChild(&mMenu);
|
||||
|
||||
// get filters from system
|
||||
|
||||
mFilterIndex = mSystem->getIndex();
|
||||
|
||||
ComponentListRow row;
|
||||
|
||||
// show filtered menu
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "RESET ALL FILTERS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistFilter::resetAllFilters, this));
|
||||
mMenu.addRow(row);
|
||||
row.elements.clear();
|
||||
|
||||
addFiltersToMenu();
|
||||
|
||||
mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this));
|
||||
|
||||
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f);
|
||||
}
|
||||
|
||||
void GuiGamelistFilter::resetAllFilters()
|
||||
{
|
||||
mFilterIndex->clearAllFilters();
|
||||
for (std::map<FilterIndexType, std::shared_ptr< OptionListComponent<std::string> >>::iterator it = mFilterOptions.begin(); it != mFilterOptions.end(); ++it ) {
|
||||
std::shared_ptr< OptionListComponent<std::string> > optionList = it->second;
|
||||
optionList->selectNone();
|
||||
}
|
||||
}
|
||||
|
||||
GuiGamelistFilter::~GuiGamelistFilter()
|
||||
{
|
||||
mFilterOptions.clear();
|
||||
}
|
||||
|
||||
void GuiGamelistFilter::addFiltersToMenu()
|
||||
{
|
||||
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
|
||||
for (std::vector<FilterDataDecl>::iterator it = decls.begin(); it != decls.end(); ++it ) {
|
||||
|
||||
FilterIndexType type = (*it).type; // type of filter
|
||||
std::map<std::string, int>* allKeys = (*it).allIndexKeys; // all possible filters for this type
|
||||
std::vector<std::string>* allFilteredKeys = (*it).currentFilteredKeys; // current keys being filtered for
|
||||
std::string menuLabel = (*it).menuLabel; // text to show in menu
|
||||
std::shared_ptr< OptionListComponent<std::string> > optionList;
|
||||
|
||||
|
||||
// add filters (with first one selected)
|
||||
ComponentListRow row;
|
||||
|
||||
// add genres
|
||||
optionList = std::make_shared< OptionListComponent<std::string> >(mWindow, menuLabel, true);
|
||||
for(auto it: *allKeys)
|
||||
{
|
||||
optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type));
|
||||
}
|
||||
if (allKeys->size() > 0)
|
||||
mMenu.addWithLabel(menuLabel, optionList);
|
||||
|
||||
mFilterOptions[type] = optionList;
|
||||
}
|
||||
}
|
||||
|
||||
void GuiGamelistFilter::applyFilters()
|
||||
{
|
||||
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
|
||||
for (std::map<FilterIndexType, std::shared_ptr< OptionListComponent<std::string> >>::iterator it = mFilterOptions.begin(); it != mFilterOptions.end(); ++it ) {
|
||||
std::shared_ptr< OptionListComponent<std::string> > optionList = it->second;
|
||||
std::vector<std::string> filters = optionList->getSelectedObjects();
|
||||
mFilterIndex->setFilter(it->first, &filters);
|
||||
}
|
||||
|
||||
delete this;
|
||||
|
||||
}
|
||||
|
||||
bool GuiGamelistFilter::input(InputConfig* config, Input input)
|
||||
{
|
||||
bool consumed = GuiComponent::input(config, input);
|
||||
if(consumed)
|
||||
return true;
|
||||
|
||||
if(config->isMappedTo("b", input) && input.value != 0)
|
||||
{
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<HelpPrompt> GuiGamelistFilter::getHelpPrompts()
|
||||
{
|
||||
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
|
||||
prompts.push_back(HelpPrompt("b", "back"));
|
||||
return prompts;
|
||||
}
|
34
es-app/src/guis/GuiGamelistFilter.h
Normal file
34
es-app/src/guis/GuiGamelistFilter.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "GuiComponent.h"
|
||||
#include "SystemData.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "FileFilterIndex.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
||||
template<typename T>
|
||||
class OptionListComponent;
|
||||
|
||||
|
||||
class GuiGamelistFilter : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiGamelistFilter(Window* window, SystemData* system);
|
||||
~GuiGamelistFilter();
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
|
||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
|
||||
private:
|
||||
void initializeMenu();
|
||||
void applyFilters();
|
||||
void resetAllFilters();
|
||||
void addFiltersToMenu();
|
||||
|
||||
std::map<FilterIndexType, std::shared_ptr< OptionListComponent<std::string> >> mFilterOptions;
|
||||
|
||||
MenuComponent mMenu;
|
||||
SystemData* mSystem;
|
||||
FileFilterIndex* mFilterIndex;
|
||||
};
|
|
@ -4,35 +4,21 @@
|
|||
#include "views/ViewController.h"
|
||||
|
||||
GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : GuiComponent(window),
|
||||
mSystem(system),
|
||||
mMenu(window, "OPTIONS")
|
||||
mSystem(system), mMenu(window, "OPTIONS"), fromPlaceholder(false), mFiltersChanged(false)
|
||||
{
|
||||
addChild(&mMenu);
|
||||
|
||||
// jump to letter
|
||||
char curChar = toupper(getGamelist()->getCursor()->getName()[0]);
|
||||
if(curChar < 'A' || curChar > 'Z')
|
||||
curChar = 'A';
|
||||
|
||||
mJumpToLetterList = std::make_shared<LetterList>(mWindow, "JUMP TO LETTER", false);
|
||||
for(char c = 'A'; c <= 'Z'; c++)
|
||||
mJumpToLetterList->add(std::string(1, c), c, c == curChar);
|
||||
|
||||
// check it's not a placeholder folder - if it is, only show "Filter Options"
|
||||
FileData* file = getGamelist()->getCursor();
|
||||
fromPlaceholder = file->isPlaceHolder();
|
||||
bool isFiltered = system->getIndex()->isFiltered();
|
||||
ComponentListRow row;
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "JUMP TO LETTER", 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)
|
||||
{
|
||||
jumpToLetter();
|
||||
return true;
|
||||
}
|
||||
else if(mJumpToLetterList->input(config, input))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// show filtered menu
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
|
||||
mMenu.addRow(row);
|
||||
|
||||
row.elements.clear();
|
||||
|
@ -48,23 +34,53 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui
|
|||
};
|
||||
mMenu.addRow(row);
|
||||
|
||||
// sort list by
|
||||
mListSort = std::make_shared<SortList>(mWindow, "SORT GAMES BY", false);
|
||||
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++)
|
||||
{
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
||||
mListSort->add(sort.description, &sort, i == 0); // TODO - actually make the sort type persistent
|
||||
if (!fromPlaceholder) {
|
||||
|
||||
if (!isFiltered) {
|
||||
// jump to letter
|
||||
row.elements.clear();
|
||||
char curChar = toupper(getGamelist()->getCursor()->getName()[0]);
|
||||
if(curChar < 'A' || curChar > 'Z')
|
||||
curChar = 'A';
|
||||
|
||||
mJumpToLetterList = std::make_shared<LetterList>(mWindow, "JUMP TO LETTER", false);
|
||||
for(char c = 'A'; c <= 'Z'; c++)
|
||||
mJumpToLetterList->add(std::string(1, c), c, c == curChar);
|
||||
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "JUMP TO LETTER", 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)
|
||||
{
|
||||
jumpToLetter();
|
||||
return true;
|
||||
}
|
||||
else if(mJumpToLetterList->input(config, input))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
// sort list by
|
||||
mListSort = std::make_shared<SortList>(mWindow, "SORT GAMES BY", false);
|
||||
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++)
|
||||
{
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
||||
mListSort->add(sort.description, &sort, i == 0); // TODO - actually make the sort type persistent
|
||||
}
|
||||
|
||||
mMenu.addWithLabel("SORT GAMES BY", mListSort);
|
||||
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
mMenu.addWithLabel("SORT GAMES BY", mListSort);
|
||||
|
||||
// edit game metadata
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
|
||||
// center the menu
|
||||
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
|
||||
|
@ -73,13 +89,36 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui
|
|||
GuiGamelistOptions::~GuiGamelistOptions()
|
||||
{
|
||||
// apply sort
|
||||
FileData* root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
||||
root->sort(*mListSort->getSelected()); // will also recursively sort children
|
||||
if (!fromPlaceholder) {
|
||||
FileData* root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
||||
root->sort(*mListSort->getSelected()); // will also recursively sort children
|
||||
|
||||
// notify that the root folder was sorted
|
||||
getGamelist()->onFileChanged(root, FILE_SORTED);
|
||||
// notify that the root folder was sorted
|
||||
getGamelist()->onFileChanged(root, FILE_SORTED);
|
||||
}
|
||||
if (mFiltersChanged)
|
||||
{
|
||||
if (!fromPlaceholder) {
|
||||
FileData* root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
||||
getGamelist()->onFileChanged(root, FILE_SORTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::openGamelistFilter()
|
||||
{
|
||||
mFiltersChanged = true;
|
||||
GuiGamelistFilter* ggf = new GuiGamelistFilter(mWindow, mSystem);
|
||||
mWindow->pushGui(ggf);
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::openMetaDataEd()
|
||||
{
|
||||
// open metadata editor
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "GuiComponent.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
#include "GuiGamelistFilter.h"
|
||||
#include "FileSorts.h"
|
||||
|
||||
class IGameListView;
|
||||
|
@ -15,6 +16,7 @@ public:
|
|||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||
|
||||
private:
|
||||
void openGamelistFilter();
|
||||
void openMetaDataEd();
|
||||
void jumpToLetter();
|
||||
|
||||
|
@ -28,4 +30,6 @@ private:
|
|||
|
||||
SystemData* mSystem;
|
||||
IGameListView* getGamelist();
|
||||
bool fromPlaceholder;
|
||||
bool mFiltersChanged;
|
||||
};
|
||||
|
|
|
@ -170,6 +170,9 @@ void GuiMetaDataEd::onSizeChanged()
|
|||
|
||||
void GuiMetaDataEd::save()
|
||||
{
|
||||
// remove game from index
|
||||
mScraperParams.system->getIndex()->removeFromIndex(mScraperParams.game);
|
||||
|
||||
for(unsigned int i = 0; i < mEditors.size(); i++)
|
||||
{
|
||||
if(mMetaDataDecl.at(i).isStatistic)
|
||||
|
@ -178,6 +181,9 @@ void GuiMetaDataEd::save()
|
|||
mMetaData->set(mMetaDataDecl.at(i).key, mEditors.at(i)->getValue());
|
||||
}
|
||||
|
||||
// enter game in index
|
||||
mScraperParams.system->getIndex()->addToIndex(mScraperParams.game);
|
||||
|
||||
if(mSavedCallback)
|
||||
mSavedCallback();
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ void ViewController::goToRandomGame()
|
|||
for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++)
|
||||
{
|
||||
if ((*it)->getName() != "retropie")
|
||||
total += (*it)->getGameCount();
|
||||
total += (*it)->getDisplayedGameCount();
|
||||
}
|
||||
|
||||
// get random number in range
|
||||
|
@ -135,14 +135,14 @@ void ViewController::goToRandomGame()
|
|||
{
|
||||
if ((*it)->getName() != "retropie")
|
||||
{
|
||||
if ((target - (int)(*it)->getGameCount()) >= 0)
|
||||
if ((target - (int)(*it)->getDisplayedGameCount()) >= 0)
|
||||
{
|
||||
target -= (int)(*it)->getGameCount();
|
||||
target -= (int)(*it)->getDisplayedGameCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
goToGameList(*it);
|
||||
std::vector<FileData*> list = (*it)->getRootFolder()->getFilesRecursive(GAME);
|
||||
std::vector<FileData*> list = (*it)->getRootFolder()->getFilesRecursive(GAME, true);
|
||||
getGameListView(*it)->setCursor(list.at(target));
|
||||
return;
|
||||
}
|
||||
|
@ -401,7 +401,11 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
|
|||
system->loadTheme();
|
||||
|
||||
std::shared_ptr<IGameListView> newView = getGameListView(system);
|
||||
newView->setCursor(cursor);
|
||||
|
||||
// to counter having come from a placeholder
|
||||
if (!cursor->isPlaceHolder()) {
|
||||
newView->setCursor(cursor);
|
||||
}
|
||||
|
||||
if(isCurrent)
|
||||
mCurrentView = newView;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "ThemeData.h"
|
||||
#include "SystemData.h"
|
||||
#include "Settings.h"
|
||||
#include "FileFilterIndex.h"
|
||||
|
||||
BasicGameListView::BasicGameListView(Window* window, FileData* root)
|
||||
: ISimpleGameListView(window, root), mList(window)
|
||||
|
@ -13,7 +14,7 @@ BasicGameListView::BasicGameListView(Window* window, FileData* root)
|
|||
mList.setPosition(0, mSize.y() * 0.2f);
|
||||
addChild(&mList);
|
||||
|
||||
populateList(root->getChildren());
|
||||
populateList(root->getChildrenListToDisplay());
|
||||
}
|
||||
|
||||
void BasicGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||
|
@ -38,12 +39,20 @@ void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
|
|||
void BasicGameListView::populateList(const std::vector<FileData*>& files)
|
||||
{
|
||||
mList.clear();
|
||||
|
||||
mHeaderText.setText(files.at(0)->getSystem()->getFullName());
|
||||
|
||||
for(auto it = files.begin(); it != files.end(); it++)
|
||||
if (files.size() > 0)
|
||||
{
|
||||
mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER));
|
||||
mHeaderText.setText(files.at(0)->getSystem()->getFullName());
|
||||
|
||||
for(auto it = files.begin(); it != files.end(); it++)
|
||||
{
|
||||
mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// empty list - add a placeholder
|
||||
FileData* placeholder = new FileData(PLACEHOLDER, "<No Results Found for Current Filter Criteria>", this->mRoot->getSystem());
|
||||
mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,9 +63,11 @@ FileData* BasicGameListView::getCursor()
|
|||
|
||||
void BasicGameListView::setCursor(FileData* cursor)
|
||||
{
|
||||
if (cursor->isPlaceHolder())
|
||||
return;
|
||||
if(!mList.setCursor(cursor))
|
||||
{
|
||||
populateList(cursor->getParent()->getChildren());
|
||||
populateList(cursor->getParent()->getChildrenListToDisplay());
|
||||
mList.setCursor(cursor);
|
||||
|
||||
// update our cursor stack in case our cursor just got set to some folder we weren't in before
|
||||
|
|
|
@ -10,7 +10,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) : ISimpleGame
|
|||
mGrid.setSize(mSize.x(), mSize.y() * 0.8f);
|
||||
addChild(&mGrid);
|
||||
|
||||
populateList(root->getChildren());
|
||||
populateList(root->getChildrenListToDisplay());
|
||||
}
|
||||
|
||||
FileData* GridGameListView::getCursor()
|
||||
|
@ -22,7 +22,7 @@ void GridGameListView::setCursor(FileData* file)
|
|||
{
|
||||
if(!mGrid.setCursor(file))
|
||||
{
|
||||
populateList(file->getParent()->getChildren());
|
||||
populateList(file->getParent()->getChildrenListToDisplay());
|
||||
mGrid.setCursor(file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,15 @@ void ISimpleGameListView::onFileChanged(FileData* file, FileChangeType change)
|
|||
// we could be tricky here to be efficient;
|
||||
// but this shouldn't happen very often so we'll just always repopulate
|
||||
FileData* cursor = getCursor();
|
||||
populateList(cursor->getParent()->getChildren());
|
||||
setCursor(cursor);
|
||||
if (!cursor->isPlaceHolder()) {
|
||||
populateList(cursor->getParent()->getChildrenListToDisplay());
|
||||
setCursor(cursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
populateList(mRoot->getChildrenListToDisplay());
|
||||
setCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
bool ISimpleGameListView::input(InputConfig* config, Input input)
|
||||
|
@ -67,7 +74,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
if(cursor->getChildren().size() > 0)
|
||||
{
|
||||
mCursorStack.push(cursor);
|
||||
populateList(cursor->getChildren());
|
||||
populateList(cursor->getChildrenListToDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -248,7 +248,9 @@ void VideoGameListView::updateInfoPanel()
|
|||
thumbnail_path.insert(0, getHomePath());
|
||||
}
|
||||
if (!mVideo.setVideo(video_path))
|
||||
{
|
||||
mVideo.setDefaultVideo();
|
||||
}
|
||||
mVideoPlaying = true;
|
||||
|
||||
mVideo.setImage(thumbnail_path);
|
||||
|
|
|
@ -81,11 +81,12 @@ bool ComponentList::input(InputConfig* config, Input input)
|
|||
}else if(config->isMappedTo("down", input))
|
||||
{
|
||||
return listInput(input.value != 0 ? 1 : 0);
|
||||
|
||||
}else if(config->isMappedTo("pageup", input))
|
||||
{
|
||||
return listInput(input.value != 0 ? -7 : 0);
|
||||
return listInput(input.value != 0 ? -6 : 0);
|
||||
}else if(config->isMappedTo("pagedown", input)){
|
||||
return listInput(input.value != 0 ? 7 : 0);
|
||||
return listInput(input.value != 0 ? 6 : 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -251,6 +251,24 @@ public:
|
|||
onSelectedChanged();
|
||||
}
|
||||
|
||||
void selectAll()
|
||||
{
|
||||
for(unsigned int i = 0; i < mEntries.size(); i++)
|
||||
{
|
||||
mEntries.at(i).selected = true;
|
||||
}
|
||||
onSelectedChanged();
|
||||
}
|
||||
|
||||
void selectNone()
|
||||
{
|
||||
for(unsigned int i = 0; i < mEntries.size(); i++)
|
||||
{
|
||||
mEntries.at(i).selected = false;
|
||||
}
|
||||
onSelectedChanged();
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int getSelectedId()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue