Merge pull request #252 from zigurana/KidMode

UI modes part 2: Kid mode (File Filtering and system hiding)
This commit is contained in:
Jools Wills 2017-11-02 21:17:51 +00:00 committed by GitHub
commit c70acc88de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 255 additions and 104 deletions

View file

@ -1,4 +1,6 @@
#include "FileFilterIndex.h"
#include "Settings.h"
#include "views/ViewController.h"
#include "FileData.h"
#include "Log.h"
@ -9,15 +11,18 @@
#define INCLUDE_UNKNOWN false;
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[] = {
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
{ FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" },
{ 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" }
{ 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]));
@ -46,7 +51,9 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
{ &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) },
{ &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) },
{ &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]));
@ -75,6 +82,8 @@ void FileFilterIndex::resetIndex()
clearIndex(pubDevIndexAllKeys);
clearIndex(ratingsIndexAllKeys);
clearIndex(favoritesIndexAllKeys);
clearIndex(hiddenIndexAllKeys);
clearIndex(kidGameIndexAllKeys);
}
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"));
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);
if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) {
@ -164,6 +187,8 @@ void FileFilterIndex::addToIndex(FileData* game)
managePubDevEntryInIndex(game);
manageRatingsEntryInIndex(game);
manageFavoritesEntryInIndex(game);
manageHiddenEntryInIndex(game);
manageKidGameEntryInIndex(game);
}
void FileFilterIndex::removeFromIndex(FileData* game)
@ -173,6 +198,8 @@ void FileFilterIndex::removeFromIndex(FileData* game)
managePubDevEntryInIndex(game, true);
manageRatingsEntryInIndex(game, true);
manageFavoritesEntryInIndex(game, true);
manageHiddenEntryInIndex(game, true);
manageKidGameEntryInIndex(game, true);
}
void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>* values)
@ -213,6 +240,28 @@ void FileFilterIndex::clearAllFilters()
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()
{
LOG(LogInfo) << "Printing Indexes...";
@ -231,6 +280,12 @@ void FileFilterIndex::debugPrintIndexes()
for (auto x: favoritesIndexAllKeys) {
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;
}
}
bool FileFilterIndex::showFile(FileData* game)
@ -290,10 +345,10 @@ bool FileFilterIndex::showFile(FileData* game)
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type)
{
const FilterIndexType filterTypes[5] = { FAVORITES_FILTER, PLAYER_FILTER, RATINGS_FILTER, GENRE_FILTER, PUBDEV_FILTER };
std::vector<std::string> filterKeysList[5] = { favoritesIndexFilteredKeys, playersIndexFilteredKeys, ratingsIndexFilteredKeys, genreIndexFilteredKeys, pubDevIndexFilteredKeys };
const FilterIndexType filterTypes[7] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER,HIDDEN_FILTER, KIDGAME_FILTER };
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)
{
@ -416,6 +471,32 @@ void FileFilterIndex::manageFavoritesEntryInIndex(FileData* game, bool 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) {
bool includeUnknown = INCLUDE_UNKNOWN;
if (!includeUnknown && key == UNKNOWN_LABEL)

View file

@ -14,7 +14,9 @@ enum FilterIndexType
PLAYER_FILTER,
PUBDEV_FILTER,
RATINGS_FILTER,
FAVORITES_FILTER
FAVORITES_FILTER,
HIDDEN_FILTER,
KIDGAME_FILTER
};
struct FilterDataDecl
@ -40,12 +42,15 @@ public:
void clearAllFilters();
void debugPrintIndexes();
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);
std::vector<FilterDataDecl>& getFilterDataDecls();
void importIndex(FileFilterIndex* indexToImport);
void resetIndex();
void resetFilters();
void setUIModeFilters();
private:
std::vector<FilterDataDecl> filterDataDecl;
std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary);
@ -55,6 +60,8 @@ private:
void managePubDevEntryInIndex(FileData* game, bool remove = false);
void manageRatingsEntryInIndex(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);
@ -65,18 +72,24 @@ private:
bool filterByPubDev;
bool filterByRatings;
bool filterByFavorites;
bool filterByHidden;
bool filterByKidGame;
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::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> playersIndexFilteredKeys;
std::vector<std::string> pubDevIndexFilteredKeys;
std::vector<std::string> ratingsIndexFilteredKeys;
std::vector<std::string> favoritesIndexFilteredKeys;
std::vector<std::string> hiddenIndexFilteredKeys;
std::vector<std::string> kidGameIndexFilteredKeys;
FileData* mRootFolder;

View file

