mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +00:00
Merge pull request #210 from pjft/Custom-Collections
Added support for custom Game Collection creation
This commit is contained in:
commit
73280d2533
File diff suppressed because it is too large
Load diff
|
@ -34,6 +34,8 @@ struct CollectionSystemData
|
|||
SystemData* system;
|
||||
CollectionSystemDecl decl;
|
||||
bool isEnabled;
|
||||
bool isPopulated;
|
||||
bool needsSave;
|
||||
};
|
||||
|
||||
class CollectionSystemManager
|
||||
|
@ -41,34 +43,73 @@ class CollectionSystemManager
|
|||
public:
|
||||
CollectionSystemManager(Window* window);
|
||||
~CollectionSystemManager();
|
||||
static void init(Window* window);
|
||||
|
||||
static CollectionSystemManager* get();
|
||||
void loadEnabledListFromSettings();
|
||||
static void init(Window* window);
|
||||
static void deinit();
|
||||
void saveCustomCollection(SystemData* sys);
|
||||
|
||||
void loadCollectionSystems();
|
||||
void updateCollectionSystems(FileData* file);
|
||||
void deleteCollectionFiles(FileData* file);
|
||||
inline std::map<std::string, CollectionSystemData> getCollectionSystems() { return mAllCollectionSystems; };
|
||||
void loadEnabledListFromSettings();
|
||||
void updateSystemsList();
|
||||
bool isThemeAutoCompatible();
|
||||
bool toggleGameInCollection(FileData* file, std::string collection);
|
||||
|
||||
void refreshCollectionSystems(FileData* file);
|
||||
void updateCollectionSystem(FileData* file, CollectionSystemData sysData);
|
||||
void deleteCollectionFiles(FileData* file);
|
||||
|
||||
inline std::map<std::string, CollectionSystemData> getAutoCollectionSystems() { return mAutoCollectionSystemsData; };
|
||||
inline std::map<std::string, CollectionSystemData> getCustomCollectionSystems() { return mCustomCollectionSystemsData; };
|
||||
inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; };
|
||||
std::vector<std::string> getUnusedSystemsFromTheme();
|
||||
SystemData* addNewCustomCollection(std::string name);
|
||||
|
||||
bool isThemeGenericCollectionCompatible(bool genericCustomCollections);
|
||||
bool isThemeCustomCollectionCompatible(std::vector<std::string> stringVector);
|
||||
std::string getValidNewCollectionName(std::string name, int index = 0);
|
||||
|
||||
void setEditMode(std::string collectionName);
|
||||
void exitEditMode();
|
||||
inline bool isEditing() { return mIsEditingCustom; };
|
||||
inline std::string getEditingCollection() { std::string res = mEditingCollection; return res; };
|
||||
bool toggleGameInCollection(FileData* file);
|
||||
|
||||
SystemData* getSystemToView(SystemData* sys);
|
||||
void updateCollectionFolderMetadata(SystemData* sys);
|
||||
|
||||
private:
|
||||
static CollectionSystemManager* sInstance;
|
||||
std::map<std::string, CollectionSystemDecl> mCollectionSystemDecls;
|
||||
SystemEnvironmentData* mCollectionEnvData;
|
||||
static FileData::SortType getSortType(std::string desc);
|
||||
void initAvailableSystemsList();
|
||||
std::map<std::string, CollectionSystemDecl> mCollectionSystemDeclsIndex;
|
||||
std::map<std::string, CollectionSystemData> mAutoCollectionSystemsData;
|
||||
std::map<std::string, CollectionSystemData> mCustomCollectionSystemsData;
|
||||
Window* mWindow;
|
||||
bool mIsEditingCustom;
|
||||
std::string mEditingCollection;
|
||||
CollectionSystemData* mEditingCollectionSystemData;
|
||||
|
||||
void initAutoCollectionSystems();
|
||||
void initCustomCollectionSystems();
|
||||
SystemData* getAllGamesCollection();
|
||||
SystemData* createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, bool index = true);
|
||||
void populateAutoCollection(CollectionSystemData* sysData);
|
||||
void populateCustomCollection(CollectionSystemData* sysData);
|
||||
|
||||
void removeCollectionsFromDisplayedSystems();
|
||||
void addEnabledCollectionsToDisplayedSystems(std::map<std::string, CollectionSystemData>* colSystemData);
|
||||
|
||||
std::vector<std::string> getSystemsFromConfig();
|
||||
std::vector<std::string> getSystemsFromTheme();
|
||||
std::vector<std::string> getUnusedSystemsFromTheme();
|
||||
std::vector<std::string> getAutoThemeFolders();
|
||||
std::vector<std::string> getCollectionsFromConfigFolder();
|
||||
std::vector<std::string> getCollectionThemeFolders(bool custom);
|
||||
std::vector<std::string> getUserCollectionThemeFolders();
|
||||
|
||||
bool themeFolderExists(std::string folder);
|
||||
void loadAutoCollectionSystems();
|
||||
void loadCustomCollectionSystems(); // TO DO NEXT
|
||||
SystemData* findCollectionSystem(std::string name);
|
||||
|
||||
bool includeFileInAutoCollections(FileData* file);
|
||||
std::map<std::string, CollectionSystemData> mAllCollectionSystems;
|
||||
std::vector<SystemData*> mAutoCollectionSystems;
|
||||
std::vector<SystemData*> mCustomCollectionSystems;
|
||||
Window* mWindow;
|
||||
|
||||
SystemData* mCustomCollectionsBundle;
|
||||
};
|
||||
|
||||
std::string getCustomCollectionConfigPath(std::string collectionName);
|
||||
std::string getCollectionsFolder();
|
||||
bool systemSort(SystemData* sys1, SystemData* sys2);
|
||||
|
|
|
@ -23,7 +23,8 @@ FileData::~FileData()
|
|||
if(mParent)
|
||||
mParent->removeChild(this);
|
||||
|
||||
mSystem->getIndex()->removeFromIndex(this);
|
||||
if(mType == GAME)
|
||||
mSystem->getIndex()->removeFromIndex(this);
|
||||
|
||||
mChildren.clear();
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ const std::string& FileData::getName()
|
|||
|
||||
const std::vector<FileData*>& FileData::getChildrenListToDisplay() {
|
||||
|
||||
FileFilterIndex* idx = mSystem->getIndex();
|
||||
FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex();
|
||||
if (idx->isFiltered()) {
|
||||
mFilteredChildren.clear();
|
||||
for(auto it = mChildren.begin(); it != mChildren.end(); it++)
|
||||
|
@ -140,6 +141,7 @@ void FileData::removeChild(FileData* file)
|
|||
{
|
||||
if(*it == file)
|
||||
{
|
||||
file->mParent = NULL;
|
||||
mChildren.erase(it);
|
||||
return;
|
||||
}
|
||||
|
@ -210,14 +212,11 @@ void FileData::launchGame(Window* window)
|
|||
//update last played time
|
||||
boost::posix_time::ptime time = boost::posix_time::second_clock::universal_time();
|
||||
gameToUpdate->metadata.setTime("lastplayed", time);
|
||||
CollectionSystemManager::get()->updateCollectionSystems(gameToUpdate);
|
||||
CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate);
|
||||
}
|
||||
|
||||
CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
|
||||
: FileData(file->getType(), file->getPath(), file->getSystemEnvData(), system)/*,
|
||||
mSourceFileData(file->getSourceFileData()),
|
||||
mParent(NULL),
|
||||
metadata(file->getSourceFileData()->metadata)*/
|
||||
: FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), file->getSourceFileData()->getSystemEnvData(), system)
|
||||
{
|
||||
// we use this constructor to create a clone of the filedata, and change its system
|
||||
mSourceFileData = file->getSourceFileData();
|
||||
|
@ -259,4 +258,20 @@ const std::string& CollectionFileData::getName()
|
|||
mDirty = false;
|
||||
}
|
||||
return mCollectionFileName;
|
||||
}
|
||||
|
||||
// returns Sort Type based on a string description
|
||||
FileData::SortType getSortTypeFromString(std::string desc) {
|
||||
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
|
||||
// find it
|
||||
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++)
|
||||
{
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
||||
if(sort.description == desc)
|
||||
{
|
||||
return sort;
|
||||
}
|
||||
}
|
||||
// if not found default to name, ascending
|
||||
return FileSorts::SortTypes.at(0);
|
||||
}
|
|
@ -114,4 +114,6 @@ private:
|
|||
// needs to be updated when metadata changes
|
||||
std::string mCollectionFileName;
|
||||
bool mDirty;
|
||||
};
|
||||
};
|
||||
|
||||
FileData::SortType getSortTypeFromString(std::string desc);
|
|
@ -20,11 +20,7 @@ FileFilterIndex::FileFilterIndex()
|
|||
|
||||
FileFilterIndex::~FileFilterIndex()
|
||||
{
|
||||
clearIndex(genreIndexAllKeys);
|
||||
clearIndex(playersIndexAllKeys);
|
||||
clearIndex(pubDevIndexAllKeys);
|
||||
clearIndex(ratingsIndexAllKeys);
|
||||
clearIndex(favoritesIndexAllKeys);
|
||||
resetIndex();
|
||||
}
|
||||
|
||||
std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
|
||||
|
@ -32,6 +28,50 @@ std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
|
|||
return filterDataDecl;
|
||||
}
|
||||
|
||||
void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
|
||||
{
|
||||
struct IndexImportStructure
|
||||
{
|
||||
std::map<std::string, int>* destinationIndex;
|
||||
std::map<std::string, int>* sourceIndex;
|
||||
};
|
||||
|
||||
IndexImportStructure indexStructDecls[] = {
|
||||
{ &genreIndexAllKeys, &(indexToImport->genreIndexAllKeys) },
|
||||
{ &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) },
|
||||
{ &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) },
|
||||
{ &ratingsIndexAllKeys, &(indexToImport->ratingsIndexAllKeys) },
|
||||
{ &favoritesIndexAllKeys, &(indexToImport->favoritesIndexAllKeys) }
|
||||
};
|
||||
|
||||
std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0]));
|
||||
|
||||
for (std::vector<IndexImportStructure>::iterator indexesIt = indexImportDecl.begin(); indexesIt != indexImportDecl.end(); ++indexesIt )
|
||||
{
|
||||
for (std::map<std::string, int>::iterator sourceIt = (*indexesIt).sourceIndex->begin(); sourceIt != (*indexesIt).sourceIndex->end(); ++sourceIt )
|
||||
{
|
||||
if ((*indexesIt).destinationIndex->find((*sourceIt).first) == (*indexesIt).destinationIndex->end())
|
||||
{
|
||||
// entry doesn't exist
|
||||
(*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void FileFilterIndex::resetIndex()
|
||||
{
|
||||
clearAllFilters();
|
||||
clearIndex(genreIndexAllKeys);
|
||||
clearIndex(playersIndexAllKeys);
|
||||
clearIndex(pubDevIndexAllKeys);
|
||||
clearIndex(ratingsIndexAllKeys);
|
||||
clearIndex(favoritesIndexAllKeys);
|
||||
}
|
||||
|
||||
std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary)
|
||||
{
|
||||
std::string key = "";
|
||||
|
@ -170,21 +210,21 @@ void FileFilterIndex::clearAllFilters()
|
|||
|
||||
void FileFilterIndex::debugPrintIndexes()
|
||||
{
|
||||
LOG(LogError) << "Printing Indexes...";
|
||||
LOG(LogInfo) << "Printing Indexes...";
|
||||
for (auto x: playersIndexAllKeys) {
|
||||
LOG(LogError) << "Multiplayer Index: " << x.first << ": " << x.second;
|
||||
LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: genreIndexAllKeys) {
|
||||
LOG(LogError) << "Genre Index: " << x.first << ": " << x.second;
|
||||
LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: ratingsIndexAllKeys) {
|
||||
LOG(LogError) << "Ratings Index: " << x.first << ": " << x.second;
|
||||
LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: pubDevIndexAllKeys) {
|
||||
LOG(LogError) << "PubDev Index: " << x.first << ": " << x.second;
|
||||
LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
for (auto x: favoritesIndexAllKeys) {
|
||||
LOG(LogError) << "Favorites Index: " << x.first << ": " << x.second;
|
||||
LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,9 @@ public:
|
|||
bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || filterByRatings || filterByFavorites); };
|
||||
bool isKeyBeingFilteredBy(std::string key, FilterIndexType type);
|
||||
std::vector<FilterDataDecl>& getFilterDataDecls();
|
||||
|
||||
void importIndex(FileFilterIndex* indexToImport);
|
||||
void resetIndex();
|
||||
private:
|
||||
std::vector<FilterDataDecl> filterDataDecl;
|
||||
std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary);
|
||||
|
|
|
@ -426,8 +426,11 @@ FileData* SystemData::getRandomGame()
|
|||
{
|
||||
std::vector<FileData*> list = mRootFolder->getFilesRecursive(GAME, true);
|
||||
unsigned int total = list.size();
|
||||
int target = 0;
|
||||
// get random number in range
|
||||
int target = std::round(((double)std::rand() / (double)RAND_MAX) * (total - 1));
|
||||
if (total == 0)
|
||||
return NULL;
|
||||
target = std::round(((double)std::rand() / (double)RAND_MAX) * (total - 1));
|
||||
return list.at(target);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include "guis/GuiCollectionSystemsOptions.h"
|
||||
#include "guis/GuiMsgBox.h"
|
||||
#include "guis/GuiTextEditPopup.h"
|
||||
#include "Settings.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#include "guis/GuiSettings.h"
|
||||
#include "Util.h"
|
||||
#include "components/TextComponent.h"
|
||||
#include "components/OptionListComponent.h"
|
||||
|
||||
|
@ -15,66 +18,172 @@ void GuiCollectionSystemsOptions::initializeMenu()
|
|||
{
|
||||
addChild(&mMenu);
|
||||
|
||||
// get virtual systems
|
||||
// get collections
|
||||
|
||||
addSystemsToMenu();
|
||||
|
||||
// add "Create New Custom Collection from Theme"
|
||||
|
||||
std::vector<std::string> unusedFolders = CollectionSystemManager::get()->getUnusedSystemsFromTheme();
|
||||
if (unusedFolders.size() > 0)
|
||||
{
|
||||
addEntry("CREATE NEW CUSTOM COLLECTION FROM THEME", 0x777777FF, true,
|
||||
[this, unusedFolders] {
|
||||
auto s = new GuiSettings(mWindow, "SELECT THEME FOLDER");
|
||||
std::shared_ptr< OptionListComponent<std::string> > folderThemes = std::make_shared< OptionListComponent<std::string> >(mWindow, "SELECT THEME FOLDER", true);
|
||||
|
||||
// add Custom Systems
|
||||
for(auto it = unusedFolders.begin() ; it != unusedFolders.end() ; it++ )
|
||||
{
|
||||
ComponentListRow row;
|
||||
std::string name = *it;
|
||||
|
||||
std::function<void()> createCollectionCall = [name, this, s] {
|
||||
createCollection(name);
|
||||
};
|
||||
row.makeAcceptInputHandler(createCollectionCall);
|
||||
|
||||
auto themeFolder = std::make_shared<TextComponent>(mWindow, strToUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||
row.addElement(themeFolder, true);
|
||||
s->addRow(row);
|
||||
}
|
||||
mWindow->pushGui(s);
|
||||
});
|
||||
}
|
||||
|
||||
ComponentListRow row;
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
auto createCustomCollection = [this](const std::string& newVal) {
|
||||
std::string name = newVal;
|
||||
// we need to store the first Gui and remove it, as it'll be deleted by the actual Gui
|
||||
Window* window = mWindow;
|
||||
GuiComponent* topGui = window->peekGui();
|
||||
window->removeGui(topGui);
|
||||
createCollection(name);
|
||||
};
|
||||
row.makeAcceptInputHandler([this, createCustomCollection] {
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, "New Collection Name", "", createCustomCollection, false));
|
||||
});
|
||||
|
||||
mMenu.addRow(row);
|
||||
|
||||
bundleCustomCollections = std::make_shared<SwitchComponent>(mWindow);
|
||||
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
|
||||
mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections);
|
||||
|
||||
sortAllSystemsSwitch = std::make_shared<SwitchComponent>(mWindow);
|
||||
sortAllSystemsSwitch->setState(Settings::getInstance()->getBool("SortAllSystems"));
|
||||
mMenu.addWithLabel("SORT CUSTOM COLLECTIONS AND SYSTEMS", sortAllSystemsSwitch);
|
||||
|
||||
if(CollectionSystemManager::get()->isEditing())
|
||||
{
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" + strToUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this));
|
||||
|
||||
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f);
|
||||
}
|
||||
|
||||
void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func)
|
||||
{
|
||||
std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM);
|
||||
|
||||
// populate the list
|
||||
ComponentListRow row;
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true);
|
||||
|
||||
if(add_arrow)
|
||||
{
|
||||
std::shared_ptr<ImageComponent> bracket = makeArrow(mWindow);
|
||||
row.addElement(bracket, false);
|
||||
}
|
||||
|
||||
row.makeAcceptInputHandler(func);
|
||||
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
void GuiCollectionSystemsOptions::createCollection(std::string inName) {
|
||||
std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName);
|
||||
SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name);
|
||||
customOptionList->add(name, name, true);
|
||||
std::string outAuto = vectorToCommaString(autoOptionList->getSelectedObjects());
|
||||
std::string outCustom = vectorToCommaString(customOptionList->getSelectedObjects());
|
||||
updateSettings(outAuto, outCustom);
|
||||
ViewController::get()->goToSystemView(newSys);
|
||||
|
||||
Window* window = mWindow;
|
||||
CollectionSystemManager::get()->setEditMode(name);
|
||||
while(window->peekGui() && window->peekGui() != ViewController::get())
|
||||
delete window->peekGui();
|
||||
return;
|
||||
}
|
||||
|
||||
void GuiCollectionSystemsOptions::exitEditMode()
|
||||
{
|
||||
CollectionSystemManager::get()->exitEditMode();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
GuiCollectionSystemsOptions::~GuiCollectionSystemsOptions()
|
||||
{
|
||||
//mSystemOptions.clear();
|
||||
|
||||
}
|
||||
|
||||
void GuiCollectionSystemsOptions::addSystemsToMenu()
|
||||
{
|
||||
|
||||
std::map<std::string, CollectionSystemData> vSystems = CollectionSystemManager::get()->getCollectionSystems();
|
||||
std::map<std::string, CollectionSystemData> autoSystems = CollectionSystemManager::get()->getAutoCollectionSystems();
|
||||
|
||||
autoOptionList = std::make_shared< OptionListComponent<std::string> >(mWindow, "SELECT COLLECTIONS", true);
|
||||
|
||||
// add Systems
|
||||
ComponentListRow row;
|
||||
|
||||
for(std::map<std::string, CollectionSystemData>::iterator it = vSystems.begin() ; it != vSystems.end() ; it++ )
|
||||
// add Auto Systems
|
||||
for(std::map<std::string, CollectionSystemData>::iterator it = autoSystems.begin() ; it != autoSystems.end() ; it++ )
|
||||
{
|
||||
autoOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled);
|
||||
}
|
||||
mMenu.addWithLabel("AUTOMATIC COLLECTIONS", autoOptionList);
|
||||
mMenu.addWithLabel("AUTOMATIC GAME COLLECTIONS", autoOptionList);
|
||||
|
||||
std::map<std::string, CollectionSystemData> customSystems = CollectionSystemManager::get()->getCustomCollectionSystems();
|
||||
|
||||
customOptionList = std::make_shared< OptionListComponent<std::string> >(mWindow, "SELECT COLLECTIONS", true);
|
||||
|
||||
// add Custom Systems
|
||||
for(std::map<std::string, CollectionSystemData>::iterator it = customSystems.begin() ; it != customSystems.end() ; it++ )
|
||||
{
|
||||
customOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled);
|
||||
}
|
||||
mMenu.addWithLabel("CUSTOM GAME COLLECTIONS", customOptionList);
|
||||
}
|
||||
|
||||
void GuiCollectionSystemsOptions::applySettings()
|
||||
{
|
||||
std::string out = commaStringToVector(autoOptionList->getSelectedObjects());
|
||||
std::string prev = Settings::getInstance()->getString("CollectionSystemsAuto");
|
||||
if (out != "" && !CollectionSystemManager::get()->isThemeAutoCompatible())
|
||||
std::string outAuto = vectorToCommaString(autoOptionList->getSelectedObjects());
|
||||
std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto");
|
||||
std::string outCustom = vectorToCommaString(customOptionList->getSelectedObjects());
|
||||
std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom");
|
||||
bool outSort = sortAllSystemsSwitch->getState();
|
||||
bool prevSort = Settings::getInstance()->getBool("SortAllSystems");
|
||||
bool outBundle = bundleCustomCollections->getState();
|
||||
bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem");
|
||||
bool needUpdateSettings = prevAuto != outAuto || prevCustom != outCustom || outSort != prevSort || outBundle != prevBundle;
|
||||
if (needUpdateSettings)
|
||||
{
|
||||
mWindow->pushGui(new GuiMsgBox(mWindow,
|
||||
"Your theme does not support game collections. Please update your theme, or ensure that you use a theme that contains the folders:\n\n• auto-favorites\n• auto-lastplayed\n• auto-allgames\n\nDo you still want to enable collections?",
|
||||
"YES", [this, out, prev] {
|
||||
if (prev != out)
|
||||
{
|
||||
updateSettings(out);
|
||||
}
|
||||
delete this; },
|
||||
"NO", [this] { delete this; }));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prev != out)
|
||||
{
|
||||
updateSettings(out);
|
||||
}
|
||||
delete this;
|
||||
updateSettings(outAuto, outCustom);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GuiCollectionSystemsOptions::updateSettings(std::string newSettings)
|
||||
void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, std::string newCustomSettings)
|
||||
{
|
||||
Settings::getInstance()->setString("CollectionSystemsAuto", newSettings);
|
||||
Settings::getInstance()->setString("CollectionSystemsAuto", newAutoSettings);
|
||||
Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings);
|
||||
Settings::getInstance()->setBool("SortAllSystems", sortAllSystemsSwitch->getState());
|
||||
Settings::getInstance()->setBool("UseCustomCollectionsSystem", bundleCustomCollections->getState());
|
||||
Settings::getInstance()->saveFile();
|
||||
CollectionSystemManager::get()->loadEnabledListFromSettings();
|
||||
CollectionSystemManager::get()->updateSystemsList();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "SystemData.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "CollectionSystemManager.h"
|
||||
#include "components/SwitchComponent.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
||||
|
@ -24,8 +25,14 @@ private:
|
|||
void initializeMenu();
|
||||
void applySettings();
|
||||
void addSystemsToMenu();
|
||||
void updateSettings(std::string newSettings);
|
||||
void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func);
|
||||
void updateSettings(std::string newAutoSettings, std::string newCustomSettings);
|
||||
void createCollection(std::string inName);
|
||||
void exitEditMode();
|
||||
std::shared_ptr< OptionListComponent<std::string> > autoOptionList;
|
||||
std::shared_ptr< OptionListComponent<std::string> > customOptionList;
|
||||
std::shared_ptr<SwitchComponent> sortAllSystemsSwitch;
|
||||
std::shared_ptr<SwitchComponent> bundleCustomCollections;
|
||||
MenuComponent mMenu;
|
||||
SystemData* mSystem;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "GuiGamelistOptions.h"
|
||||
#include "GuiMetaDataEd.h"
|
||||
#include "Util.h"
|
||||
#include "views/gamelist/IGameListView.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "CollectionSystemManager.h"
|
||||
|
@ -54,6 +55,33 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui
|
|||
}
|
||||
|
||||
mMenu.addWithLabel("SORT GAMES BY", mListSort);
|
||||
}
|
||||
// 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);
|
||||
|
||||
std::map<std::string, CollectionSystemData> customCollections = CollectionSystemManager::get()->getCustomCollectionSystems();
|
||||
if((customCollections.find(system->getName()) != customCollections.end() && CollectionSystemManager::get()->getEditingCollection() != system->getName()) ||
|
||||
CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == system->getName())
|
||||
{
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
if(CollectionSystemManager::get()->isEditing())
|
||||
{
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" + strToUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
if (!fromPlaceholder && !(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
|
@ -62,13 +90,6 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui
|
|||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// center the menu
|
||||
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
|
||||
|
@ -78,7 +99,7 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
{
|
||||
// apply sort
|
||||
if (!fromPlaceholder) {
|
||||
FileData* root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
||||
FileData* root = mSystem->getRootFolder();
|
||||
root->sort(*mListSort->getSelected()); // will also recursively sort children
|
||||
|
||||
// notify that the root folder was sorted
|
||||
|
@ -86,17 +107,10 @@ GuiGamelistOptions::~GuiGamelistOptions()
|
|||
}
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +121,34 @@ void GuiGamelistOptions::openGamelistFilter()
|
|||
mWindow->pushGui(ggf);
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::startEditMode()
|
||||
{
|
||||
std::string editingSystem = mSystem->getName();
|
||||
// need to check if we're editing the collections bundle, as we will want to edit the selected collection within
|
||||
if(editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName())
|
||||
{
|
||||
FileData* file = getGamelist()->getCursor();
|
||||
// do we have the cursor on a specific collection?
|
||||
if (file->getType() == FOLDER)
|
||||
{
|
||||
editingSystem = file->getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are inside a specific collection. We want to edit that one.
|
||||
editingSystem = file->getSystem()->getName();
|
||||
}
|
||||
}
|
||||
CollectionSystemManager::get()->setEditMode(editingSystem);
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::exitEditMode()
|
||||
{
|
||||
CollectionSystemManager::get()->exitEditMode();
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::openMetaDataEd()
|
||||
{
|
||||
// open metadata editor
|
||||
|
|
|
@ -19,6 +19,8 @@ public:
|
|||
private:
|
||||
void openGamelistFilter();
|
||||
void openMetaDataEd();
|
||||
void startEditMode();
|
||||
void exitEditMode();
|
||||
void jumpToLetter();
|
||||
|
||||
MenuComponent mMenu;
|
||||
|
|
|
@ -234,7 +234,10 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
|
|||
Settings::getInstance()->setString("ThemeSet", theme_set->getSelected());
|
||||
|
||||
if(needReload)
|
||||
{
|
||||
CollectionSystemManager::get()->updateSystemsList();
|
||||
ViewController::get()->reloadAll(); // TODO - replace this with some sort of signal-based implementation
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ void GuiMetaDataEd::save()
|
|||
mSavedCallback();
|
||||
|
||||
// update respective Collection Entries
|
||||
CollectionSystemManager::get()->updateCollectionSystems(mScraperParams.game);
|
||||
CollectionSystemManager::get()->refreshCollectionSystems(mScraperParams.game);
|
||||
}
|
||||
|
||||
void GuiMetaDataEd::fetch()
|
||||
|
|
|
@ -376,6 +376,7 @@ int main(int argc, char* argv[])
|
|||
delete window.peekGui();
|
||||
window.deinit();
|
||||
|
||||
CollectionSystemManager::deinit();
|
||||
SystemData::deleteSystems();
|
||||
|
||||
LOG(LogInfo) << "EmulationStation cleanly shutting down.";
|
||||
|
|
|
@ -271,6 +271,17 @@ void ViewController::launch(FileData* game, Eigen::Vector3f center)
|
|||
}
|
||||
}
|
||||
|
||||
void ViewController::removeGameListView(SystemData* system)
|
||||
{
|
||||
//if we already made one, return that one
|
||||
auto exists = mGameListViews.find(system);
|
||||
if(exists != mGameListViews.end())
|
||||
{
|
||||
exists->second.reset();
|
||||
mGameListViews.erase(system);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* system)
|
||||
{
|
||||
//if we already made one, return that one
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
|
||||
std::shared_ptr<IGameListView> getGameListView(SystemData* system);
|
||||
std::shared_ptr<SystemView> getSystemListView();
|
||||
void removeGameListView(SystemData* system);
|
||||
|
||||
private:
|
||||
ViewController(Window* window);
|
||||
|
|
|
@ -42,10 +42,9 @@ void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
|
|||
void BasicGameListView::populateList(const std::vector<FileData*>& files)
|
||||
{
|
||||
mList.clear();
|
||||
mHeaderText.setText(mRoot->getSystem()->getFullName());
|
||||
if (files.size() > 0)
|
||||
{
|
||||
mHeaderText.setText(files.at(0)->getSystem()->getFullName());
|
||||
|
||||
for(auto it = files.begin(); it != files.end(); it++)
|
||||
{
|
||||
mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER));
|
||||
|
@ -143,7 +142,10 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
|
|||
prompts.push_back(HelpPrompt("b", "back"));
|
||||
prompts.push_back(HelpPrompt("select", "options"));
|
||||
prompts.push_back(HelpPrompt("x", "random"));
|
||||
if(Settings::getInstance()->getString("CollectionSystemsAuto").find("favorites") != std::string::npos && mRoot->getSystem()->isGameSystem())
|
||||
prompts.push_back(HelpPrompt("y", "favorite"));
|
||||
if(mRoot->getSystem()->isGameSystem())
|
||||
{
|
||||
const char* prompt = CollectionSystemManager::get()->getEditingCollection().c_str();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
}
|
||||
return prompts;
|
||||
}
|
||||
|
|
|
@ -93,6 +93,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
{
|
||||
mCursorStack.push(cursor);
|
||||
populateList(cursor->getChildrenListToDisplay());
|
||||
FileData* cursor = getCursor();
|
||||
setCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +109,12 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
Sound::getFromTheme(getTheme(), getName(), "back")->play();
|
||||
}else{
|
||||
onFocusLost();
|
||||
ViewController::get()->goToSystemView(getCursor()->getSystem());
|
||||
SystemData* systemToView = getCursor()->getSystem();
|
||||
if (systemToView->isCollection())
|
||||
{
|
||||
systemToView = CollectionSystemManager::get()->getSystemToView(systemToView);
|
||||
}
|
||||
ViewController::get()->goToSystemView(systemToView);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -130,14 +137,17 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}else if (config->isMappedTo("x", input))
|
||||
{
|
||||
// go to random system game
|
||||
setCursor(mRoot->getSystem()->getRandomGame());
|
||||
//ViewController::get()->goToRandomGame();
|
||||
FileData* randomGame = getCursor()->getSystem()->getRandomGame();
|
||||
if (randomGame)
|
||||
{
|
||||
setCursor(randomGame);
|
||||
}
|
||||
return true;
|
||||
}else if (config->isMappedTo("y", input))
|
||||
{
|
||||
if(Settings::getInstance()->getString("CollectionSystemsAuto").find("favorites") != std::string::npos && mRoot->getSystem()->isGameSystem())
|
||||
if(mRoot->getSystem()->isGameSystem())
|
||||
{
|
||||
if(CollectionSystemManager::get()->toggleGameInCollection(getCursor(), "favorites"))
|
||||
if(CollectionSystemManager::get()->toggleGameInCollection(getCursor()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,9 @@ void Settings::setDefaults()
|
|||
// Audio out device for Video playback using OMX player.
|
||||
mStringMap["OMXAudioDev"] = "both";
|
||||
mStringMap["CollectionSystemsAuto"] = "";
|
||||
mStringMap["CollectionSystemsCustom"] = "";
|
||||
mBoolMap["SortAllSystems"] = false;
|
||||
mBoolMap["UseCustomCollectionsSystem"] = true;
|
||||
|
||||
// Audio out device for volume control
|
||||
#ifdef _RPI_
|
||||
|
|
|
@ -296,12 +296,14 @@ std::vector<std::string> commaStringToVector(std::string commaString)
|
|||
// from a comma separated string, get a vector of strings
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, commaString, boost::is_any_of(","));
|
||||
std::sort(strs.begin(), strs.end());
|
||||
return strs;
|
||||
}
|
||||
|
||||
std::string commaStringToVector(std::vector<std::string> stringVector)
|
||||
std::string vectorToCommaString(std::vector<std::string> stringVector)
|
||||
{
|
||||
std::string out = "";
|
||||
std::sort(stringVector.begin(), stringVector.end());
|
||||
// from a vector of system names get comma separated string
|
||||
for(std::vector<std::string>::iterator it = stringVector.begin() ; it != stringVector.end() ; it++ )
|
||||
{
|
||||
|
|
|
@ -46,4 +46,4 @@ std::string removeParenthesis(const std::string& str);
|
|||
std::vector<std::string> commaStringToVector(std::string commaString);
|
||||
|
||||
// turn a vector of strings into a comma-separated string
|
||||
std::string commaStringToVector(std::vector<std::string> stringVector);
|
||||
std::string vectorToCommaString(std::vector<std::string> stringVector);
|
Loading…
Reference in a new issue