2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-23 18:07:00 +00:00
|
|
|
//
|
2024-07-10 16:04:40 +00:00
|
|
|
// ES-DE Frontend
|
2020-06-23 18:07:00 +00:00
|
|
|
// GuiSettings.cpp
|
|
|
|
//
|
|
|
|
// User interface template for a settings GUI.
|
2021-06-16 17:05:24 +00:00
|
|
|
// The saving of es_settings.xml, the reload of gamelists and some other actions are
|
2021-01-05 09:45:32 +00:00
|
|
|
// also triggered to be executed here via flags set by the menu entries' lambda functions.
|
2020-06-23 18:07:00 +00:00
|
|
|
//
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "guis/GuiSettings.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2020-12-23 17:06:30 +00:00
|
|
|
#include "CollectionSystemsManager.h"
|
2021-01-05 09:45:32 +00:00
|
|
|
#include "FileFilterIndex.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "Settings.h"
|
|
|
|
#include "SystemData.h"
|
|
|
|
#include "Window.h"
|
2021-07-07 18:03:42 +00:00
|
|
|
#include "components/HelpComponent.h"
|
2021-09-17 20:23:41 +00:00
|
|
|
#include "guis/GuiTextEditKeyboardPopup.h"
|
2021-07-07 18:03:42 +00:00
|
|
|
#include "guis/GuiTextEditPopup.h"
|
2022-01-18 19:42:50 +00:00
|
|
|
#include "views/GamelistView.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
GuiSettings::GuiSettings(std::string title)
|
2023-02-09 23:25:22 +00:00
|
|
|
: mRenderer {Renderer::getInstance()}
|
|
|
|
, mMenu {title}
|
2022-01-19 17:01:54 +00:00
|
|
|
, mGoToSystem {nullptr}
|
|
|
|
, mNeedsSaving {false}
|
|
|
|
, mNeedsCollectionsUpdate {false}
|
|
|
|
, mNeedsSorting {false}
|
|
|
|
, mNeedsSortingCollections {false}
|
|
|
|
, mNeedsResetFilters {false}
|
2023-08-13 18:36:18 +00:00
|
|
|
, mNeedsRescanROMDirectory {false}
|
2022-01-19 17:01:54 +00:00
|
|
|
, mNeedsReloading {false}
|
|
|
|
, mNeedsGoToStart {false}
|
|
|
|
, mNeedsGoToSystem {false}
|
|
|
|
, mNeedsGoToGroupedCollections {false}
|
|
|
|
, mInvalidateCachedBackground {false}
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
addChild(&mMenu);
|
2024-07-18 13:07:41 +00:00
|
|
|
mMenu.addButton(_("BACK"), _("back"), [this] { delete this; });
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-02-11 22:38:23 +00:00
|
|
|
setSize(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
2021-08-16 16:25:01 +00:00
|
|
|
mMenu.setPosition((mSize.x - mMenu.getSize().x) / 2.0f, Renderer::getScreenHeight() * 0.13f);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GuiSettings::~GuiSettings()
|
|
|
|
{
|
2021-07-07 18:03:42 +00:00
|
|
|
// Save on exit.
|
2020-06-23 18:07:00 +00:00
|
|
|
save();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GuiSettings::save()
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
if (!mSaveFuncs.size())
|
|
|
|
return;
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2021-11-17 16:35:34 +00:00
|
|
|
for (auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); ++it)
|
2020-06-23 18:07:00 +00:00
|
|
|
(*it)();
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-11-05 17:18:11 +00:00
|
|
|
if (mNeedsSaving)
|
|
|
|
Settings::getInstance()->saveFile();
|
|
|
|
|
2023-08-13 18:36:18 +00:00
|
|
|
if (mNeedsRescanROMDirectory) {
|
|
|
|
if (CollectionSystemsManager::getInstance()->isEditing())
|
|
|
|
CollectionSystemsManager::getInstance()->exitEditMode();
|
|
|
|
mWindow->stopInfoPopup();
|
|
|
|
// Write any gamelist.xml changes before proceeding with the rescan.
|
|
|
|
if (Settings::getInstance()->getString("SaveGamelistsMode") == "on exit") {
|
|
|
|
for (auto system : SystemData::sSystemVector)
|
|
|
|
system->writeMetaData();
|
|
|
|
}
|
2023-08-13 22:02:20 +00:00
|
|
|
// If a close menu function was passed to us, then run it.
|
2023-09-17 10:45:00 +00:00
|
|
|
if (mCloseMenuFunction) {
|
2023-08-13 22:02:20 +00:00
|
|
|
mCloseMenuFunction();
|
2023-09-17 10:45:00 +00:00
|
|
|
mCloseMenuFunction = nullptr;
|
|
|
|
}
|
2023-08-13 18:36:18 +00:00
|
|
|
ViewController::getInstance()->rescanROMDirectory();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-06 19:27:41 +00:00
|
|
|
if (mNeedsCollectionsUpdate) {
|
2022-01-04 20:21:26 +00:00
|
|
|
CollectionSystemsManager::getInstance()->loadEnabledListFromSettings();
|
|
|
|
CollectionSystemsManager::getInstance()->updateSystemsList();
|
2020-11-06 19:27:41 +00:00
|
|
|
}
|
2020-11-05 17:18:11 +00:00
|
|
|
|
|
|
|
if (mNeedsSorting) {
|
2021-07-07 18:03:42 +00:00
|
|
|
for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend();
|
2021-11-17 16:35:34 +00:00
|
|
|
++it) {
|
2021-07-07 18:03:42 +00:00
|
|
|
if (!(!mNeedsSortingCollections && (*it)->isCollection()))
|
2020-11-05 17:18:11 +00:00
|
|
|
(*it)->sortSystem(true);
|
2021-07-07 18:03:42 +00:00
|
|
|
|
2020-11-05 17:18:11 +00:00
|
|
|
// Jump to the first row of the gamelist.
|
2022-09-03 10:44:49 +00:00
|
|
|
GamelistView* gameList {ViewController::getInstance()->getGamelistView((*it)).get()};
|
2020-11-05 17:18:11 +00:00
|
|
|
gameList->setCursor(gameList->getFirstEntry());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-05 09:45:32 +00:00
|
|
|
if (mNeedsResetFilters) {
|
2021-07-07 18:03:42 +00:00
|
|
|
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
|
2021-11-17 16:35:34 +00:00
|
|
|
it != SystemData::sSystemVector.cend(); ++it) {
|
2021-01-05 09:45:32 +00:00
|
|
|
if ((*it)->getThemeFolder() == "custom-collections") {
|
2021-07-07 18:03:42 +00:00
|
|
|
for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay())
|
2021-01-05 09:45:32 +00:00
|
|
|
customSystem->getSystem()->getIndex()->resetFilters();
|
|
|
|
}
|
|
|
|
(*it)->getIndex()->resetFilters();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mNeedsReloading)
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->reloadAll();
|
2021-01-05 09:45:32 +00:00
|
|
|
|
2020-11-06 19:27:41 +00:00
|
|
|
if (mNeedsGoToStart)
|
2024-06-03 15:35:08 +00:00
|
|
|
ViewController::getInstance()->goToStart(false);
|
2020-11-06 19:27:41 +00:00
|
|
|
|
2023-01-12 18:45:54 +00:00
|
|
|
// Special case from GuiCollectionSystemsOptions where we didn't yet know whether a matching
|
|
|
|
// theme existed when creating a new custom collection.
|
|
|
|
if (mNeedsGoToSystem && mNeedsGoToGroupedCollections) {
|
|
|
|
mNeedsGoToSystem = false;
|
|
|
|
for (SystemData* system : SystemData::sSystemVector) {
|
|
|
|
if (system->getThemeFolder() == mGoToSystem->getThemeFolder()) {
|
|
|
|
mNeedsGoToSystem = true;
|
|
|
|
mNeedsGoToGroupedCollections = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-01 21:37:21 +00:00
|
|
|
if (mNeedsGoToSystem)
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->goToSystem(mGoToSystem, false);
|
2020-11-06 19:27:41 +00:00
|
|
|
|
2021-01-02 11:33:27 +00:00
|
|
|
if (mNeedsGoToGroupedCollections) {
|
2022-09-03 10:44:49 +00:00
|
|
|
bool groupedSystemExists {false};
|
2021-01-02 11:33:27 +00:00
|
|
|
for (SystemData* system : SystemData::sSystemVector) {
|
|
|
|
if (system->getThemeFolder() == "custom-collections") {
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->goToSystem(system, false);
|
2021-01-02 14:14:13 +00:00
|
|
|
groupedSystemExists = true;
|
2023-01-12 18:45:54 +00:00
|
|
|
break;
|
2021-01-02 11:33:27 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-02 14:14:13 +00:00
|
|
|
if (!groupedSystemExists)
|
|
|
|
// No grouped custom collection system exists, so go to the first system instead.
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->goToSystem(SystemData::sSystemVector.front(), false);
|
2021-01-02 11:33:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mNeedsCollectionsUpdate) {
|
2022-01-04 20:49:22 +00:00
|
|
|
auto state = ViewController::getInstance()->getState();
|
2021-01-02 11:33:27 +00:00
|
|
|
// If we're in any view other than the grouped custom collections, always jump to the
|
|
|
|
// system view in case of any collection updates. This is overkill in some instances but
|
|
|
|
// these views can behave a bit strange during collection changes so it's better to be on
|
|
|
|
// the safe side.
|
|
|
|
if (state.getSystem()->isCollection() &&
|
2021-07-07 18:03:42 +00:00
|
|
|
state.getSystem()->getThemeFolder() != "custom-collections") {
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->goToStart(false);
|
2023-01-12 19:54:12 +00:00
|
|
|
ViewController::getInstance()->resetCamera();
|
|
|
|
SDL_Delay(100);
|
|
|
|
mWindow->invalidateCachedBackground();
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->goToSystem(SystemData::sSystemVector.front(), false);
|
2021-01-02 11:33:27 +00:00
|
|
|
// We don't want to invalidate the cached background when there has been a collection
|
|
|
|
// systen change as that may show a black screen in some circumstances.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// If the last displayed custom collection was just disabled, then go to start (to the
|
|
|
|
// system view).
|
|
|
|
if (std::find(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(),
|
2021-07-07 18:03:42 +00:00
|
|
|
state.getSystem()) == SystemData::sSystemVector.end()) {
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->goToStart(false);
|
2023-01-12 19:54:12 +00:00
|
|
|
ViewController::getInstance()->resetCamera();
|
|
|
|
SDL_Delay(100);
|
|
|
|
mWindow->invalidateCachedBackground();
|
2021-01-02 11:33:27 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-01-12 19:54:12 +00:00
|
|
|
ViewController::getInstance()->resetCamera();
|
2021-01-02 11:33:27 +00:00
|
|
|
}
|
|
|
|
|
2023-08-13 22:02:20 +00:00
|
|
|
// If a close menu function was passed to us, then run it.
|
|
|
|
if (mCloseMenuFunction)
|
|
|
|
mCloseMenuFunction();
|
2023-08-13 18:36:18 +00:00
|
|
|
|
2021-03-21 17:46:45 +00:00
|
|
|
if (mInvalidateCachedBackground) {
|
2021-03-21 15:41:24 +00:00
|
|
|
// This delay reduces the likelyhood that the SVG rasterizer which is running in a
|
|
|
|
// separate thread is not done until the cached background is invalidated. Without
|
|
|
|
// this delay there's a high chance that some theme elements are not rendered in
|
|
|
|
// time and thus not getting included in the regenerated cached background.
|
|
|
|
// This is just a hack though and a better mechanism is needed to handle this.
|
|
|
|
SDL_Delay(100);
|
2021-03-21 17:55:01 +00:00
|
|
|
mWindow->invalidateCachedBackground();
|
2021-03-12 19:36:38 +00:00
|
|
|
}
|
2020-11-05 17:18:11 +00:00
|
|
|
}
|
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
void GuiSettings::addEditableTextComponent(const std::string label,
|
|
|
|
std::shared_ptr<GuiComponent> ed,
|
|
|
|
std::string value,
|
|
|
|
std::string defaultValue,
|
|
|
|
bool isPassword)
|
2020-11-05 17:18:11 +00:00
|
|
|
{
|
|
|
|
ComponentListRow row;
|
|
|
|
row.elements.clear();
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
auto lbl = std::make_shared<TextComponent>(Utils::String::toUpper(label),
|
2023-05-07 20:56:24 +00:00
|
|
|
Font::get(FONT_SIZE_MEDIUM), mMenuColorPrimary);
|
2020-11-05 17:18:11 +00:00
|
|
|
row.addElement(lbl, true);
|
|
|
|
row.addElement(ed, true);
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
auto spacer = std::make_shared<GuiComponent>();
|
2023-05-07 20:56:24 +00:00
|
|
|
spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f);
|
2020-11-05 17:18:11 +00:00
|
|
|
row.addElement(spacer, false);
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
auto bracket = std::make_shared<ImageComponent>();
|
2022-01-16 11:09:55 +00:00
|
|
|
bracket->setResize(glm::vec2 {0.0f, lbl->getFont()->getLetterHeight()});
|
2022-08-30 18:26:48 +00:00
|
|
|
bracket->setImage(":/graphics/arrow.svg");
|
2023-05-07 20:56:24 +00:00
|
|
|
bracket->setColorShift(mMenuColorPrimary);
|
2020-11-05 17:18:11 +00:00
|
|
|
row.addElement(bracket, false);
|
|
|
|
|
|
|
|
// OK callback (apply new value to ed).
|
2020-11-07 14:34:15 +00:00
|
|
|
auto updateVal = [ed, defaultValue, isPassword](const std::string& newVal) {
|
2020-11-05 17:18:11 +00:00
|
|
|
// If the field is blank, apply the default value if it's been passes as an argument.
|
2020-11-07 14:34:15 +00:00
|
|
|
if (defaultValue != "" && newVal == "") {
|
2020-11-05 17:18:11 +00:00
|
|
|
ed->setValue(defaultValue);
|
2020-11-07 14:34:15 +00:00
|
|
|
}
|
|
|
|
// If it's a password and actually set to something, then show a star mask.
|
|
|
|
else if (isPassword && newVal == "") {
|
|
|
|
ed->setValue("");
|
|
|
|
ed->setHiddenValue("");
|
|
|
|
}
|
|
|
|
else if (isPassword) {
|
|
|
|
ed->setValue("********");
|
|
|
|
ed->setHiddenValue(newVal);
|
|
|
|
}
|
|
|
|
else {
|
2020-11-05 17:18:11 +00:00
|
|
|
ed->setValue(newVal);
|
2020-11-07 14:34:15 +00:00
|
|
|
}
|
2020-11-05 17:18:11 +00:00
|
|
|
};
|
2020-11-07 14:34:15 +00:00
|
|
|
|
2021-09-17 20:23:41 +00:00
|
|
|
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
|
|
|
|
row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] {
|
|
|
|
// Never display the value if it's a password, instead set it to blank.
|
|
|
|
if (isPassword)
|
2024-07-13 10:32:16 +00:00
|
|
|
mWindow->pushGui(new GuiTextEditKeyboardPopup(
|
|
|
|
getHelpStyle(), getMenu().getPosition().y, label, "", updateVal, false,
|
|
|
|
_("SAVE"), _("SAVE CHANGES?")));
|
2021-09-17 20:23:41 +00:00
|
|
|
else
|
2023-02-09 23:25:22 +00:00
|
|
|
mWindow->pushGui(new GuiTextEditKeyboardPopup(
|
|
|
|
getHelpStyle(), getMenu().getPosition().y, label, ed->getValue(), updateVal,
|
2024-07-13 10:32:16 +00:00
|
|
|
false, _("SAVE"), _("SAVE CHANGES?")));
|
2021-09-17 20:23:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] {
|
|
|
|
if (isPassword)
|
2022-01-19 17:01:54 +00:00
|
|
|
mWindow->pushGui(new GuiTextEditPopup(getHelpStyle(), label, "", updateVal, false,
|
2024-07-13 10:32:16 +00:00
|
|
|
_("SAVE"), _("SAVE CHANGES?")));
|
2021-09-17 20:23:41 +00:00
|
|
|
else
|
2022-01-19 17:01:54 +00:00
|
|
|
mWindow->pushGui(new GuiTextEditPopup(getHelpStyle(), label, ed->getValue(),
|
2024-07-13 10:32:16 +00:00
|
|
|
updateVal, false, _("SAVE"),
|
|
|
|
_("SAVE CHANGES?")));
|
2021-09-17 20:23:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-11-05 17:18:11 +00:00
|
|
|
assert(ed);
|
|
|
|
addRow(row);
|
|
|
|
|
|
|
|
ed->setValue(value);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool GuiSettings::input(InputConfig* config, Input input)
|
|
|
|
{
|
2020-06-23 18:07:00 +00:00
|
|
|
if (config->isMappedTo("b", input) && input.value != 0) {
|
|
|
|
delete this;
|
|
|
|
return true;
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-23 18:07:00 +00:00
|
|
|
return GuiComponent::input(config, input);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<HelpPrompt> GuiSettings::getHelpPrompts()
|
|
|
|
{
|
2022-09-03 10:44:49 +00:00
|
|
|
std::vector<HelpPrompt> prompts {mMenu.getHelpPrompts()};
|
2024-07-13 17:14:34 +00:00
|
|
|
prompts.push_back(HelpPrompt("b", _("back")));
|
2020-06-23 18:07:00 +00:00
|
|
|
return prompts;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|