@ -21,6 +21,8 @@ MetaDataDecl gameDecls[] = {
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre"},
{"players", MD_INT, "1", false, "players", "enter number of players"},
{"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"},
{"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";
}
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
{
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 bool isCollection() { return mIsCollectionSystem; };
inline bool isGameSystem() { return mIsGameSystem; }
inline SystemData* getNext() const
{
auto it = getIterator();
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;
}
SystemData* getNext() const;
SystemData* getPrev() const;
static SystemData* getRandomSystem();
FileData* getRandomGame();

View file

@ -1,6 +1,7 @@
#include "guis/GuiGamelistFilter.h"
#include "components/OptionListComponent.h"
#include "views/ViewController.h"
#include "SystemData.h"
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()
{
mFilterIndex->clearAllFilters();
mFilterIndex->resetFilters();
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();
@ -49,7 +50,14 @@ GuiGamelistFilter::~GuiGamelistFilter()
void GuiGamelistFilter::addFiltersToMenu()
{
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
std::map<std::string, int>* allKeys = (*it).allIndexKeys; // all possible filters for this type

View file

@ -38,6 +38,7 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
if (isFullUI)
addEntry("CONFIGURE INPUT", 0x777777FF, true, [this] { openConfigInput(); });
if (!(ViewController::get()->isUIModeKid() && Settings::getInstance()->getBool("hideQuitMenuOnKidUI")))
addEntry("QUIT", 0x777777FF, true, [this] {openQuitMenu(); });
addChild(&mMenu);
@ -422,7 +423,6 @@ void GuiMenu::openQuitMenu()
s->addRow(row);
}
}
row.elements.clear();
row.makeAcceptInputHandler([window] {
window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES",
@ -455,7 +455,6 @@ void GuiMenu::addVersionInfo()
mVersion.setText("EMULATIONSTATION V" + strToUpper(PROGRAM_VERSION_STRING));
mVersion.setHorizontalAlignment(ALIGN_CENTER);
addChild(&mVersion);
}
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)
{
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
// 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 "animations/LambdaAnimation.h"
#include "guis/GuiMsgBox.h"
#include "views/ViewController.h"
#include "Log.h"
#include "Renderer.h"
@ -35,6 +36,8 @@ void SystemView::populate()
if(mViewNeedsReload)
getViewElements(theme);
if((*it)->getDisplayedGameCount() > 0)
{
Entry e;
e.name = (*it)->getName();
e.object = *it;
@ -51,7 +54,6 @@ void SystemView::populate()
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);
}
}
@ -92,7 +94,6 @@ void SystemView::populate()
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;
@ -109,6 +110,16 @@ void SystemView::populate()
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())
{
Settings::getInstance()->setString("UIMode", "Full");
mWindow->pushGui(new GuiMsgBox(mWindow, "The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK", nullptr));
}
}
}
void SystemView::goToSystem(SystemData* system, bool animate)
{

View file

@ -9,6 +9,7 @@
#include "views/gamelist/IGameListView.h"
#include "views/gamelist/VideoGameListView.h"
#include "views/SystemView.h"
#include "FileFilterIndex.h"
#include "Log.h"
#include "Settings.h"
#include "SystemData.h"
@ -411,6 +412,7 @@ void ViewController::preload()
{
for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++)
{
(*it)->getIndex()->resetFilters();
getGameListView(*it);
}
}
@ -448,6 +450,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
void ViewController::reloadAll()
{
// clear all gamelistviews
std::map<SystemData*, FileData*> cursorMap;
for(auto it = mGameListViews.begin(); it != mGameListViews.end(); it++)
{
@ -455,12 +458,16 @@ void ViewController::reloadAll()
}
mGameListViews.clear();
// load themes, create gamelistviews and reset filters
for(auto it = cursorMap.begin(); it != cursorMap.end(); it++)
{
it->first->loadTheme();
it->first->getIndex()->resetFilters();
getGameListView(it->first)->setCursor(it->second);
}
// Rebuild SystemListView
mSystemListView.reset();
getSystemListView();
@ -486,6 +493,7 @@ void ViewController::monitorUIMode()
std::string uimode = Settings::getInstance()->getString("UIMode");
if (uimode != mCurUIMode) // UIMODE HAS CHANGED
{
mWindow->renderLoadingScreen();
mCurUIMode = uimode;
reloadAll();
goToStart();
@ -497,6 +505,12 @@ bool ViewController::isUIModeFull()
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> prompts;

View file

@ -11,7 +11,7 @@ class IGameListView;
class SystemData;
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).
class ViewController : public GuiComponent
@ -33,8 +33,9 @@ public:
void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes.
void monitorUIMode();
bool isUIModeFull();
inline std::vector<std::string> getUIModes() { return UIModes; };
bool isUIModeFull();
bool isUIModeKid();
// Navigation.
void goToNextGameList();

View file

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

View file

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

View file

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