* Introduce KidMode: Prevent collection editing

* Introduce FileData Filtering for Kiosk and  Kid Modes to:
1. In Kiosk mode: Hide items with metadata tag `<hidden>true</hidden>`
2. In Kid mode: only show items with metadata tag `<kidgame>true</kidgame>`
* ES will auto-revert UI mode back to Full  when there is nothing at all to show.
* Changing the setting hideQuitMenuOnKidUI to true will hide this menu.
This commit is contained in:
D. Polders 2017-09-08 15:20:07 +02:00
parent 5b792c4ef2
commit 189eb05fee
14 changed files with 255 additions and 104 deletions

View file

@ -1,4 +1,6 @@
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "Settings.h"
#include "views/ViewController.h"
#include "FileData.h" #include "FileData.h"
#include "Log.h" #include "Log.h"
@ -9,15 +11,18 @@
#define INCLUDE_UNKNOWN false; #define INCLUDE_UNKNOWN false;
FileFilterIndex::FileFilterIndex() FileFilterIndex::FileFilterIndex()
: filterByGenre(false), filterByPlayers(false), filterByPubDev(false), filterByRatings(false), filterByFavorites(false) : filterByFavorites(false), filterByGenre(false), filterByHidden(false), filterByKidGame(false), filterByPlayers(false), filterByPubDev(false), filterByRatings(false)
{ {
clearAllFilters();
FilterDataDecl filterDecls[] = { FilterDataDecl filterDecls[] = {
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
{ FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" }, { FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" },
{ GENRE_FILTER, &genreIndexAllKeys, &filterByGenre, &genreIndexFilteredKeys, "genre", true, "genre", "GENRE" }, { GENRE_FILTER, &genreIndexAllKeys, &filterByGenre, &genreIndexFilteredKeys, "genre", true, "genre", "GENRE" },
{ PLAYER_FILTER, &playersIndexAllKeys, &filterByPlayers, &playersIndexFilteredKeys, "players", false, "", "PLAYERS" }, { PLAYER_FILTER, &playersIndexAllKeys, &filterByPlayers, &playersIndexFilteredKeys, "players", false, "", "PLAYERS" },
{ PUBDEV_FILTER, &pubDevIndexAllKeys, &filterByPubDev, &pubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" }, { PUBDEV_FILTER, &pubDevIndexAllKeys, &filterByPubDev, &pubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" },
{ RATINGS_FILTER, &ratingsIndexAllKeys, &filterByRatings, &ratingsIndexFilteredKeys, "rating", false, "", "RATING" } { RATINGS_FILTER, &ratingsIndexAllKeys, &filterByRatings, &ratingsIndexFilteredKeys, "rating", false, "", "RATING" },
{ KIDGAME_FILTER, &kidGameIndexAllKeys, &filterByKidGame, &kidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME" },
{ HIDDEN_FILTER, &hiddenIndexAllKeys, &filterByHidden, &hiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" }
}; };
filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0])); filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0]));
@ -36,17 +41,19 @@ std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
{ {
struct IndexImportStructure struct IndexImportStructure
{ {
std::map<std::string, int>* destinationIndex; std::map<std::string, int>* destinationIndex;
std::map<std::string, int>* sourceIndex; std::map<std::string, int>* sourceIndex;
}; };
IndexImportStructure indexStructDecls[] = { IndexImportStructure indexStructDecls[] = {
{ &genreIndexAllKeys, &(indexToImport->genreIndexAllKeys) }, { &genreIndexAllKeys, &(indexToImport->genreIndexAllKeys) },
{ &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) }, { &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) },
{ &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) }, { &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) },
{ &ratingsIndexAllKeys, &(indexToImport->ratingsIndexAllKeys) }, { &ratingsIndexAllKeys, &(indexToImport->ratingsIndexAllKeys) },
{ &favoritesIndexAllKeys, &(indexToImport->favoritesIndexAllKeys) } { &favoritesIndexAllKeys, &(indexToImport->favoritesIndexAllKeys) },
{ &hiddenIndexAllKeys, &(indexToImport->hiddenIndexAllKeys) },
{ &kidGameIndexAllKeys, &(indexToImport->kidGameIndexAllKeys) },
}; };
std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0]));
@ -75,6 +82,8 @@ void FileFilterIndex::resetIndex()
clearIndex(pubDevIndexAllKeys); clearIndex(pubDevIndexAllKeys);
clearIndex(ratingsIndexAllKeys); clearIndex(ratingsIndexAllKeys);
clearIndex(favoritesIndexAllKeys); clearIndex(favoritesIndexAllKeys);
clearIndex(hiddenIndexAllKeys);
clearIndex(kidGameIndexAllKeys);
} }
std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary) std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary)
@ -149,6 +158,20 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType typ
key = strToUpper(game->metadata.get("favorite")); key = strToUpper(game->metadata.get("favorite"));
break; break;
} }
case HIDDEN_FILTER:
{
if (game->getType() != GAME)
return "FALSE";
key = strToUpper(game->metadata.get("hidden"));
break;
}
case KIDGAME_FILTER:
{
if (game->getType() != GAME)
return "FALSE";
key = strToUpper(game->metadata.get("kidgame"));
break;
}
} }
boost::trim(key); boost::trim(key);
if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) { if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) {
@ -164,6 +187,8 @@ void FileFilterIndex::addToIndex(FileData* game)
managePubDevEntryInIndex(game); managePubDevEntryInIndex(game);
manageRatingsEntryInIndex(game); manageRatingsEntryInIndex(game);
manageFavoritesEntryInIndex(game); manageFavoritesEntryInIndex(game);
manageHiddenEntryInIndex(game);
manageKidGameEntryInIndex(game);
} }
void FileFilterIndex::removeFromIndex(FileData* game) void FileFilterIndex::removeFromIndex(FileData* game)
@ -173,6 +198,8 @@ void FileFilterIndex::removeFromIndex(FileData* game)
managePubDevEntryInIndex(game, true); managePubDevEntryInIndex(game, true);
manageRatingsEntryInIndex(game, true); manageRatingsEntryInIndex(game, true);
manageFavoritesEntryInIndex(game, true); manageFavoritesEntryInIndex(game, true);
manageHiddenEntryInIndex(game, true);
manageKidGameEntryInIndex(game, true);
} }
void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>* values) void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>* values)
@ -213,6 +240,28 @@ void FileFilterIndex::clearAllFilters()
return; return;
} }
void FileFilterIndex::resetFilters()
{
clearAllFilters();
setUIModeFilters();
}
void FileFilterIndex::setUIModeFilters()
{
if (!ViewController::get()->isUIModeFull())
{
filterByHidden = true;
std::vector<std::string> val = { "FALSE" };
setFilter(HIDDEN_FILTER, &val);
}
if (ViewController::get()->isUIModeKid())
{
filterByKidGame = true;
std::vector<std::string> val = { "TRUE" };
setFilter(KIDGAME_FILTER, &val);
}
}
void FileFilterIndex::debugPrintIndexes() void FileFilterIndex::debugPrintIndexes()
{ {
LOG(LogInfo) << "Printing Indexes..."; LOG(LogInfo) << "Printing Indexes...";
@ -230,6 +279,12 @@ void FileFilterIndex::debugPrintIndexes()
} }
for (auto x: favoritesIndexAllKeys) { for (auto x: favoritesIndexAllKeys) {
LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second; LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second;
}
for (auto x : hiddenIndexAllKeys) {
LOG(LogInfo) << "Hidden Index: " << x.first << ": " << x.second;
}
for (auto x : kidGameIndexAllKeys) {
LOG(LogInfo) << "KidGames Index: " << x.first << ": " << x.second;
} }
} }
@ -290,10 +345,10 @@ bool FileFilterIndex::showFile(FileData* game)
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type)
{ {
const FilterIndexType filterTypes[5] = { FAVORITES_FILTER, PLAYER_FILTER, RATINGS_FILTER, GENRE_FILTER, PUBDEV_FILTER }; const FilterIndexType filterTypes[7] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER,HIDDEN_FILTER, KIDGAME_FILTER };
std::vector<std::string> filterKeysList[5] = { favoritesIndexFilteredKeys, playersIndexFilteredKeys, ratingsIndexFilteredKeys, genreIndexFilteredKeys, pubDevIndexFilteredKeys }; std::vector<std::string> filterKeysList[7] = { favoritesIndexFilteredKeys, genreIndexFilteredKeys, playersIndexFilteredKeys, pubDevIndexFilteredKeys, ratingsIndexFilteredKeys, hiddenIndexFilteredKeys, kidGameIndexFilteredKeys };
for (int i = 0; i < 5; i++) for (int i = 0; i < 7; i++)
{ {
if (filterTypes[i] == type) if (filterTypes[i] == type)
{ {
@ -416,6 +471,32 @@ void FileFilterIndex::manageFavoritesEntryInIndex(FileData* game, bool remove)
manageIndexEntry(&favoritesIndexAllKeys, key, remove); manageIndexEntry(&favoritesIndexAllKeys, key, remove);
} }
void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove)
{
// flag for including unknowns
bool includeUnknown = INCLUDE_UNKNOWN;
std::string key = getIndexableKey(game, HIDDEN_FILTER, false);
if (!includeUnknown && key == UNKNOWN_LABEL) {
// no valid hidden info found
return;
}
manageIndexEntry(&hiddenIndexAllKeys, key, remove);
}
void FileFilterIndex::manageKidGameEntryInIndex(FileData* game, bool remove)
{
// flag for including unknowns
bool includeUnknown = INCLUDE_UNKNOWN;
std::string key = getIndexableKey(game, KIDGAME_FILTER, false);
if (!includeUnknown && key == UNKNOWN_LABEL) {
// no valid kidgame info found
return;
}
manageIndexEntry(&kidGameIndexAllKeys, key, remove);
}
void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove) { void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove) {
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
if (!includeUnknown && key == UNKNOWN_LABEL) if (!includeUnknown && key == UNKNOWN_LABEL)

View file

@ -14,7 +14,9 @@ enum FilterIndexType
PLAYER_FILTER, PLAYER_FILTER,
PUBDEV_FILTER, PUBDEV_FILTER,
RATINGS_FILTER, RATINGS_FILTER,
FAVORITES_FILTER FAVORITES_FILTER,
HIDDEN_FILTER,
KIDGAME_FILTER
}; };
struct FilterDataDecl struct FilterDataDecl
@ -40,12 +42,15 @@ public:
void clearAllFilters(); void clearAllFilters();
void debugPrintIndexes(); void debugPrintIndexes();
bool showFile(FileData* game); bool showFile(FileData* game);
bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || filterByRatings || filterByFavorites); }; bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || filterByRatings || filterByFavorites || filterByHidden || filterByKidGame); };
bool isKeyBeingFilteredBy(std::string key, FilterIndexType type); bool isKeyBeingFilteredBy(std::string key, FilterIndexType type);
std::vector<FilterDataDecl>& getFilterDataDecls(); std::vector<FilterDataDecl>& getFilterDataDecls();
void importIndex(FileFilterIndex* indexToImport); void importIndex(FileFilterIndex* indexToImport);
void resetIndex(); void resetIndex();
void resetFilters();
void setUIModeFilters();
private: private:
std::vector<FilterDataDecl> filterDataDecl; std::vector<FilterDataDecl> filterDataDecl;
std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary); std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary);
@ -55,6 +60,8 @@ private:
void managePubDevEntryInIndex(FileData* game, bool remove = false); void managePubDevEntryInIndex(FileData* game, bool remove = false);
void manageRatingsEntryInIndex(FileData* game, bool remove = false); void manageRatingsEntryInIndex(FileData* game, bool remove = false);
void manageFavoritesEntryInIndex(FileData* game, bool remove = false); void manageFavoritesEntryInIndex(FileData* game, bool remove = false);
void manageHiddenEntryInIndex(FileData* game, bool remove = false);
void manageKidGameEntryInIndex(FileData* game, bool remove = false);
void manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove); void manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove);
@ -65,18 +72,24 @@ private:
bool filterByPubDev; bool filterByPubDev;
bool filterByRatings; bool filterByRatings;
bool filterByFavorites; bool filterByFavorites;
bool filterByHidden;
bool filterByKidGame;
std::map<std::string, int> genreIndexAllKeys; std::map<std::string, int> genreIndexAllKeys;
std::map<std::string, int> playersIndexAllKeys; std::map<std::string, int> playersIndexAllKeys;
std::map<std::string, int> pubDevIndexAllKeys; std::map<std::string, int> pubDevIndexAllKeys;
std::map<std::string, int> ratingsIndexAllKeys; std::map<std::string, int> ratingsIndexAllKeys;
std::map<std::string, int> favoritesIndexAllKeys; std::map<std::string, int> favoritesIndexAllKeys;
std::map<std::string, int> hiddenIndexAllKeys;
std::map<std::string, int> kidGameIndexAllKeys;
std::vector<std::string> genreIndexFilteredKeys; std::vector<std::string> genreIndexFilteredKeys;
std::vector<std::string> playersIndexFilteredKeys; std::vector<std::string> playersIndexFilteredKeys;
std::vector<std::string> pubDevIndexFilteredKeys; std::vector<std::string> pubDevIndexFilteredKeys;
std::vector<std::string> ratingsIndexFilteredKeys; std::vector<std::string> ratingsIndexFilteredKeys;
std::vector<std::string> favoritesIndexFilteredKeys; std::vector<std::string> favoritesIndexFilteredKeys;
std::vector<std::string> hiddenIndexFilteredKeys;
std::vector<std::string> kidGameIndexFilteredKeys;
FileData* mRootFolder; FileData* mRootFolder;

