ES-DE/es-app/src/guis/GuiCollectionSystemsOptions.cpp
2022-09-03 12:44:49 +02:00

405 lines
20 KiB
C++

// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiCollectionSystemsOptions.cpp
//
// User interface for the game collection settings.
// Submenu to the GuiMenu main menu.
//
#include "guis/GuiCollectionSystemsOptions.h"
#include "CollectionSystemsManager.h"
#include "components/OptionListComponent.h"
#include "components/SwitchComponent.h"
#include "guis/GuiMsgBox.h"
#include "guis/GuiSettings.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h"
#include "utils/StringUtil.h"
#include "views/ViewController.h"
GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(std::string title)
: GuiSettings {title}
, mAddedCustomCollection {false}
, mDeletedCustomCollection {false}
{
// Finish editing custom collection.
if (CollectionSystemsManager::getInstance()->isEditing()) {
ComponentListRow row;
row.addElement(
std::make_shared<TextComponent>(
"FINISH EDITING '" +
Utils::String::toUpper(
CollectionSystemsManager::getInstance()->getEditingCollection()) +
"' COLLECTION",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
row.makeAcceptInputHandler([this] {
CollectionSystemsManager::getInstance()->exitEditMode();
mWindow->invalidateCachedBackground();
delete this;
});
addRow(row);
}
// Automatic collections.
mCollectionSystemsAuto = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "SELECT COLLECTIONS", true);
std::map<std::string, CollectionSystemData, StringComparator> autoSystems {
CollectionSystemsManager::getInstance()->getAutoCollectionSystems()};
// Add automatic systems.
for (std::map<std::string, CollectionSystemData, StringComparator>::const_iterator it =
autoSystems.cbegin();
it != autoSystems.cend(); ++it)
mCollectionSystemsAuto->add(it->second.decl.fullName, it->second.decl.name,
it->second.isEnabled);
addWithLabel("AUTOMATIC GAME COLLECTIONS", mCollectionSystemsAuto);
addSaveFunc([this, autoSystems] {
std::string autoSystemsSelected {Utils::String::vectorToDelimitedString(
mCollectionSystemsAuto->getSelectedObjects(), ",", true)};
std::string autoSystemsConfig {Settings::getInstance()->getString("CollectionSystemsAuto")};
if (autoSystemsSelected != autoSystemsConfig) {
if (CollectionSystemsManager::getInstance()->isEditing())
CollectionSystemsManager::getInstance()->exitEditMode();
Settings::getInstance()->setString("CollectionSystemsAuto", autoSystemsSelected);
// Check if any systems have been enabled, and if so repopulate them, which results in
// a complete initialization of their content. This is necessary as collections aren't
// updated while they are disabled.
std::vector<std::string> addedAutoSystems;
if (autoSystemsConfig == "") {
addedAutoSystems = Utils::String::delimitedStringToVector(autoSystemsSelected, ",");
}
else if (autoSystemsSelected != "") {
std::vector<std::string> selectedVector {
Utils::String::delimitedStringToVector(autoSystemsSelected, ",")};
std::vector<std::string> configuredVector {
Utils::String::delimitedStringToVector(autoSystemsConfig, ",")};
for (std::string system : selectedVector) {
if (std::find(configuredVector.begin(), configuredVector.end(), system) ==
configuredVector.end())
addedAutoSystems.push_back(system);
}
}
if (!addedAutoSystems.empty()) {
for (std::string system : addedAutoSystems)
CollectionSystemsManager::getInstance()->repopulateCollection(
autoSystems.find(system)->second.system);
}
setNeedsSaving();
setNeedsReloading();
setNeedsCollectionsUpdate();
}
});
// Custom collections.
mCollectionSystemsCustom = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "SELECT COLLECTIONS", true);
std::map<std::string, CollectionSystemData, StringComparator> customSystems {
CollectionSystemsManager::getInstance()->getCustomCollectionSystems()};
// Add custom systems.
for (std::map<std::string, CollectionSystemData, StringComparator>::const_iterator it =
customSystems.cbegin();
it != customSystems.cend(); ++it)
mCollectionSystemsCustom->add(it->second.decl.fullName, it->second.decl.name,
it->second.isEnabled);
addWithLabel("CUSTOM GAME COLLECTIONS", mCollectionSystemsCustom);
addSaveFunc([this, customSystems] {
if (!mDeletedCustomCollection) {
std::string customSystemsSelected {Utils::String::vectorToDelimitedString(
mCollectionSystemsCustom->getSelectedObjects(), ",", true)};
std::string customSystemsConfig {
Settings::getInstance()->getString("CollectionSystemsCustom")};
if (customSystemsSelected != customSystemsConfig) {
if (CollectionSystemsManager::getInstance()->isEditing())
CollectionSystemsManager::getInstance()->exitEditMode();
Settings::getInstance()->setString("CollectionSystemsCustom",
customSystemsSelected);
// Check if any systems have been enabled, and if so repopulate them, which
// results in a complete initialization of their content. This is necessary as
// collections aren't updated while they are disabled.
std::vector<std::string> addedCustomSystems;
if (customSystemsConfig == "") {
addedCustomSystems =
Utils::String::delimitedStringToVector(customSystemsSelected, ",");
}
else if (customSystemsSelected != "") {
std::vector<std::string> selectedVector {
Utils::String::delimitedStringToVector(customSystemsSelected, ",")};
std::vector<std::string> configuredVector {
Utils::String::delimitedStringToVector(customSystemsConfig, ",")};
for (std::string system : selectedVector) {
if (std::find(configuredVector.begin(), configuredVector.end(), system) ==
configuredVector.end())
addedCustomSystems.push_back(system);
}
}
if (!mAddedCustomCollection && !addedCustomSystems.empty()) {
for (std::string system : addedCustomSystems)
CollectionSystemsManager::getInstance()->repopulateCollection(
customSystems.find(system)->second.system);
}
setNeedsSaving();
setNeedsReloading();
setNeedsCollectionsUpdate();
setNeedsGoToGroupedCollections();
}
}
});
// If there are no custom collections, then gray out this menu entry.
if (customSystems.empty()) {
mCollectionSystemsCustom->setEnabled(false);
mCollectionSystemsCustom->setOpacity(DISABLED_OPACITY);
mCollectionSystemsCustom->getParent()
->getChild(mCollectionSystemsCustom->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Create custom collection from theme.
std::vector<std::string> unusedFolders {
CollectionSystemsManager::getInstance()->getUnusedSystemsFromTheme()};
if (unusedFolders.size() > 0) {
ComponentListRow row;
auto themeCollection = std::make_shared<TextComponent>(
"CREATE NEW CUSTOM COLLECTION FROM THEME", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketThemeCollection = std::make_shared<ImageComponent>();
bracketThemeCollection->setResize(
glm::vec2 {0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
bracketThemeCollection->setImage(":/graphics/arrow.svg");
row.addElement(themeCollection, true);
row.addElement(bracketThemeCollection, false);
row.makeAcceptInputHandler([this, unusedFolders] {
auto ss = new GuiSettings("SELECT THEME FOLDER");
std::shared_ptr<OptionListComponent<std::string>> folderThemes {
std::make_shared<OptionListComponent<std::string>>(getHelpStyle(),
"SELECT THEME FOLDER", true)};
// Add custom systems.
for (auto it = unusedFolders.cbegin(); it != unusedFolders.cend(); ++it) {
ComponentListRow row;
std::string name {*it};
std::function<void()> createCollectionCall {
[this, name] { createCustomCollection(name); }};
row.makeAcceptInputHandler(createCollectionCall);
auto themeFolder = std::make_shared<TextComponent>(
Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
themeFolder->setSelectable(true);
row.addElement(themeFolder, true);
ss->addRow(row);
}
mWindow->pushGui(ss);
});
addRow(row);
}
// Create new custom collection.
ComponentListRow row;
auto newCollection = std::make_shared<TextComponent>("CREATE NEW CUSTOM COLLECTION",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketNewCollection = std::make_shared<ImageComponent>();
bracketNewCollection->setResize(
glm::vec2 {0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
bracketNewCollection->setImage(":/graphics/arrow.svg");
row.addElement(newCollection, true);
row.addElement(bracketNewCollection, false);
auto createCollectionCall = [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);
createCustomCollection(name);
};
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
row.makeAcceptInputHandler([this, createCollectionCall] {
mWindow->pushGui(new GuiTextEditKeyboardPopup(getHelpStyle(), "New Collection Name", "",
createCollectionCall, false, "CREATE",
"CREATE COLLECTION?"));
});
}
else {
row.makeAcceptInputHandler([this, createCollectionCall] {
mWindow->pushGui(new GuiTextEditPopup(getHelpStyle(), "New Collection Name", "",
createCollectionCall, false, "CREATE",
"CREATE COLLECTION?"));
});
}
addRow(row);
// Delete custom collection.
row.elements.clear();
auto deleteCollection = std::make_shared<TextComponent>(
"DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketDeleteCollection = std::make_shared<ImageComponent>();
bracketDeleteCollection->setResize(
glm::vec2 {0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
bracketDeleteCollection->setImage(":/graphics/arrow.svg");
row.addElement(deleteCollection, true);
row.addElement(bracketDeleteCollection, false);
row.makeAcceptInputHandler([this, customSystems] {
auto ss = new GuiSettings("SELECT COLLECTION TO DELETE");
std::shared_ptr<OptionListComponent<std::string>> customCollections {
std::make_shared<OptionListComponent<std::string>>(getHelpStyle(), "", true)};
for (std::map<std::string, CollectionSystemData, StringComparator>::const_iterator it =
customSystems.cbegin();
it != customSystems.cend(); ++it) {
ComponentListRow row;
std::string name {(*it).first};
std::function<void()> deleteCollectionCall = [this, name] {
mWindow->pushGui(new GuiMsgBox(
getHelpStyle(),
"THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" +
Utils::String::toUpper(name) +
"'\n"
"ARE YOU SURE?",
"YES",
[this, name] {
if (CollectionSystemsManager::getInstance()->isEditing())
CollectionSystemsManager::getInstance()->exitEditMode();
mDeletedCustomCollection = true;
std::vector<std::string> selectedCustomCollections {
mCollectionSystemsCustom->getSelectedObjects()};
std::string collectionsConfigEntry;
// Create the configuration file entry. If the collection to be
// deleted was activated, then exclude it.
for (auto it = selectedCustomCollections.begin();
it != selectedCustomCollections.end(); ++it) {
if ((*it) != name) {
if ((*it) != selectedCustomCollections.front() &&
collectionsConfigEntry != "")
collectionsConfigEntry += ",";
collectionsConfigEntry += (*it);
}
}
// If the system to be deleted was present in es_settings.xml, we
// need to re-write it.
if (collectionsConfigEntry !=
Settings::getInstance()->getString("CollectionSystemsCustom")) {
Settings::getInstance()->setString("CollectionSystemsCustom",
collectionsConfigEntry);
if (selectedCustomCollections.size() == 1 &&
Settings::getInstance()->getString("StartupSystem") ==
"collections")
Settings::getInstance()->setString("StartupSystem", "");
setNeedsSaving();
setNeedsGoToStart();
}
CollectionSystemsManager::getInstance()->deleteCustomCollection(name);
return true;
},
"NO", [] { return false; }));
};
row.makeAcceptInputHandler(deleteCollectionCall);
auto customCollection = std::make_shared<TextComponent>(
Utils::String::toUpper(name), Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
customCollection->setSelectable(true);
row.addElement(customCollection, true);
ss->addRow(row);
}
// Make the menu slightly wider to fit the scroll indicators.
glm::vec2 menuSize {ss->getMenuSize()};
glm::vec3 menuPos {ss->getMenuPosition()};
ss->setMenuSize(glm::vec2 {menuSize.x * 1.08f, menuSize.y});
menuPos.x = (Renderer::getScreenWidth() - ss->getMenuSize().x) / 2.0f;
ss->setMenuPosition(menuPos);
mWindow->pushGui(ss);
});
addRow(row);
// Sort favorites on top for custom collections.
auto fav_first_custom = std::make_shared<SwitchComponent>();
fav_first_custom->setState(Settings::getInstance()->getBool("FavFirstCustom"));
addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", fav_first_custom);
addSaveFunc([this, fav_first_custom] {
if (fav_first_custom->getState() != Settings::getInstance()->getBool("FavFirstCustom")) {
Settings::getInstance()->setBool("FavFirstCustom", fav_first_custom->getState());
setNeedsSaving();
setNeedsReloading();
setNeedsSorting();
setNeedsSortingCollections();
setInvalidateCachedBackground();
}
});
// Display star markings for custom collections.
auto fav_star_custom = std::make_shared<SwitchComponent>();
fav_star_custom->setState(Settings::getInstance()->getBool("FavStarCustom"));
addWithLabel("DISPLAY STAR MARKINGS FOR CUSTOM COLLECTIONS", fav_star_custom);
addSaveFunc([this, fav_star_custom] {
if (fav_star_custom->getState() != Settings::getInstance()->getBool("FavStarCustom")) {
Settings::getInstance()->setBool("FavStarCustom", fav_star_custom->getState());
setNeedsSaving();
setNeedsReloading();
setInvalidateCachedBackground();
}
});
// Group unthemed custom collections.
auto use_custom_collections_system = std::make_shared<SwitchComponent>();
use_custom_collections_system->setState(
Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", use_custom_collections_system);
addSaveFunc([this, use_custom_collections_system] {
if (use_custom_collections_system->getState() !=
Settings::getInstance()->getBool("UseCustomCollectionsSystem")) {
Settings::getInstance()->setBool("UseCustomCollectionsSystem",
use_custom_collections_system->getState());
if (CollectionSystemsManager::getInstance()->isEditing())
CollectionSystemsManager::getInstance()->exitEditMode();
setNeedsSaving();
setNeedsSorting();
setNeedsSortingCollections();
setNeedsCollectionsUpdate();
setNeedsReloading();
setNeedsGoToSystem(SystemData::sSystemVector.front());
setInvalidateCachedBackground();
}
});
// Show system names in collections.
auto collection_show_system_info = std::make_shared<SwitchComponent>();
collection_show_system_info->setState(
Settings::getInstance()->getBool("CollectionShowSystemInfo"));
addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", collection_show_system_info);
addSaveFunc([this, collection_show_system_info] {
if (collection_show_system_info->getState() !=
Settings::getInstance()->getBool("CollectionShowSystemInfo")) {
Settings::getInstance()->setBool("CollectionShowSystemInfo",
collection_show_system_info->getState());
setNeedsSaving();
setNeedsReloading();
setInvalidateCachedBackground();
}
});
}
void GuiCollectionSystemsOptions::createCustomCollection(std::string inName)
{
if (CollectionSystemsManager::getInstance()->isEditing())
CollectionSystemsManager::getInstance()->exitEditMode();
std::string collectionName {
CollectionSystemsManager::getInstance()->getValidNewCollectionName(inName)};
SystemData* newCollection {
CollectionSystemsManager::getInstance()->addNewCustomCollection(collectionName)};
CollectionSystemsManager::getInstance()->saveCustomCollection(newCollection);
mCollectionSystemsCustom->add(collectionName, collectionName, true);
mAddedCustomCollection = true;
setNeedsGoToStart();
if (Settings::getInstance()->getBool("UseCustomCollectionsSystem"))
setNeedsGoToGroupedCollections();
else
setNeedsGoToSystem(newCollection);
Window* window {mWindow};
while (window->peekGui() && window->peekGui() != ViewController::getInstance())
delete window->peekGui();
CollectionSystemsManager::getInstance()->setEditMode(collectionName);
}