View file

@ -21,6 +21,8 @@ MetaDataDecl gameDecls[] = {
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre"}, {"genre", MD_STRING, "unknown", false, "genre", "enter game genre"},
{"players", MD_INT, "1", false, "players", "enter number of players"}, {"players", MD_INT, "1", false, "players", "enter number of players"},
{"favorite", MD_BOOL, "false", false, "favorite", "enter favorite off/on"}, {"favorite", MD_BOOL, "false", false, "favorite", "enter favorite off/on"},
{"hidden", MD_BOOL, "false", false, "hidden", "enter hidden off/on" },
{"kidgame", MD_BOOL, "false", false, "kidgame", "enter kidgame off/on" },
{"playcount", MD_INT, "0", true, "play count", "enter number of times played"}, {"playcount", MD_INT, "0", true, "play count", "enter number of times played"},
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date"} {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date"}
}; };

View file

@ -340,6 +340,33 @@ std::string SystemData::getConfigPath(bool forWrite)
return "/etc/emulationstation/es_systems.cfg"; return "/etc/emulationstation/es_systems.cfg";
} }
SystemData* SystemData::getNext() const
{
std::vector<SystemData*>::const_iterator it = getIterator();
do {
it++;
if (it == sSystemVector.end())
it = sSystemVector.begin();
} while ((*it)->getDisplayedGameCount() == 0);
// as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle.
return *it;
}
SystemData* SystemData::getPrev() const
{
auto it = getRevIterator();
do {
it++;
if (it == sSystemVector.rend())
it = sSystemVector.rbegin();
} while ((*it)->getDisplayedGameCount() == 0);
// as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle.
return *it;
}
std::string SystemData::getGamelistPath(bool forWrite) const std::string SystemData::getGamelistPath(bool forWrite) const
{ {
fs::path filePath; fs::path filePath;

View file

@ -56,22 +56,9 @@ public:
inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.rbegin(), sSystemVector.rend(), this); }; inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.rbegin(), sSystemVector.rend(), this); };
inline bool isCollection() { return mIsCollectionSystem; }; inline bool isCollection() { return mIsCollectionSystem; };
inline bool isGameSystem() { return mIsGameSystem; } inline bool isGameSystem() { return mIsGameSystem; }
inline SystemData* getNext() const
{ SystemData* getNext() const;
auto it = getIterator(); SystemData* getPrev() const;
it++;
if(it == sSystemVector.end()) it = sSystemVector.begin();
return *it;
}
inline SystemData* getPrev() const
{
auto it = getRevIterator();
it++;
if(it == sSystemVector.rend()) it = sSystemVector.rbegin();
return *it;
}
static SystemData* getRandomSystem(); static SystemData* getRandomSystem();
FileData* getRandomGame(); FileData* getRandomGame();

View file

@ -1,6 +1,7 @@
#include "guis/GuiGamelistFilter.h" #include "guis/GuiGamelistFilter.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "views/ViewController.h"
#include "SystemData.h" #include "SystemData.h"
GuiGamelistFilter::GuiGamelistFilter(Window* window, SystemData* system) : GuiComponent(window), mMenu(window, "FILTER GAMELIST BY"), mSystem(system) GuiGamelistFilter::GuiGamelistFilter(Window* window, SystemData* system) : GuiComponent(window), mMenu(window, "FILTER GAMELIST BY"), mSystem(system)
@ -34,7 +35,7 @@ void GuiGamelistFilter::initializeMenu()
void GuiGamelistFilter::resetAllFilters() void GuiGamelistFilter::resetAllFilters()
{ {
mFilterIndex->clearAllFilters(); mFilterIndex->resetFilters();
for (std::map<FilterIndexType, std::shared_ptr< OptionListComponent<std::string> >>::iterator it = mFilterOptions.begin(); it != mFilterOptions.end(); ++it ) { 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::shared_ptr< OptionListComponent<std::string> > optionList = it->second;
optionList->selectNone(); optionList->selectNone();
@ -49,7 +50,14 @@ GuiGamelistFilter::~GuiGamelistFilter()
void GuiGamelistFilter::addFiltersToMenu() void GuiGamelistFilter::addFiltersToMenu()
{ {
std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls(); std::vector<FilterDataDecl> decls = mFilterIndex->getFilterDataDecls();
for (std::vector<FilterDataDecl>::iterator it = decls.begin(); it != decls.end(); ++it ) {
int skip = 0;
if (!ViewController::get()->isUIModeFull())
skip = 1;
if (ViewController::get()->isUIModeKid())
skip = 2;
for (std::vector<FilterDataDecl>::iterator it = decls.begin(); it != decls.end()-skip; ++it ) {
FilterIndexType type = (*it).type; // type of filter FilterIndexType type = (*it).type; // type of filter
std::map<std::string, int>* allKeys = (*it).allIndexKeys; // all possible filters for this type std::map<std::string, int>* allKeys = (*it).allIndexKeys; // all possible filters for this type

View file

@ -38,7 +38,8 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
if (isFullUI) if (isFullUI)
addEntry("CONFIGURE INPUT", 0x777777FF, true, [this] { openConfigInput(); }); addEntry("CONFIGURE INPUT", 0x777777FF, true, [this] { openConfigInput(); });
addEntry("QUIT", 0x777777FF, true, [this] {openQuitMenu(); }); if (!(ViewController::get()->isUIModeKid() && Settings::getInstance()->getBool("hideQuitMenuOnKidUI")))
addEntry("QUIT", 0x777777FF, true, [this] {openQuitMenu(); });
addChild(&mMenu); addChild(&mMenu);
addVersionInfo(); addVersionInfo();
@ -422,12 +423,11 @@ void GuiMenu::openQuitMenu()
s->addRow(row); s->addRow(row);
} }
} }
row.elements.clear(); row.elements.clear();
row.makeAcceptInputHandler([window] { row.makeAcceptInputHandler([window] {
window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES",
[] { [] {
if(quitES("/tmp/es-sysrestart") != 0) if (quitES("/tmp/es-sysrestart") != 0)
LOG(LogWarning) << "Restart terminated with non-zero result!"; LOG(LogWarning) << "Restart terminated with non-zero result!";
}, "NO", nullptr)); }, "NO", nullptr));
}); });
@ -455,7 +455,6 @@ void GuiMenu::addVersionInfo()
mVersion.setText("EMULATIONSTATION V" + strToUpper(PROGRAM_VERSION_STRING)); mVersion.setText("EMULATIONSTATION V" + strToUpper(PROGRAM_VERSION_STRING));
mVersion.setHorizontalAlignment(ALIGN_CENTER); mVersion.setHorizontalAlignment(ALIGN_CENTER);
addChild(&mVersion); addChild(&mVersion);
} }
void GuiMenu::openScreensaverOptions() { void GuiMenu::openScreensaverOptions() {

View file

@ -86,7 +86,12 @@ bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height
else if (strcmp(argv[i], "--force-kiosk") == 0) else if (strcmp(argv[i], "--force-kiosk") == 0)
{ {
Settings::getInstance()->setBool("ForceKiosk", true); Settings::getInstance()->setBool("ForceKiosk", true);
}else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) }
else if (strcmp(argv[i], "--force-kid") == 0)
{
Settings::getInstance()->setBool("ForceKid", true);
}
else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
{ {
#ifdef WIN32 #ifdef WIN32
// This is a bit of a hack, but otherwise output will go to nowhere // This is a bit of a hack, but otherwise output will go to nowhere

View file

@ -1,6 +1,7 @@
#include "views/SystemView.h" #include "views/SystemView.h"
#include "animations/LambdaAnimation.h" #include "animations/LambdaAnimation.h"
#include "guis/GuiMsgBox.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "Log.h" #include "Log.h"
#include "Renderer.h" #include "Renderer.h"
@ -35,78 +36,88 @@ void SystemView::populate()
if(mViewNeedsReload) if(mViewNeedsReload)
getViewElements(theme); getViewElements(theme);
Entry e; if((*it)->getDisplayedGameCount() > 0)
e.name = (*it)->getName();
e.object = *it;
// make logo
const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image");
if(logoElem)
{ {
std::string path = logoElem->get<std::string>("path"); Entry e;
std::string defaultPath = logoElem->has("default") ? logoElem->get<std::string>("default") : ""; e.name = (*it)->getName();
if((!path.empty() && ResourceManager::getInstance()->fileExists(path)) e.object = *it;
|| (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath)))
// make logo
const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image");
if(logoElem)
{ {
ImageComponent* logo = new ImageComponent(mWindow, false, false); std::string path = logoElem->get<std::string>("path");
logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); std::string defaultPath = logoElem->has("default") ? logoElem->get<std::string>("default") : "";
logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR); if((!path.empty() && ResourceManager::getInstance()->fileExists(path))
|| (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath)))
e.data.logo = std::shared_ptr<GuiComponent>(logo); {
ImageComponent* logo = new ImageComponent(mWindow, false, false);
logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale);
logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR);
e.data.logo = std::shared_ptr<GuiComponent>(logo);
}
}
if (!e.data.logo)
{
// no logo in theme; use text
TextComponent* text = new TextComponent(mWindow,
(*it)->getName(),
Font::get(FONT_SIZE_LARGE),
0x000000FF,
ALIGN_CENTER);
text->setSize(mCarousel.logoSize * mCarousel.logoScale);
text->applyTheme((*it)->getTheme(), "system", "logoText", ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FORCE_UPPERCASE);
e.data.logo = std::shared_ptr<GuiComponent>(text);
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL)
text->setHorizontalAlignment(mCarousel.logoAlignment);
else
text->setVerticalAlignment(mCarousel.logoAlignment);
} }
}
if (!e.data.logo)
{
// no logo in theme; use text
TextComponent* text = new TextComponent(mWindow,
(*it)->getName(),
Font::get(FONT_SIZE_LARGE),
0x000000FF,
ALIGN_CENTER);
text->setSize(mCarousel.logoSize * mCarousel.logoScale);
text->applyTheme((*it)->getTheme(), "system", "logoText", ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FORCE_UPPERCASE);
e.data.logo = std::shared_ptr<GuiComponent>(text);
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL)
text->setHorizontalAlignment(mCarousel.logoAlignment); {
else if (mCarousel.logoAlignment == ALIGN_LEFT)
text->setVerticalAlignment(mCarousel.logoAlignment); e.data.logo->setOrigin(0, 0.5);
} else if (mCarousel.logoAlignment == ALIGN_RIGHT)
e.data.logo->setOrigin(1.0, 0.5);
else
e.data.logo->setOrigin(0.5, 0.5);
} else {
if (mCarousel.logoAlignment == ALIGN_TOP)
e.data.logo->setOrigin(0.5, 0);
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
e.data.logo->setOrigin(0.5, 1);
else
e.data.logo->setOrigin(0.5, 0.5);
}
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin();
e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0);
// delete any existing extras
for (auto extra : e.data.backgroundExtras)
delete extra;
e.data.backgroundExtras.clear();
// make background extras
e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow);
// sort the extras by z-index
std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) {
return b->getZIndex() > a->getZIndex();
});
this->add(e);
}
}
if (mEntries.size() == 0)
{
// Something is wrong, there is not a single system to show, check if UI mode is not full
if (!ViewController::get()->isUIModeFull())
{ {
if (mCarousel.logoAlignment == ALIGN_LEFT) Settings::getInstance()->setString("UIMode", "Full");
e.data.logo->setOrigin(0, 0.5); mWindow->pushGui(new GuiMsgBox(mWindow, "The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK", nullptr));
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
e.data.logo->setOrigin(1.0, 0.5);
else
e.data.logo->setOrigin(0.5, 0.5);
} else {
if (mCarousel.logoAlignment == ALIGN_TOP)
e.data.logo->setOrigin(0.5, 0);
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
e.data.logo->setOrigin(0.5, 1);
else
e.data.logo->setOrigin(0.5, 0.5);
} }
Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin();
e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0);
// delete any existing extras
for (auto extra : e.data.backgroundExtras)
delete extra;
e.data.backgroundExtras.clear();
// make background extras
e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow);
// sort the extras by z-index
std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) {
return b->getZIndex() > a->getZIndex();
});
this->add(e);
} }
} }

View file

@ -9,6 +9,7 @@
#include "views/gamelist/IGameListView.h" #include "views/gamelist/IGameListView.h"
#include "views/gamelist/VideoGameListView.h" #include "views/gamelist/VideoGameListView.h"
#include "views/SystemView.h" #include "views/SystemView.h"
#include "FileFilterIndex.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
@ -393,7 +394,7 @@ void ViewController::render(const Transform4x4f& parentTrans)
if(guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() && if(guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() &&
guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y())
it->second->render(trans); it->second->render(trans);
} }
if(mWindow->peekGui() == this) if(mWindow->peekGui() == this)
@ -411,6 +412,7 @@ void ViewController::preload()
{ {
for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++)
{ {
(*it)->getIndex()->resetFilters();
getGameListView(*it); getGameListView(*it);
} }
} }
@ -448,6 +450,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
void ViewController::reloadAll() void ViewController::reloadAll()
{ {
// clear all gamelistviews
std::map<SystemData*, FileData*> cursorMap; std::map<SystemData*, FileData*> cursorMap;
for(auto it = mGameListViews.begin(); it != mGameListViews.end(); it++) for(auto it = mGameListViews.begin(); it != mGameListViews.end(); it++)
{ {
@ -455,12 +458,16 @@ void ViewController::reloadAll()
} }
mGameListViews.clear(); mGameListViews.clear();
// load themes, create gamelistviews and reset filters
for(auto it = cursorMap.begin(); it != cursorMap.end(); it++) for(auto it = cursorMap.begin(); it != cursorMap.end(); it++)
{ {
it->first->loadTheme(); it->first->loadTheme();
it->first->getIndex()->resetFilters();
getGameListView(it->first)->setCursor(it->second); getGameListView(it->first)->setCursor(it->second);
} }
// Rebuild SystemListView
mSystemListView.reset(); mSystemListView.reset();
getSystemListView(); getSystemListView();
@ -486,6 +493,7 @@ void ViewController::monitorUIMode()
std::string uimode = Settings::getInstance()->getString("UIMode"); std::string uimode = Settings::getInstance()->getString("UIMode");
if (uimode != mCurUIMode) // UIMODE HAS CHANGED if (uimode != mCurUIMode) // UIMODE HAS CHANGED
{ {
mWindow->renderLoadingScreen();
mCurUIMode = uimode; mCurUIMode = uimode;
reloadAll(); reloadAll();
goToStart(); goToStart();
@ -497,6 +505,12 @@ bool ViewController::isUIModeFull()
return ((mCurUIMode == "Full") && ! Settings::getInstance()->getBool("ForceKiosk")); return ((mCurUIMode == "Full") && ! Settings::getInstance()->getBool("ForceKiosk"));
} }
bool ViewController::isUIModeKid()
{
return (Settings::getInstance()->getBool("ForceKid") ||
((mCurUIMode == "Kid") && !Settings::getInstance()->getBool("ForceKiosk")));
}
std::vector<HelpPrompt> ViewController::getHelpPrompts() std::vector<HelpPrompt> ViewController::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts; std::vector<HelpPrompt> prompts;

View file

@ -11,7 +11,7 @@ class IGameListView;
class SystemData; class SystemData;
class SystemView; class SystemView;
const std::vector<std::string> UIModes = { "Full", "Kiosk" }; const std::vector<std::string> UIModes = { "Full", "Kiosk", "Kid" };
// Used to smoothly transition the camera between multiple views (e.g. from system to system, from gamelist to gamelist). // Used to smoothly transition the camera between multiple views (e.g. from system to system, from gamelist to gamelist).
class ViewController : public GuiComponent class ViewController : public GuiComponent
@ -33,8 +33,9 @@ public:
void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes. void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes.
void monitorUIMode(); void monitorUIMode();
bool isUIModeFull();
inline std::vector<std::string> getUIModes() { return UIModes; }; inline std::vector<std::string> getUIModes() { return UIModes; };
bool isUIModeFull();
bool isUIModeKid();
// Navigation. // Navigation.
void goToNextGameList(); void goToNextGameList();

View file

@ -141,7 +141,7 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
prompts.push_back(HelpPrompt("b", "back")); prompts.push_back(HelpPrompt("b", "back"));
prompts.push_back(HelpPrompt("select", "options")); prompts.push_back(HelpPrompt("select", "options"));
prompts.push_back(HelpPrompt("x", "random")); prompts.push_back(HelpPrompt("x", "random"));
if(mRoot->getSystem()->isGameSystem()) if(mRoot->getSystem()->isGameSystem() && !ViewController::get()->isUIModeKid())
{ {
std::string prompt = CollectionSystemManager::get()->getEditingCollection(); std::string prompt = CollectionSystemManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));

View file

@ -142,7 +142,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
setCursor(randomGame); setCursor(randomGame);
} }
return true; return true;
}else if (config->isMappedTo("y", input)) }else if (config->isMappedTo("y", input) && !(ViewController::get()->isUIModeKid()))
{ {
if(mRoot->getSystem()->isGameSystem()) if(mRoot->getSystem()->isGameSystem())
{ {

View file

@ -14,6 +14,7 @@ std::vector<const char*> settings_dont_save = boost::assign::list_of
("Debug") ("Debug")
("DebugGrid") ("DebugGrid")
("DebugText") ("DebugText")
("ForceKid")
("ForceKiosk") ("ForceKiosk")
("IgnoreGamelist") ("IgnoreGamelist")
("HideConsole") ("HideConsole")
@ -124,6 +125,8 @@ void Settings::setDefaults()
mStringMap["UIMode"] = "Full"; mStringMap["UIMode"] = "Full";
mStringMap["UIMode_passkey"] = "uuddlrlrba"; mStringMap["UIMode_passkey"] = "uuddlrlrba";
mBoolMap["ForceKiosk"] = false; mBoolMap["ForceKiosk"] = false;
mBoolMap["ForceKid"] = false;
mBoolMap["hideQuitMenuOnKidUI"] = false;
} }
template <typename K, typename V> template <typename K, typename V>