From 021a9f8383bb6d97846bc7a4b7175ac65484a463 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 18 May 2026 01:59:02 -0700 Subject: [PATCH] [DlgSettings] Refactor: split SettingsPage into separate files (#6899) --- cockatrice/CMakeLists.txt | 8 + cockatrice/resources/config/qtlogging.ini | 2 + .../widgets/dialogs/dlg_settings.cpp | 1954 +---------------- .../interface/widgets/dialogs/dlg_settings.h | 359 +-- .../settings_page/abstract_settings_page.h | 16 + .../appearance_settings_page.cpp | 415 ++++ .../settings_page/appearance_settings_page.h | 75 + .../deck_editor_settings_page.cpp | 241 ++ .../settings_page/deck_editor_settings_page.h | 51 + .../settings_page/general_settings_page.cpp | 373 ++++ .../settings_page/general_settings_page.h | 71 + .../settings_page/messages_settings_page.cpp | 252 +++ .../settings_page/messages_settings_page.h | 58 + .../settings_page/shortcut_settings_page.cpp | 128 ++ .../settings_page/shortcut_settings_page.h | 45 + .../settings_page/sound_settings_page.cpp | 86 + .../settings_page/sound_settings_page.h | 35 + .../settings_page/storage_settings_page.cpp | 244 ++ .../settings_page/storage_settings_page.h | 50 + .../user_interface_settings_page.cpp | 234 ++ .../user_interface_settings_page.h | 54 + 21 files changed, 2452 insertions(+), 2299 deletions(-) create mode 100644 cockatrice/src/interface/widgets/settings_page/abstract_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/general_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/messages_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/sound_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/sound_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/storage_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/storage_settings_page.h create mode 100644 cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp create mode 100644 cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index ab47aff80..69b052a36 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -235,6 +235,14 @@ set(cockatrice_SOURCES src/interface/widgets/server/user/user_info_connection.cpp src/interface/widgets/server/user/user_list_manager.cpp src/interface/widgets/server/user/user_list_widget.cpp + src/interface/widgets/settings_page/appearance_settings_page.cpp + src/interface/widgets/settings_page/deck_editor_settings_page.cpp + src/interface/widgets/settings_page/general_settings_page.cpp + src/interface/widgets/settings_page/messages_settings_page.cpp + src/interface/widgets/settings_page/shortcut_settings_page.cpp + src/interface/widgets/settings_page/sound_settings_page.cpp + src/interface/widgets/settings_page/storage_settings_page.cpp + src/interface/widgets/settings_page/user_interface_settings_page.cpp src/interface/widgets/utility/custom_line_edit.cpp src/interface/widgets/utility/get_text_with_max.cpp src/interface/widgets/utility/sequence_edit.cpp diff --git a/cockatrice/resources/config/qtlogging.ini b/cockatrice/resources/config/qtlogging.ini index 20aa206ce..7ac0d9ca4 100644 --- a/cockatrice/resources/config/qtlogging.ini +++ b/cockatrice/resources/config/qtlogging.ini @@ -28,6 +28,8 @@ #dlg_tip_of_the_day = true #dlg_update = true +#general_settings_page = true + #settings_cache = true #servers_settings = true #shortcuts_settings = true diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp index d9f0c0fe4..3f133c5b6 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp @@ -1,1957 +1,29 @@ #include "dlg_settings.h" #include "../../../client/settings/cache_settings.h" -#include "../../../client/settings/shortcut_treeview.h" -#include "../../palette_editor/palette_editor_dialog.h" -#include "../client/network/update/card_spoiler/spoiler_background_updater.h" -#include "../client/network/update/client/release_channel.h" -#include "../client/sound_engine.h" -#include "../interface/card_picture_loader/card_picture_loader.h" -#include "../interface/theme_manager.h" -#include "../interface/widgets/general/background_sources.h" -#include "../interface/widgets/tabs/tab_supervisor.h" -#include "../interface/widgets/utility/custom_line_edit.h" -#include "../interface/widgets/utility/get_text_with_max.h" -#include "../interface/widgets/utility/sequence_edit.h" #include "../main.h" -#include "override_printing_warning.h" +#include "../settings_page/appearance_settings_page.h" +#include "../settings_page/deck_editor_settings_page.h" +#include "../settings_page/general_settings_page.h" +#include "../settings_page/messages_settings_page.h" +#include "../settings_page/shortcut_settings_page.h" +#include "../settings_page/sound_settings_page.h" +#include "../settings_page/storage_settings_page.h" +#include "../settings_page/user_interface_settings_page.h" +#include "libcockatrice/card/database/card_database_loader.h" +#include "libcockatrice/card/database/card_database_manager.h" -#include <../../client/settings/card_counter_settings.h> -#include -#include -#include -#include -#include #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include -#include #include #include #include -#include -#include #include -#include -#include -#include -#include -#include - -#define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs" -#define WIKI_CUSTOM_SHORTCUTS "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts" -#define WIKI_TRANSLATION_FAQ "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ" - -enum startupCardUpdateCheckBehaviorIndex -{ - startupCardUpdateCheckBehaviorIndexNone, - startupCardUpdateCheckBehaviorIndexPrompt, - startupCardUpdateCheckBehaviorIndexAlways -}; - -GeneralSettingsPage::GeneralSettingsPage() -{ - QStringList languageCodes = findQmFiles(); - for (const QString &code : languageCodes) { - QString langName = languageName(code); - languageBox.addItem(langName, code); - } - - QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); - int index = languageBox.findText(setLanguage, Qt::MatchExactly); - if (index == -1) { - qWarning() << "could not find language" << setLanguage; - } else { - languageBox.setCurrentIndex(index); - } - - // updates - SettingsCache &settings = SettingsCache::instance(); - startupUpdateCheckCheckBox.setChecked(settings.getCheckUpdatesOnStartup()); - - startupCardUpdateCheckBehaviorSelector.addItem(""); // these will be set in retranslateUI - startupCardUpdateCheckBehaviorSelector.addItem(""); - startupCardUpdateCheckBehaviorSelector.addItem(""); - if (SettingsCache::instance().getStartupCardUpdateCheckPromptForUpdate()) { - startupCardUpdateCheckBehaviorSelector.setCurrentIndex(startupCardUpdateCheckBehaviorIndexPrompt); - } else if (SettingsCache::instance().getStartupCardUpdateCheckAlwaysUpdate()) { - startupCardUpdateCheckBehaviorSelector.setCurrentIndex(startupCardUpdateCheckBehaviorIndexAlways); - } else { - startupCardUpdateCheckBehaviorSelector.setCurrentIndex(startupCardUpdateCheckBehaviorIndexNone); - } - - cardUpdateCheckIntervalSpinBox.setMinimum(1); - cardUpdateCheckIntervalSpinBox.setMaximum(30); - cardUpdateCheckIntervalSpinBox.setValue(settings.getCardUpdateCheckInterval()); - updateNotificationCheckBox.setChecked(settings.getNotifyAboutUpdates()); - newVersionOracleCheckBox.setChecked(settings.getNotifyAboutNewVersion()); - - showTipsOnStartup.setChecked(settings.getShowTipsOnStartup()); - - advertiseTranslationPageLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); - advertiseTranslationPageLabel.setOpenExternalLinks(true); - - connect(&languageBox, qOverload(&QComboBox::currentIndexChanged), this, - &GeneralSettingsPage::languageBoxChanged); - connect(&startupUpdateCheckCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setCheckUpdatesOnStartup); - connect(&startupCardUpdateCheckBehaviorSelector, QOverload::of(&QComboBox::currentIndexChanged), this, - [](int index) { - SettingsCache::instance().setStartupCardUpdateCheckPromptForUpdate( - index == startupCardUpdateCheckBehaviorIndexPrompt); - SettingsCache::instance().setStartupCardUpdateCheckAlwaysUpdate( - index == startupCardUpdateCheckBehaviorIndexAlways); - }); - connect(&cardUpdateCheckIntervalSpinBox, qOverload(&QSpinBox::valueChanged), &settings, - &SettingsCache::setCardUpdateCheckInterval); - connect(&updateNotificationCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setNotifyAboutUpdate); - connect(&newVersionOracleCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setNotifyAboutNewVersion); - connect(&showTipsOnStartup, &QCheckBox::clicked, &settings, &SettingsCache::setShowTipsOnStartup); - - auto *personalGrid = new QGridLayout; - personalGrid->addWidget(&languageLabel, 0, 0); - personalGrid->addWidget(&languageBox, 0, 1); - personalGrid->addWidget(&advertiseTranslationPageLabel, 1, 1, Qt::AlignRight); - personalGrid->addWidget(&updateReleaseChannelLabel, 2, 0); - personalGrid->addWidget(&updateReleaseChannelBox, 2, 1); - personalGrid->addWidget(&startupUpdateCheckCheckBox, 4, 0, 1, 2); - personalGrid->addWidget(&startupCardUpdateCheckBehaviorLabel, 5, 0); - personalGrid->addWidget(&startupCardUpdateCheckBehaviorSelector, 5, 1); - personalGrid->addWidget(&cardUpdateCheckIntervalLabel, 6, 0); - personalGrid->addWidget(&cardUpdateCheckIntervalSpinBox, 6, 1); - personalGrid->addWidget(&lastCardUpdateCheckDateLabel, 7, 1); - personalGrid->addWidget(&updateNotificationCheckBox, 8, 0, 1, 2); - personalGrid->addWidget(&newVersionOracleCheckBox, 9, 0, 1, 2); - personalGrid->addWidget(&showTipsOnStartup, 10, 0, 1, 2); - - personalGroupBox = new QGroupBox; - personalGroupBox->setLayout(personalGrid); - - deckPathEdit = new QLineEdit(settings.getDeckPath()); - deckPathEdit->setReadOnly(true); - auto *deckPathButton = new QPushButton("..."); - connect(deckPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::deckPathButtonClicked); - - filtersPathEdit = new QLineEdit(settings.getFiltersPath()); - filtersPathEdit->setReadOnly(true); - auto *filtersPathButton = new QPushButton("..."); - connect(filtersPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::filtersPathButtonClicked); - - replaysPathEdit = new QLineEdit(settings.getReplaysPath()); - replaysPathEdit->setReadOnly(true); - auto *replaysPathButton = new QPushButton("..."); - connect(replaysPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::replaysPathButtonClicked); - - picsPathEdit = new QLineEdit(settings.getPicsPath()); - picsPathEdit->setReadOnly(true); - auto *picsPathButton = new QPushButton("..."); - connect(picsPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::picsPathButtonClicked); - - cardDatabasePathEdit = new QLineEdit(settings.getCardDatabasePath()); - cardDatabasePathEdit->setReadOnly(true); - auto *cardDatabasePathButton = new QPushButton("..."); - connect(cardDatabasePathButton, &QPushButton::clicked, this, &GeneralSettingsPage::cardDatabasePathButtonClicked); - - customCardDatabasePathEdit = new QLineEdit(settings.getCustomCardDatabasePath()); - customCardDatabasePathEdit->setReadOnly(true); - auto *customCardDatabasePathButton = new QPushButton("..."); - connect(customCardDatabasePathButton, &QPushButton::clicked, this, - &GeneralSettingsPage::customCardDatabaseButtonClicked); - - tokenDatabasePathEdit = new QLineEdit(settings.getTokenDatabasePath()); - tokenDatabasePathEdit->setReadOnly(true); - auto *tokenDatabasePathButton = new QPushButton("..."); - connect(tokenDatabasePathButton, &QPushButton::clicked, this, &GeneralSettingsPage::tokenDatabasePathButtonClicked); - - // Required init here to avoid crashing on Portable builds - resetAllPathsButton = new QPushButton; - - bool isPortable = settings.getIsPortableBuild(); - if (isPortable) { - deckPathEdit->setEnabled(false); - filtersPathEdit->setEnabled(false); - replaysPathEdit->setEnabled(false); - picsPathEdit->setEnabled(false); - cardDatabasePathEdit->setEnabled(false); - customCardDatabasePathEdit->setEnabled(false); - tokenDatabasePathEdit->setEnabled(false); - - deckPathButton->setVisible(false); - replaysPathButton->setVisible(false); - picsPathButton->setVisible(false); - cardDatabasePathButton->setVisible(false); - customCardDatabasePathButton->setVisible(false); - tokenDatabasePathButton->setVisible(false); - } else { - connect(resetAllPathsButton, &QPushButton::clicked, this, &GeneralSettingsPage::resetAllPathsClicked); - allPathsResetLabel = new QLabel(tr("All paths have been reset")); - allPathsResetLabel->setVisible(false); - } - - auto *pathsGrid = new QGridLayout; - pathsGrid->addWidget(&deckPathLabel, 0, 0); - pathsGrid->addWidget(deckPathEdit, 0, 1); - pathsGrid->addWidget(deckPathButton, 0, 2); - pathsGrid->addWidget(&filtersPathLabel, 1, 0); - pathsGrid->addWidget(filtersPathEdit, 1, 1); - pathsGrid->addWidget(filtersPathButton, 1, 2); - pathsGrid->addWidget(&replaysPathLabel, 2, 0); - pathsGrid->addWidget(replaysPathEdit, 2, 1); - pathsGrid->addWidget(replaysPathButton, 2, 2); - pathsGrid->addWidget(&picsPathLabel, 3, 0); - pathsGrid->addWidget(picsPathEdit, 3, 1); - pathsGrid->addWidget(picsPathButton, 3, 2); - pathsGrid->addWidget(&cardDatabasePathLabel, 4, 0); - pathsGrid->addWidget(cardDatabasePathEdit, 4, 1); - pathsGrid->addWidget(cardDatabasePathButton, 4, 2); - pathsGrid->addWidget(&customCardDatabasePathLabel, 5, 0); - pathsGrid->addWidget(customCardDatabasePathEdit, 5, 1); - pathsGrid->addWidget(customCardDatabasePathButton, 5, 2); - pathsGrid->addWidget(&tokenDatabasePathLabel, 6, 0); - pathsGrid->addWidget(tokenDatabasePathEdit, 6, 1); - pathsGrid->addWidget(tokenDatabasePathButton, 6, 2); - if (!isPortable) { - pathsGrid->addWidget(resetAllPathsButton, 7, 0); - pathsGrid->addWidget(allPathsResetLabel, 7, 1); - } - pathsGroupBox = new QGroupBox; - pathsGroupBox->setLayout(pathsGrid); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(personalGroupBox); - mainLayout->addWidget(pathsGroupBox); - mainLayout->addStretch(); - - GeneralSettingsPage::retranslateUi(); - - // connect the ReleaseChannel combo box only after the entries are inserted in retranslateUi - connect(&updateReleaseChannelBox, qOverload(&QComboBox::currentIndexChanged), &settings, - &SettingsCache::setUpdateReleaseChannelIndex); - updateReleaseChannelBox.setCurrentIndex(settings.getUpdateReleaseChannelIndex()); - - setLayout(mainLayout); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &GeneralSettingsPage::retranslateUi); - retranslateUi(); -} - -QStringList GeneralSettingsPage::findQmFiles() -{ - QDir dir(translationPath); - QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); - fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); - return fileNames; -} - -QString GeneralSettingsPage::languageName(const QString &lang) -{ - QTranslator qTranslator; - - QString appNameHint = translationPrefix + "_" + lang; - bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); - if (!appTranslationLoaded) { - qCWarning(DlgSettingsLog) << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" - << translationPath; - } - - return qTranslator.translate("i18n", DEFAULT_LANG_NAME); -} - -void GeneralSettingsPage::deckPathButtonClicked() -{ - QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), deckPathEdit->text()); - if (path.isEmpty()) { - return; - } - - deckPathEdit->setText(path); - SettingsCache::instance().setDeckPath(path); -} - -void GeneralSettingsPage::filtersPathButtonClicked() -{ - QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), filtersPathEdit->text()); - if (path.isEmpty()) { - return; - } - - filtersPathEdit->setText(path); - SettingsCache::instance().setFiltersPath(path); -} - -void GeneralSettingsPage::replaysPathButtonClicked() -{ - QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), replaysPathEdit->text()); - if (path.isEmpty()) { - return; - } - - replaysPathEdit->setText(path); - SettingsCache::instance().setReplaysPath(path); -} - -void GeneralSettingsPage::picsPathButtonClicked() -{ - QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), picsPathEdit->text()); - if (path.isEmpty()) { - return; - } - - picsPathEdit->setText(path); - SettingsCache::instance().setPicsPath(path); -} - -void GeneralSettingsPage::cardDatabasePathButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path"), cardDatabasePathEdit->text()); - if (path.isEmpty()) { - return; - } - - cardDatabasePathEdit->setText(path); - SettingsCache::instance().setCardDatabasePath(path); -} - -void GeneralSettingsPage::customCardDatabaseButtonClicked() -{ - QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), customCardDatabasePathEdit->text()); - if (path.isEmpty()) { - return; - } - - customCardDatabasePathEdit->setText(path); - SettingsCache::instance().setCustomCardDatabasePath(path); -} - -void GeneralSettingsPage::tokenDatabasePathButtonClicked() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Choose path"), tokenDatabasePathEdit->text()); - if (path.isEmpty()) { - return; - } - - tokenDatabasePathEdit->setText(path); - SettingsCache::instance().setTokenDatabasePath(path); -} - -void GeneralSettingsPage::resetAllPathsClicked() -{ - SettingsCache &settings = SettingsCache::instance(); - settings.resetPaths(); - deckPathEdit->setText(settings.getDeckPath()); - replaysPathEdit->setText(settings.getReplaysPath()); - picsPathEdit->setText(settings.getPicsPath()); - cardDatabasePathEdit->setText(settings.getCardDatabasePath()); - customCardDatabasePathEdit->setText(settings.getCustomCardDatabasePath()); - tokenDatabasePathEdit->setText(settings.getTokenDatabasePath()); - allPathsResetLabel->setVisible(true); -} - -void GeneralSettingsPage::languageBoxChanged(int index) -{ - SettingsCache::instance().setLang(languageBox.itemData(index).toString()); -} - -void GeneralSettingsPage::retranslateUi() -{ - personalGroupBox->setTitle(tr("Personal settings")); - languageLabel.setText(tr("Language:")); - - if (SettingsCache::instance().getIsPortableBuild()) { - pathsGroupBox->setTitle(tr("Paths (editing disabled in portable mode)")); - } else { - pathsGroupBox->setTitle(tr("Paths")); - } - advertiseTranslationPageLabel.setText( - QString("%2").arg(WIKI_TRANSLATION_FAQ).arg(tr("How to help with translations"))); - deckPathLabel.setText(tr("Decks directory:")); - filtersPathLabel.setText(tr("Filters directory:")); - replaysPathLabel.setText(tr("Replays directory:")); - picsPathLabel.setText(tr("Pictures directory:")); - cardDatabasePathLabel.setText(tr("Card database:")); - customCardDatabasePathLabel.setText(tr("Custom database directory:")); - tokenDatabasePathLabel.setText(tr("Token database:")); - updateReleaseChannelLabel.setText(tr("Update channel")); - startupUpdateCheckCheckBox.setText(tr("Check for client updates on startup")); - startupCardUpdateCheckBehaviorLabel.setText(tr("Check for card database updates on startup")); - startupCardUpdateCheckBehaviorSelector.setItemText(startupCardUpdateCheckBehaviorIndexNone, tr("Don't check")); - startupCardUpdateCheckBehaviorSelector.setItemText(startupCardUpdateCheckBehaviorIndexPrompt, - tr("Prompt for update")); - startupCardUpdateCheckBehaviorSelector.setItemText(startupCardUpdateCheckBehaviorIndexAlways, - tr("Always update in the background")); - cardUpdateCheckIntervalLabel.setText(tr("Check for card database updates every")); - cardUpdateCheckIntervalSpinBox.setSuffix(tr(" days")); - updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client")); - newVersionOracleCheckBox.setText(tr("Automatically run Oracle when running a new version of Cockatrice")); - showTipsOnStartup.setText(tr("Show tips on startup")); - resetAllPathsButton->setText(tr("Reset all paths")); - - const auto &settings = SettingsCache::instance(); - - QDate lastCheckDate = settings.getLastCardUpdateCheck(); - int daysAgo = lastCheckDate.daysTo(QDate::currentDate()); - - lastCardUpdateCheckDateLabel.setText( - tr("Last update check on %1 (%2 days ago)").arg(lastCheckDate.toString()).arg(daysAgo)); - - // We can't change the strings after they're put into the QComboBox, so this is our workaround - int oldIndex = updateReleaseChannelBox.currentIndex(); - updateReleaseChannelBox.clear(); - for (ReleaseChannel *chan : settings.getUpdateReleaseChannels()) { - updateReleaseChannelBox.addItem(tr(chan->getName().toUtf8())); - } - updateReleaseChannelBox.setCurrentIndex(oldIndex); -} - -AppearanceSettingsPage::AppearanceSettingsPage() -{ - SettingsCache &settings = SettingsCache::instance(); - - // Theme settings - QString themeName = SettingsCache::instance().getThemeName(); - - QStringList themeDirs = themeManager->getAvailableThemes().keys(); - for (int i = 0; i < themeDirs.size(); i++) { - themeBox.addItem(themeDirs[i]); - if (themeDirs[i] == themeName) { - themeBox.setCurrentIndex(i); - } - } - - connect(&themeBox, qOverload(&QComboBox::currentIndexChanged), this, &AppearanceSettingsPage::themeBoxChanged); - connect(&openThemeButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openThemeLocation); - - schemeCombo.addItem(tr("Light"), QStringLiteral("Light")); - schemeCombo.addItem(tr("Dark"), QStringLiteral("Dark")); -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - schemeCombo.addItem(tr("System"), QStringLiteral("System")); -#endif - - // Seed from whatever the current theme already has saved - const QString dirPath = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName()); - const ThemeConfig cfg = ThemeConfig::fromThemeDir(dirPath); - const QString current = cfg.colorScheme; - const int seedIdx = schemeCombo.findData(current); - schemeCombo.setCurrentIndex(seedIdx >= 0 ? seedIdx : 0); - - connect(&schemeCombo, &QComboBox::currentIndexChanged, this, - [this] { themeManager->setColorScheme(schemeCombo.currentData().toString()); }); - - connect(themeManager, &ThemeManager::themeChanged, this, [this, dirPath] { - const QString newDir = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName()); - const ThemeConfig cfg = ThemeConfig::fromThemeDir(newDir); - const QString current = cfg.colorScheme; - - schemeCombo.blockSignals(true); - const int idx = schemeCombo.findData(current); - schemeCombo.setCurrentIndex(idx >= 0 ? idx : 0); - schemeCombo.blockSignals(false); - }); - - connect(&editPaletteButton, &QPushButton::clicked, this, &AppearanceSettingsPage::editPalette); - - for (const auto &entry : BackgroundSources::all()) { - homeTabBackgroundSourceBox.addItem(QObject::tr(entry.trKey), QVariant::fromValue(entry.type)); - } - - QString homeTabBackgroundSource = SettingsCache::instance().getHomeTabBackgroundSource(); - int homeTabBackgroundSourceId = - homeTabBackgroundSourceBox.findData(BackgroundSources::fromId(homeTabBackgroundSource)); - if (homeTabBackgroundSourceId != -1) { - homeTabBackgroundSourceBox.setCurrentIndex(homeTabBackgroundSourceId); - } - - connect(&homeTabBackgroundSourceBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { - auto type = homeTabBackgroundSourceBox.currentData().value(); - SettingsCache::instance().setHomeTabBackgroundSource(BackgroundSources::toId(type)); - updateHomeTabSettingsVisibility(); - }); - - homeTabBackgroundShuffleFrequencySpinBox.setRange(0, 3600); - homeTabBackgroundShuffleFrequencySpinBox.setSuffix(tr(" seconds")); - homeTabBackgroundShuffleFrequencySpinBox.setValue(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency()); - connect(&homeTabBackgroundShuffleFrequencySpinBox, qOverload(&QSpinBox::valueChanged), - &SettingsCache::instance(), &SettingsCache::setHomeTabBackgroundShuffleFrequency); - - homeTabDisplayCardNameCheckBox.setChecked(settings.getHomeTabDisplayCardName()); - connect(&homeTabDisplayCardNameCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setHomeTabDisplayCardName); - - updateHomeTabSettingsVisibility(); - - auto *themeGrid = new QGridLayout; - themeGrid->addWidget(&themeLabel, 0, 0); - themeGrid->addWidget(&themeBox, 0, 1); - themeGrid->addWidget(&openThemeButton, 1, 1); - themeGrid->addWidget(&schemeComboLabel, 2, 0); - themeGrid->addWidget(&schemeCombo, 2, 1); - themeGrid->addWidget(&editPaletteButton, 3, 1); - themeGrid->addWidget(&homeTabBackgroundSourceLabel, 4, 0); - themeGrid->addWidget(&homeTabBackgroundSourceBox, 4, 1); - themeGrid->addWidget(&homeTabBackgroundShuffleFrequencyLabel, 5, 0); - themeGrid->addWidget(&homeTabBackgroundShuffleFrequencySpinBox, 5, 1); - themeGrid->addWidget(&homeTabDisplayCardNameLabel, 6, 0); - themeGrid->addWidget(&homeTabDisplayCardNameCheckBox, 6, 1); - - themeGroupBox = new QGroupBox; - themeGroupBox->setLayout(themeGrid); - - // Menu settings - showShortcutsCheckBox.setChecked(settings.getShowShortcuts()); - connect(&showShortcutsCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &AppearanceSettingsPage::showShortcutsChanged); - - showGameSelectorFilterToolbarCheckBox.setChecked(settings.getShowGameSelectorFilterToolbar()); - connect(&showGameSelectorFilterToolbarCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setShowGameSelectorFilterToolbar); - - auto *menuGrid = new QGridLayout; - menuGrid->addWidget(&showShortcutsCheckBox, 0, 0); - menuGrid->addWidget(&showGameSelectorFilterToolbarCheckBox, 1, 0); - - menuGroupBox = new QGroupBox; - menuGroupBox->setLayout(menuGrid); - - // Card rendering - displayCardNamesCheckBox.setChecked(settings.getDisplayCardNames()); - connect(&displayCardNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setDisplayCardNames); - - autoRotateSidewaysLayoutCardsCheckBox.setChecked(settings.getAutoRotateSidewaysLayoutCards()); - connect(&autoRotateSidewaysLayoutCardsCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setAutoRotateSidewaysLayoutCards); - - overrideAllCardArtWithPersonalPreferenceCheckBox.setChecked(settings.getOverrideAllCardArtWithPersonalPreference()); - connect(&overrideAllCardArtWithPersonalPreferenceCheckBox, &QCheckBox::QT_STATE_CHANGED, this, - &AppearanceSettingsPage::overrideAllCardArtWithPersonalPreferenceToggled); - - bumpSetsWithCardsInDeckToTopCheckBox.setChecked(settings.getBumpSetsWithCardsInDeckToTop()); - connect(&bumpSetsWithCardsInDeckToTopCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setBumpSetsWithCardsInDeckToTop); - - cardScalingCheckBox.setChecked(settings.getScaleCards()); - connect(&cardScalingCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setCardScaling); - - roundCardCornersCheckBox.setChecked(settings.getRoundCardCorners()); - connect(&roundCardCornersCheckBox, &QAbstractButton::toggled, &settings, &SettingsCache::setRoundCardCorners); - - verticalCardOverlapPercentBox.setValue(settings.getStackCardOverlapPercent()); - verticalCardOverlapPercentBox.setRange(0, 80); - connect(&verticalCardOverlapPercentBox, qOverload(&QSpinBox::valueChanged), &settings, - &SettingsCache::setStackCardOverlapPercent); - - cardViewInitialRowsMaxBox.setRange(1, 999); - cardViewInitialRowsMaxBox.setValue(SettingsCache::instance().getCardViewInitialRowsMax()); - connect(&cardViewInitialRowsMaxBox, qOverload(&QSpinBox::valueChanged), this, - &AppearanceSettingsPage::cardViewInitialRowsMaxChanged); - - cardViewExpandedRowsMaxBox.setRange(1, 999); - cardViewExpandedRowsMaxBox.setValue(SettingsCache::instance().getCardViewExpandedRowsMax()); - connect(&cardViewExpandedRowsMaxBox, qOverload(&QSpinBox::valueChanged), this, - &AppearanceSettingsPage::cardViewExpandedRowsMaxChanged); - - auto *cardsGrid = new QGridLayout; - cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2); - cardsGrid->addWidget(&autoRotateSidewaysLayoutCardsCheckBox, 1, 0, 1, 2); - cardsGrid->addWidget(&cardScalingCheckBox, 2, 0, 1, 2); - cardsGrid->addWidget(&roundCardCornersCheckBox, 3, 0, 1, 2); - cardsGrid->addWidget(&overrideAllCardArtWithPersonalPreferenceCheckBox, 4, 0, 1, 2); - cardsGrid->addWidget(&bumpSetsWithCardsInDeckToTopCheckBox, 5, 0, 1, 2); - cardsGrid->addWidget(&verticalCardOverlapPercentLabel, 6, 0, 1, 1); - cardsGrid->addWidget(&verticalCardOverlapPercentBox, 6, 1, 1, 1); - cardsGrid->addWidget(&cardViewInitialRowsMaxLabel, 7, 0); - cardsGrid->addWidget(&cardViewInitialRowsMaxBox, 7, 1); - cardsGrid->addWidget(&cardViewExpandedRowsMaxLabel, 8, 0); - cardsGrid->addWidget(&cardViewExpandedRowsMaxBox, 8, 1); - - cardsGroupBox = new QGroupBox; - cardsGroupBox->setLayout(cardsGrid); - - // Card counter colors - - auto *cardCounterColorsLayout = new QGridLayout; - cardCounterColorsLayout->setColumnStretch(1, 1); - cardCounterColorsLayout->setColumnStretch(3, 1); - cardCounterColorsLayout->setColumnStretch(5, 1); - - auto &cardCounterSettings = SettingsCache::instance().cardCounters(); - for (int index = 0; index < 6; ++index) { - auto *pushButton = new QPushButton; - pushButton->setStyleSheet(QString("background-color: %1").arg(cardCounterSettings.color(index).name())); - - connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, pushButton, - [index, pushButton](int changedIndex, const QColor &color) { - if (index == changedIndex) { - pushButton->setStyleSheet(QString("background-color: %1").arg(color.name())); - } - }); - - connect(pushButton, &QPushButton::clicked, this, [index, this]() { - auto &cardCounterSettings = SettingsCache::instance().cardCounters(); - - auto newColor = QColorDialog::getColor(cardCounterSettings.color(index), this); - if (!newColor.isValid()) { - return; - } - - cardCounterSettings.setColor(index, newColor); - }); - - auto *colorName = new QLabel; - cardCounterNames.append(colorName); - - int row = index / 3; - int column = 2 * (index % 3); - - cardCounterColorsLayout->addWidget(pushButton, row, column); - cardCounterColorsLayout->addWidget(colorName, row, column + 1); - } - - auto *cardCountersLayout = new QVBoxLayout; - cardCountersLayout->addLayout(cardCounterColorsLayout, 1); - - cardCountersGroupBox = new QGroupBox; - cardCountersGroupBox->setLayout(cardCountersLayout); - - // Hand layout - horizontalHandCheckBox.setChecked(settings.getHorizontalHand()); - connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand); - - leftJustifiedHandCheckBox.setChecked(settings.getLeftJustified()); - connect(&leftJustifiedHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setLeftJustified); - - auto *handGrid = new QGridLayout; - handGrid->addWidget(&horizontalHandCheckBox, 0, 0, 1, 2); - handGrid->addWidget(&leftJustifiedHandCheckBox, 1, 0, 1, 2); - - handGroupBox = new QGroupBox; - handGroupBox->setLayout(handGrid); - - // table grid layout - invertVerticalCoordinateCheckBox.setChecked(settings.getInvertVerticalCoordinate()); - connect(&invertVerticalCoordinateCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, - &SettingsCache::setInvertVerticalCoordinate); - - minPlayersForMultiColumnLayoutEdit.setMinimum(2); - minPlayersForMultiColumnLayoutEdit.setValue(settings.getMinPlayersForMultiColumnLayout()); - connect(&minPlayersForMultiColumnLayoutEdit, qOverload(&QSpinBox::valueChanged), &settings, - &SettingsCache::setMinPlayersForMultiColumnLayout); - minPlayersForMultiColumnLayoutLabel.setBuddy(&minPlayersForMultiColumnLayoutEdit); - - connect(&maxFontSizeForCardsEdit, qOverload(&QSpinBox::valueChanged), &settings, - &SettingsCache::setMaxFontSize); - maxFontSizeForCardsEdit.setValue(settings.getMaxFontSize()); - maxFontSizeForCardsLabel.setBuddy(&maxFontSizeForCardsEdit); - maxFontSizeForCardsEdit.setMinimum(9); - maxFontSizeForCardsEdit.setMaximum(100); - - auto *tableGrid = new QGridLayout; - tableGrid->addWidget(&invertVerticalCoordinateCheckBox, 0, 0, 1, 2); - tableGrid->addWidget(&minPlayersForMultiColumnLayoutLabel, 1, 0, 1, 1); - tableGrid->addWidget(&minPlayersForMultiColumnLayoutEdit, 1, 1, 1, 1); - tableGrid->addWidget(&maxFontSizeForCardsLabel, 2, 0, 1, 1); - tableGrid->addWidget(&maxFontSizeForCardsEdit, 2, 1, 1, 1); - - tableGroupBox = new QGroupBox; - tableGroupBox->setLayout(tableGrid); - - // putting it all together - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(themeGroupBox); - mainLayout->addWidget(menuGroupBox); - mainLayout->addWidget(cardsGroupBox); - mainLayout->addWidget(cardCountersGroupBox); - mainLayout->addWidget(handGroupBox); - mainLayout->addWidget(tableGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &AppearanceSettingsPage::retranslateUi); - retranslateUi(); -} - -void AppearanceSettingsPage::themeBoxChanged(int index) -{ - QStringList themeDirs = themeManager->getAvailableThemes().keys(); - if (index >= 0 && index < themeDirs.count()) { - SettingsCache::instance().setThemeName(themeDirs.at(index)); - } -} - -void AppearanceSettingsPage::openThemeLocation() -{ - QString dir = SettingsCache::instance().getThemesPath(); - QDir dirDir = dir; - dirDir.cdUp(); - // open if dir exists, create if parent dir does exist - if (dirDir.exists() && dirDir.mkpath(dir)) { - QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); - } else { - QMessageBox::critical(this, tr("Error"), tr("Could not create themes directory at '%1'.").arg(dir)); - } -} - -void AppearanceSettingsPage::editPalette() -{ - PaletteEditorDialog dlg(themeManager->getCurrentThemePath(), SettingsCache::instance().getThemeName(), this); - dlg.exec(); -} - -void AppearanceSettingsPage::updateHomeTabSettingsVisibility() -{ - bool visible = - SettingsCache::instance().getHomeTabBackgroundSource() != BackgroundSources::toId(BackgroundSources::Theme); - - homeTabBackgroundShuffleFrequencyLabel.setVisible(visible); - homeTabBackgroundShuffleFrequencySpinBox.setVisible(visible); - homeTabDisplayCardNameLabel.setVisible(visible); - homeTabDisplayCardNameCheckBox.setVisible(visible); -} - -void AppearanceSettingsPage::showShortcutsChanged(QT_STATE_CHANGED_T value) -{ - SettingsCache::instance().setShowShortcuts(value); - qApp->setAttribute(Qt::AA_DontShowShortcutsInContextMenus, value == 0); // 0 = unchecked -} - -void AppearanceSettingsPage::overrideAllCardArtWithPersonalPreferenceToggled(QT_STATE_CHANGED_T value) -{ - bool enable = static_cast(value); - - bool accepted = OverridePrintingWarning::execMessageBox(this, enable); - - if (!accepted) { - // If user cancels, revert the checkbox/state back - QTimer::singleShot(0, this, [this, enable]() { - overrideAllCardArtWithPersonalPreferenceCheckBox.blockSignals(true); - overrideAllCardArtWithPersonalPreferenceCheckBox.setChecked(!enable); - overrideAllCardArtWithPersonalPreferenceCheckBox.blockSignals(false); - }); - } -} - -/** - * Updates the settings for cardViewInitialRowsMax. - * Forces expanded rows max to always be >= initial rows max - * @param value The new value - */ -void AppearanceSettingsPage::cardViewInitialRowsMaxChanged(int value) -{ - SettingsCache::instance().setCardViewInitialRowsMax(value); - if (cardViewExpandedRowsMaxBox.value() < value) { - cardViewExpandedRowsMaxBox.setValue(value); - } -} - -/** - * Updates the settings for cardViewExpandedRowsMax. - * Forces initial rows max to always be <= expanded rows max - * @param value The new value - */ -void AppearanceSettingsPage::cardViewExpandedRowsMaxChanged(int value) -{ - SettingsCache::instance().setCardViewExpandedRowsMax(value); - if (cardViewInitialRowsMaxBox.value() > value) { - cardViewInitialRowsMaxBox.setValue(value); - } -} - -void AppearanceSettingsPage::retranslateUi() -{ - themeGroupBox->setTitle(tr("Theme settings")); - themeLabel.setText(tr("Current theme:")); - openThemeButton.setText(tr("Open themes folder")); - schemeComboLabel.setText(tr("Active theme palette:")); - editPaletteButton.setText(tr("Edit theme palette")); - homeTabBackgroundSourceLabel.setText(tr("Home tab background source:")); - homeTabBackgroundShuffleFrequencyLabel.setText(tr("Home tab background shuffle frequency:")); - homeTabBackgroundShuffleFrequencySpinBox.setSpecialValueText(tr("Disabled")); - homeTabDisplayCardNameLabel.setText(tr("Display card name of background in bottom right:")); - - menuGroupBox->setTitle(tr("Menu settings")); - showShortcutsCheckBox.setText(tr("Show keyboard shortcuts in right-click menus")); - showGameSelectorFilterToolbarCheckBox.setText(tr("Show game filter toolbar above list in room tab")); - - cardsGroupBox->setTitle(tr("Card rendering")); - displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture")); - autoRotateSidewaysLayoutCardsCheckBox.setText(tr("Auto-Rotate cards with sideways layout")); - overrideAllCardArtWithPersonalPreferenceCheckBox.setText( - tr("Override all card art with personal set preference (Pre-ProviderID change behavior)")); - bumpSetsWithCardsInDeckToTopCheckBox.setText( - tr("Bump sets that the deck contains cards from to the top in the printing selector")); - cardScalingCheckBox.setText(tr("Scale cards on mouse over")); - roundCardCornersCheckBox.setText(tr("Use rounded card corners")); - verticalCardOverlapPercentLabel.setText( - tr("Minimum overlap percentage of cards on the stack and in vertical hand")); - cardViewInitialRowsMaxLabel.setText(tr("Maximum initial height for card view window:")); - cardViewInitialRowsMaxBox.setSuffix(tr(" rows")); - cardViewExpandedRowsMaxLabel.setText(tr("Maximum expanded height for card view window:")); - cardViewExpandedRowsMaxBox.setSuffix(tr(" rows")); - - cardCountersGroupBox->setTitle(tr("Card counters")); - - auto &cardCounterSettings = SettingsCache::instance().cardCounters(); - for (int index = 0; index < cardCounterNames.size(); ++index) { - cardCounterNames[index]->setText(tr("Counter %1").arg(cardCounterSettings.displayName(index))); - } - - handGroupBox->setTitle(tr("Hand layout")); - horizontalHandCheckBox.setText(tr("Display hand horizontally (wastes space)")); - leftJustifiedHandCheckBox.setText(tr("Enable left justification")); - - tableGroupBox->setTitle(tr("Table grid layout")); - invertVerticalCoordinateCheckBox.setText(tr("Invert vertical coordinate")); - minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:")); - maxFontSizeForCardsLabel.setText(tr("Maximum font size for information displayed on cards:")); -} - -enum visualDeckStoragePromptForConversionIndex -{ - visualDeckStoragePromptForConversionIndexNone, - visualDeckStoragePromptForConversionIndexPrompt, - visualDeckStoragePromptForConversionIndexAlways -}; - -UserInterfaceSettingsPage::UserInterfaceSettingsPage() -{ - // general settings and notification settings - notificationsEnabledCheckBox.setChecked(SettingsCache::instance().getNotificationsEnabled()); - connect(¬ificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setNotificationsEnabled); - connect(¬ificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, this, - &UserInterfaceSettingsPage::setNotificationEnabled); - - specNotificationsEnabledCheckBox.setChecked(SettingsCache::instance().getSpectatorNotificationsEnabled()); - specNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled()); - connect(&specNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setSpectatorNotificationsEnabled); - - buddyConnectNotificationsEnabledCheckBox.setChecked( - SettingsCache::instance().getBuddyConnectNotificationsEnabled()); - buddyConnectNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled()); - connect(&buddyConnectNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setBuddyConnectNotificationsEnabled); - - doubleClickToPlayCheckBox.setChecked(SettingsCache::instance().getDoubleClickToPlay()); - connect(&doubleClickToPlayCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setDoubleClickToPlay); - - clickPlaysAllSelectedCheckBox.setChecked(SettingsCache::instance().getClickPlaysAllSelected()); - connect(&clickPlaysAllSelectedCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setClickPlaysAllSelected); - - playToStackCheckBox.setChecked(SettingsCache::instance().getPlayToStack()); - connect(&playToStackCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setPlayToStack); - - doNotDeleteArrowsInSubPhasesCheckBox.setChecked(SettingsCache::instance().getDoNotDeleteArrowsInSubPhases()); - connect(&doNotDeleteArrowsInSubPhasesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setDoNotDeleteArrowsInSubPhases); - - closeEmptyCardViewCheckBox.setChecked(SettingsCache::instance().getCloseEmptyCardView()); - connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setCloseEmptyCardView); - - focusCardViewSearchBarCheckBox.setChecked(SettingsCache::instance().getFocusCardViewSearchBar()); - connect(&focusCardViewSearchBarCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setFocusCardViewSearchBar); - - annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens()); - connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setAnnotateTokens); - - showDragSelectionCountCheckBox.setChecked(SettingsCache::instance().getShowDragSelectionCount()); - connect(&showDragSelectionCountCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setShowDragSelectionCount); - - showTotalSelectionCountCheckBox.setChecked(SettingsCache::instance().getShowTotalSelectionCount()); - connect(&showTotalSelectionCountCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setShowTotalSelectionCount); - - useTearOffMenusCheckBox.setChecked(SettingsCache::instance().getUseTearOffMenus()); - connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); - - auto *generalGrid = new QGridLayout; - generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0); - generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); - generalGrid->addWidget(&playToStackCheckBox, 2, 0); - generalGrid->addWidget(&doNotDeleteArrowsInSubPhasesCheckBox, 3, 0); - generalGrid->addWidget(&closeEmptyCardViewCheckBox, 4, 0); - generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 5, 0); - generalGrid->addWidget(&annotateTokensCheckBox, 6, 0); - generalGrid->addWidget(&showDragSelectionCountCheckBox, 7, 0); - generalGrid->addWidget(&showTotalSelectionCountCheckBox, 8, 0); - generalGrid->addWidget(&useTearOffMenusCheckBox, 9, 0); - - generalGroupBox = new QGroupBox; - generalGroupBox->setLayout(generalGrid); - - auto *notificationsGrid = new QGridLayout; - notificationsGrid->addWidget(¬ificationsEnabledCheckBox, 0, 0); - notificationsGrid->addWidget(&specNotificationsEnabledCheckBox, 1, 0); - notificationsGrid->addWidget(&buddyConnectNotificationsEnabledCheckBox, 2, 0); - - notificationsGroupBox = new QGroupBox; - notificationsGroupBox->setLayout(notificationsGrid); - - // animation settings - tapAnimationCheckBox.setChecked(SettingsCache::instance().getTapAnimation()); - connect(&tapAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setTapAnimation); - - auto *animationGrid = new QGridLayout; - animationGrid->addWidget(&tapAnimationCheckBox, 0, 0); - - animationGroupBox = new QGroupBox; - animationGroupBox->setLayout(animationGrid); - - // deck editor settings - openDeckInNewTabCheckBox.setChecked(SettingsCache::instance().getOpenDeckInNewTab()); - connect(&openDeckInNewTabCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setOpenDeckInNewTab); - - visualDeckStorageInGameCheckBox.setChecked(SettingsCache::instance().getVisualDeckStorageInGame()); - connect(&visualDeckStorageInGameCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setVisualDeckStorageInGame); - - visualDeckStorageSelectionAnimationCheckBox.setChecked( - SettingsCache::instance().getVisualDeckStorageSelectionAnimation()); - connect(&visualDeckStorageSelectionAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setVisualDeckStorageSelectionAnimation); - - visualDeckStoragePromptForConversionSelector.addItem(""); // these will be set in retranslateUI - visualDeckStoragePromptForConversionSelector.addItem(""); - visualDeckStoragePromptForConversionSelector.addItem(""); - if (SettingsCache::instance().getVisualDeckStoragePromptForConversion()) { - visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexPrompt); - } else if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) { - visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexAlways); - } else { - visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexNone); - } - connect(&visualDeckStoragePromptForConversionSelector, QOverload::of(&QComboBox::currentIndexChanged), this, - [](int index) { - SettingsCache::instance().setVisualDeckStoragePromptForConversion( - index == visualDeckStoragePromptForConversionIndexPrompt); - SettingsCache::instance().setVisualDeckStorageAlwaysConvert( - index == visualDeckStoragePromptForConversionIndexAlways); - }); - - defaultDeckEditorTypeSelector.addItem(""); // these will be set in retranslateUI - defaultDeckEditorTypeSelector.addItem(""); - defaultDeckEditorTypeSelector.setCurrentIndex(SettingsCache::instance().getDefaultDeckEditorType()); - connect(&defaultDeckEditorTypeSelector, QOverload::of(&QComboBox::currentIndexChanged), - &SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType); - - auto *deckEditorGrid = new QGridLayout; - deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); - deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0); - deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0); - deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0); - deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1); - deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0); - deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1); - - deckEditorGroupBox = new QGroupBox; - deckEditorGroupBox->setLayout(deckEditorGrid); - - // replay settings - rewindBufferingMsBox.setRange(0, 9999); - rewindBufferingMsBox.setValue(SettingsCache::instance().getRewindBufferingMs()); - connect(&rewindBufferingMsBox, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), - &SettingsCache::setRewindBufferingMs); - - auto *replayGrid = new QGridLayout; - replayGrid->addWidget(&rewindBufferingMsLabel, 0, 0, 1, 1); - replayGrid->addWidget(&rewindBufferingMsBox, 0, 1, 1, 1); - - replayGroupBox = new QGroupBox; - replayGroupBox->setLayout(replayGrid); - - // putting it all together - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(generalGroupBox); - mainLayout->addWidget(notificationsGroupBox); - mainLayout->addWidget(animationGroupBox); - mainLayout->addWidget(deckEditorGroupBox); - mainLayout->addWidget(replayGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &UserInterfaceSettingsPage::retranslateUi); - retranslateUi(); -} - -void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i) -{ - specNotificationsEnabledCheckBox.setEnabled(i != 0); - buddyConnectNotificationsEnabledCheckBox.setEnabled(i != 0); - if (i == 0) { - specNotificationsEnabledCheckBox.setChecked(false); - buddyConnectNotificationsEnabledCheckBox.setChecked(false); - } -} - -void UserInterfaceSettingsPage::retranslateUi() -{ - generalGroupBox->setTitle(tr("General interface settings")); - doubleClickToPlayCheckBox.setText(tr("&Double-click cards to play them (instead of single-click)")); - clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)")); - playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default")); - doNotDeleteArrowsInSubPhasesCheckBox.setText(tr("Do not delete &arrows inside of subphases")); - closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed")); - focusCardViewSearchBarCheckBox.setText(tr("Auto focus search bar when card view window is opened")); - annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); - showDragSelectionCountCheckBox.setText(tr("Show selection counter during drag selection")); - showTotalSelectionCountCheckBox.setText(tr("Show total selection counter")); - useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); - notificationsGroupBox->setTitle(tr("Notifications settings")); - notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar")); - specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating")); - buddyConnectNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar when users in your buddy list connect")); - animationGroupBox->setTitle(tr("Animation settings")); - tapAnimationCheckBox.setText(tr("&Tap/untap animation")); - deckEditorGroupBox->setTitle(tr("Deck editor/storage settings")); - openDeckInNewTabCheckBox.setText(tr("Open deck in new tab by default")); - visualDeckStorageInGameCheckBox.setText(tr("Use visual deck storage in game lobby")); - visualDeckStorageSelectionAnimationCheckBox.setText(tr("Use selection animation for Visual Deck Storage")); - visualDeckStoragePromptForConversionLabel.setText( - tr("When adding a tag in the visual deck storage to a .txt deck:")); - visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexNone, - tr("do nothing")); - visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexPrompt, - tr("ask to convert to .cod")); - visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways, - tr("always convert to .cod")); - defaultDeckEditorTypeLabel.setText(tr("Default deck editor type")); - defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor")); - defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor")); - replayGroupBox->setTitle(tr("Replay settings")); - rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:")); - rewindBufferingMsBox.setSuffix(" ms"); -} - -DeckEditorSettingsPage::DeckEditorSettingsPage() -{ - picDownloadCheckBox.setChecked(SettingsCache::instance().getPicDownload()); - connect(&picDownloadCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setPicDownload); - - urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); - urlLinkLabel.setOpenExternalLinks(true); - - connect(&resetDownloadURLs, &QPushButton::clicked, this, &DeckEditorSettingsPage::resetDownloadedURLsButtonClicked); - - auto *lpGeneralGrid = new QGridLayout; - auto *lpSpoilerGrid = new QGridLayout; - - mcDownloadSpoilersCheckBox.setChecked(SettingsCache::instance().getDownloadSpoilersStatus()); - - mpSpoilerSavePathLineEdit = new QLineEdit(SettingsCache::instance().getSpoilerCardDatabasePath()); - mpSpoilerSavePathLineEdit->setReadOnly(true); - mpSpoilerPathButton = new QPushButton("..."); - connect(mpSpoilerPathButton, &QPushButton::clicked, this, &DeckEditorSettingsPage::spoilerPathButtonClicked); - - updateNowButton = new QPushButton; - updateNowButton->setFixedWidth(150); - connect(updateNowButton, &QPushButton::clicked, this, &DeckEditorSettingsPage::updateSpoilers); - - // Update the GUI depending on if the box is ticked or not - setSpoilersEnabled(mcDownloadSpoilersCheckBox.isChecked()); - - urlList = new QListWidget; - urlList->setSelectionMode(QAbstractItemView::SingleSelection); - urlList->setAlternatingRowColors(true); - urlList->setDragEnabled(true); - urlList->setDragDropMode(QAbstractItemView::InternalMove); - connect(urlList->model(), &QAbstractItemModel::rowsMoved, this, &DeckEditorSettingsPage::urlListChanged); - - urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); - - aAdd = new QAction(this); - aAdd->setIcon(QPixmap("theme:icons/increment")); - connect(aAdd, &QAction::triggered, this, &DeckEditorSettingsPage::actAddURL); - - aEdit = new QAction(this); - aEdit->setIcon(QPixmap("theme:icons/pencil")); - connect(aEdit, &QAction::triggered, this, &DeckEditorSettingsPage::actEditURL); - - aRemove = new QAction(this); - aRemove->setIcon(QPixmap("theme:icons/decrement")); - connect(aRemove, &QAction::triggered, this, &DeckEditorSettingsPage::actRemoveURL); - - auto *urlToolBar = new QToolBar; - urlToolBar->setOrientation(Qt::Vertical); - urlToolBar->addAction(aAdd); - urlToolBar->addAction(aRemove); - urlToolBar->addAction(aEdit); - urlToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - - auto *urlListLayout = new QHBoxLayout; - urlListLayout->addWidget(urlToolBar); - urlListLayout->addWidget(urlList); - - // Top Layout - lpGeneralGrid->addWidget(&picDownloadCheckBox, 0, 0); - lpGeneralGrid->addWidget(&resetDownloadURLs, 0, 1); - lpGeneralGrid->addLayout(urlListLayout, 1, 0, 1, 2); - lpGeneralGrid->addWidget(&urlLinkLabel, 4, 0); - - // Spoiler Layout - lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0); - lpSpoilerGrid->addWidget(&mcSpoilerSaveLabel, 1, 0); - lpSpoilerGrid->addWidget(mpSpoilerSavePathLineEdit, 1, 1); - lpSpoilerGrid->addWidget(mpSpoilerPathButton, 1, 2); - lpSpoilerGrid->addWidget(&lastUpdatedLabel, 2, 0); - lpSpoilerGrid->addWidget(updateNowButton, 2, 1); - lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 3, 0, 1, 3, Qt::AlignTop); - - // On a change to the checkbox, hide/un-hide the other fields - connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, &SettingsCache::instance(), - &SettingsCache::setDownloadSpoilerStatus); - connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, this, &DeckEditorSettingsPage::setSpoilersEnabled); - - mpGeneralGroupBox = new QGroupBox; - mpGeneralGroupBox->setLayout(lpGeneralGrid); - - mpSpoilerGroupBox = new QGroupBox; - mpSpoilerGroupBox->setLayout(lpSpoilerGrid); - - auto *lpMainLayout = new QVBoxLayout; - lpMainLayout->addWidget(mpGeneralGroupBox); - lpMainLayout->addWidget(mpSpoilerGroupBox); - - setLayout(lpMainLayout); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &DeckEditorSettingsPage::retranslateUi); - retranslateUi(); -} - -void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked() -{ - SettingsCache::instance().downloads().resetToDefaultURLs(); - urlList->clear(); - urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); - QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset.")); -} - -void DeckEditorSettingsPage::actAddURL() -{ - bool ok; - QString msg = QInputDialog::getText(this, tr("Add URL"), tr("URL:"), QLineEdit::Normal, QString(), &ok); - if (ok) { - urlList->addItem(msg); - storeSettings(); - } -} - -void DeckEditorSettingsPage::actRemoveURL() -{ - if (urlList->currentItem() != nullptr) { - delete urlList->takeItem(urlList->currentRow()); - storeSettings(); - } -} - -void DeckEditorSettingsPage::actEditURL() -{ - if (urlList->currentItem()) { - QString oldText = urlList->currentItem()->text(); - bool ok; - QString msg = QInputDialog::getText(this, tr("Edit URL"), tr("URL:"), QLineEdit::Normal, oldText, &ok); - if (ok) { - urlList->currentItem()->setText(msg); - storeSettings(); - } - } -} - -void DeckEditorSettingsPage::storeSettings() -{ - qInfo() << "URL Priority Reset"; - - QStringList downloadUrls; - for (int i = 0; i < urlList->count(); i++) { - qInfo() << "Priority" << i << ":" << urlList->item(i)->text(); - downloadUrls << urlList->item(i)->text(); - } - SettingsCache::instance().downloads().setDownloadUrls(downloadUrls); -} - -void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int) -{ - storeSettings(); -} - -void DeckEditorSettingsPage::updateSpoilers() -{ - // Disable the button so the user can only press it once at a time - updateNowButton->setDisabled(true); - updateNowButton->setText(tr("Updating...")); - - // Create a new SBU that will act as if the client was just reloaded - auto *sbu = new SpoilerBackgroundUpdater(); - connect(sbu, &SpoilerBackgroundUpdater::spoilerCheckerDone, this, &DeckEditorSettingsPage::unlockSettings); - connect(sbu, &SpoilerBackgroundUpdater::spoilersUpdatedSuccessfully, this, &DeckEditorSettingsPage::unlockSettings); -} - -void DeckEditorSettingsPage::unlockSettings() -{ - updateNowButton->setDisabled(false); - updateNowButton->setText(tr("Update Spoilers")); -} - -QString DeckEditorSettingsPage::getLastUpdateTime() -{ - QString fileName = SettingsCache::instance().getSpoilerCardDatabasePath(); - QFileInfo fi(fileName); - QDir fileDir(fi.path()); - QFile file(fileName); - - if (file.exists()) { - return fi.lastModified().toString("MMM d, hh:mm"); - } - - return QString(); -} - -void DeckEditorSettingsPage::spoilerPathButtonClicked() -{ - QString lsPath = QFileDialog::getExistingDirectory(this, tr("Choose path"), mpSpoilerSavePathLineEdit->text()); - if (lsPath.isEmpty()) { - return; - } - - mpSpoilerSavePathLineEdit->setText(lsPath + "/spoiler.xml"); - SettingsCache::instance().setSpoilerDatabasePath(lsPath + "/spoiler.xml"); -} - -void DeckEditorSettingsPage::setSpoilersEnabled(bool anInput) -{ - msDownloadSpoilersLabel.setEnabled(anInput); - mcSpoilerSaveLabel.setEnabled(anInput); - mpSpoilerSavePathLineEdit->setEnabled(anInput); - mpSpoilerPathButton->setEnabled(anInput); - lastUpdatedLabel.setEnabled(anInput); - updateNowButton->setEnabled(anInput); - infoOnSpoilersLabel.setEnabled(anInput); - - if (!anInput) { - SpoilerBackgroundUpdater::deleteSpoilerFile(); - } -} - -void DeckEditorSettingsPage::retranslateUi() -{ - mpGeneralGroupBox->setTitle(tr("URL Download Priority")); - mpSpoilerGroupBox->setTitle(tr("Spoilers")); - mcDownloadSpoilersCheckBox.setText(tr("Download Spoilers Automatically")); - mcSpoilerSaveLabel.setText(tr("Spoiler Location:")); - lastUpdatedLabel.setText(tr("Last Change") + ": " + getLastUpdateTime()); - infoOnSpoilersLabel.setText(tr("Spoilers download automatically on launch") + "\n" + - tr("Press the button to manually update without relaunching") + "\n\n" + - tr("Do not close settings until manual update is complete")); - picDownloadCheckBox.setText(tr("Download card pictures on the fly")); - urlLinkLabel.setText(QString("%2").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL"))); - resetDownloadURLs.setText(tr("Reset Download URLs")); - updateNowButton->setText(tr("Update Spoilers")); - aAdd->setText(tr("Add New URL")); - aEdit->setText(tr("Edit URL")); - aRemove->setText(tr("Remove URL")); -} - -StorageSettingsPage::StorageSettingsPage() -{ - auto *lpNetworkCacheGrid = new QGridLayout; - auto *lpImageBackupGrid = new QGridLayout; - auto *lpPixmapCacheGrid = new QGridLayout; - - networkCacheExplainerLabel.setWordWrap(true); - imageBackupExplainerLabel.setWordWrap(true); - pixmapCacheExplainerLabel.setWordWrap(true); - - connect(&clearDownloadedPicsButton, &QPushButton::clicked, this, - &StorageSettingsPage::clearDownloadedPicsButtonClicked); - - connect(&clearPixmapCacheButton, &QPushButton::clicked, this, &StorageSettingsPage::clearPixmapCacheButtonClicked); - - // pixmap cache - pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN); - // 2047 is the max value to avoid overflowing of QPixmapCache::setCacheLimit(int size) - pixmapCacheEdit.setMaximum(PIXMAPCACHE_SIZE_MAX); - pixmapCacheEdit.setSingleStep(64); - pixmapCacheEdit.setValue(SettingsCache::instance().getPixmapCacheSize()); - pixmapCacheEdit.setSuffix(" MB"); - - // Caching method - - cardPictureLoaderCacheMethodComboBox = new QComboBox; - for (auto method : CardPictureLoaderCacheMethod::methods()) { - cardPictureLoaderCacheMethodComboBox->addItem(method.displayName, static_cast(method.id)); - } - - int currentCacheMethod = static_cast(SettingsCache::instance().getCardPictureLoaderCacheMethod()); - - int currentIndex = cardPictureLoaderCacheMethodComboBox->findData(currentCacheMethod); - if (currentIndex >= 0) { - cardPictureLoaderCacheMethodComboBox->setCurrentIndex(currentIndex); - } - - connect(cardPictureLoaderCacheMethodComboBox, qOverload(&QComboBox::currentIndexChanged), this, - [this](int index) { - auto cacheMethod = static_cast( - cardPictureLoaderCacheMethodComboBox->itemData(index).toInt()); - - bool useNetworkCache = (cacheMethod == CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE); - - if (useNetworkCache) { - clearImageBackupsButtonClicked(); - } else { - clearDownloadedPicsButtonClicked(); - } - - mpNetworkCacheGroupBox->setEnabled(useNetworkCache); - mpImageBackupGroupBox->setEnabled(!useNetworkCache); - - SettingsCache::instance().setCardImageCacheMethod(cacheMethod); - }); - - // Network Cache - - networkCacheEdit.setMinimum(NETWORK_CACHE_SIZE_MIN); - networkCacheEdit.setMaximum(NETWORK_CACHE_SIZE_MAX); - networkCacheEdit.setSingleStep(1); - networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB()); - networkCacheEdit.setSuffix(" MB"); - - networkRedirectCacheTtlEdit.setMinimum(NETWORK_REDIRECT_CACHE_TTL_MIN); - networkRedirectCacheTtlEdit.setMaximum(NETWORK_REDIRECT_CACHE_TTL_MAX); - networkRedirectCacheTtlEdit.setSingleStep(1); - networkRedirectCacheTtlEdit.setValue(SettingsCache::instance().getRedirectCacheTtl()); - - // Image Backup - localCardImageStorageNamingSchemeComboBox = new QComboBox; - for (const auto &scheme : CardPictureLoaderLocalSchemes::exportSchemes()) { - localCardImageStorageNamingSchemeComboBox->addItem(scheme.displayName, static_cast(scheme.id)); - } - - int current = static_cast(SettingsCache::instance().getLocalCardImageStorageNamingScheme()); - - int index = localCardImageStorageNamingSchemeComboBox->findData(current); - if (index >= 0) { - localCardImageStorageNamingSchemeComboBox->setCurrentIndex(index); - } - - connect(localCardImageStorageNamingSchemeComboBox, qOverload(&QComboBox::currentIndexChanged), this, - [this](int index) { - auto scheme = static_cast( - localCardImageStorageNamingSchemeComboBox->itemData(index).toInt()); - SettingsCache::instance().setLocalCardImageStorageNamingScheme(scheme); - }); - - connect(&clearBackupsButton, &QPushButton::clicked, this, &StorageSettingsPage::clearImageBackupsButtonClicked); - - auto cacheMethodLayout = new QHBoxLayout; - cacheMethodLayout->addWidget(&cardPictureLoaderCacheMethodLabel); - cacheMethodLayout->addWidget(cardPictureLoaderCacheMethodComboBox); - - auto networkCacheLayout = new QHBoxLayout; - networkCacheLayout->addWidget(&clearDownloadedPicsButton); - networkCacheLayout->addStretch(); - networkCacheLayout->addWidget(&networkCacheLabel); - networkCacheLayout->addWidget(&networkCacheEdit); - - auto networkRedirectCacheLayout = new QHBoxLayout; - networkRedirectCacheLayout->addStretch(); - networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlLabel); - networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlEdit); - - auto pixmapCacheLayout = new QHBoxLayout; - pixmapCacheLayout->addWidget(&clearPixmapCacheButton); - pixmapCacheLayout->addStretch(); - pixmapCacheLayout->addWidget(&pixmapCacheLabel); - pixmapCacheLayout->addWidget(&pixmapCacheEdit); - - lpNetworkCacheGrid->addWidget(&networkCacheExplainerLabel, 0, 0); - lpNetworkCacheGrid->addLayout(networkCacheLayout, 1, 0); - lpNetworkCacheGrid->addLayout(networkRedirectCacheLayout, 2, 0); - - // Image Backup Layout - lpImageBackupGrid->addWidget(&imageBackupExplainerLabel, 0, 0, 1, 2); - lpImageBackupGrid->addWidget(&localCardImageStorageNamingSchemeLabel, 1, 0); - lpImageBackupGrid->addWidget(localCardImageStorageNamingSchemeComboBox, 1, 1); - lpImageBackupGrid->addWidget(&clearBackupsButton, 2, 0); - - lpPixmapCacheGrid->addWidget(&pixmapCacheExplainerLabel, 0, 0); - lpPixmapCacheGrid->addLayout(pixmapCacheLayout, 1, 0); - - connect(&pixmapCacheEdit, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), - &SettingsCache::setPixmapCacheSize); - connect(&networkCacheEdit, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), - &SettingsCache::setNetworkCacheSizeInMB); - connect(&networkRedirectCacheTtlEdit, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), - &SettingsCache::setNetworkRedirectCacheTtl); - - mpCacheMethodGroupBox = new QGroupBox; - mpCacheMethodGroupBox->setLayout(cacheMethodLayout); - - mpNetworkCacheGroupBox = new QGroupBox; - mpNetworkCacheGroupBox->setLayout(lpNetworkCacheGrid); - - mpImageBackupGroupBox = new QGroupBox; - mpImageBackupGroupBox->setLayout(lpImageBackupGrid); - - mpPixmapCacheGroupBox = new QGroupBox; - mpPixmapCacheGroupBox->setLayout(lpPixmapCacheGrid); - - auto *lpMainLayout = new QVBoxLayout; - - lpMainLayout->addWidget(mpCacheMethodGroupBox); - lpMainLayout->addWidget(mpNetworkCacheGroupBox); - lpMainLayout->addWidget(mpImageBackupGroupBox); - lpMainLayout->addWidget(mpPixmapCacheGroupBox); - lpMainLayout->addStretch(); - - setLayout(lpMainLayout); - - bool useNetworkCache = SettingsCache::instance().getCardPictureLoaderCacheMethod() == - CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE; - - mpNetworkCacheGroupBox->setEnabled(useNetworkCache); - mpImageBackupGroupBox->setEnabled(!useNetworkCache); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &StorageSettingsPage::retranslateUi); - retranslateUi(); -} - -void StorageSettingsPage::clearDownloadedPicsButtonClicked() -{ - CardPictureLoader::clearNetworkCache(); - CardPictureLoader::clearPixmapCache(); - QMessageBox::information(this, tr("Success"), tr("Cached card pictures have been reset.")); -} - -void StorageSettingsPage::clearImageBackupsButtonClicked() -{ - QString picsPath = SettingsCache::instance().getPicsPath() + "/downloadedPics"; - - QDir dir(picsPath); - bool success = dir.removeRecursively(); - - CardPictureLoader::clearPixmapCache(); - - if (success) { - QMessageBox::information(this, tr("Success"), tr("Downloaded card pictures have been reset.")); - } else { - QMessageBox::critical(this, tr("Error"), tr("One or more downloaded card pictures could not be cleared.")); - } -} - -void StorageSettingsPage::clearPixmapCacheButtonClicked() -{ - CardPictureLoader::clearPixmapCache(); - QMessageBox::information(this, tr("Success"), tr("In-memory (currently loaded) card pictures have been reset.")); -} - -void StorageSettingsPage::retranslateUi() -{ - cardPictureLoaderCacheMethodLabel.setText(tr("Card Picture Loader Caching Method:")); - - networkCacheExplainerLabel.setText( - tr("The network cache is the preferred way of storing images. Downloaded images " - "are stored here until the size of the cache exceeds the configured size. Cockatrice automatically monitors " - "this cache and deletes the least recently seen card images to ensure the cache does not exceed the " - "configured size.")); - imageBackupExplainerLabel.setText( - tr("Writing card images directly to a folder on your hard drive is another way " - "of storing images. This does not change how Cockatrice accesses or downloads " - "images. Cockatrice will NOT automatically monitor and clear this folder, so if you enable this option, it " - "is up to you to ensure sufficient available space. It should also be noted that if a provider outage " - "causes you to download the wrong picture (i.e. wrong printing) you will be stuck with it until you " - "manually delete the file, as opposed to using the network cache, which automatically rotates and thus " - "correct errors after a while.")); - pixmapCacheExplainerLabel.setText( - tr("This is the in-memory picture cache used by the application at runtime. It determines how much memory " - "(RAM) Cockatrice can use before it has to fetch card images from the hard disk again. Increasing this will " - "allow more card images to be displayed at once but shouldn't be necessary. Clearing this will make " - "Cockatrice reload all images from the network cache or the disk.")); - - clearDownloadedPicsButton.setText(tr("Delete Cached Images")); - clearBackupsButton.setText(tr("Delete Saved Images")); - clearPixmapCacheButton.setText(tr("Clear In-Memory Images")); - - mpCacheMethodGroupBox->setTitle(tr("Card Picture Loader Cache Method")); - mpNetworkCacheGroupBox->setTitle(tr("Network Cache")); - mpImageBackupGroupBox->setTitle(tr("Filesystem")); - mpPixmapCacheGroupBox->setTitle(tr("In-Memory Picture Cache")); - - networkCacheLabel.setText(tr("Network Cache Size:")); - networkCacheEdit.setToolTip(tr("On-disk cache for downloaded pictures")); - networkRedirectCacheTtlLabel.setText(tr("Redirect Cache TTL:")); - networkRedirectCacheTtlEdit.setToolTip(tr("How long cached redirects for urls are valid for.")); - pixmapCacheLabel.setText(tr("Picture Cache Size:")); - pixmapCacheEdit.setToolTip(tr("In-memory cache for pictures not currently on screen")); - localCardImageStorageNamingSchemeLabel.setText(tr("Naming scheme:")); - - networkRedirectCacheTtlEdit.setSuffix(" " + tr("Day(s)")); -} - -MessagesSettingsPage::MessagesSettingsPage() -{ - chatMentionCheckBox.setChecked(SettingsCache::instance().getChatMention()); - connect(&chatMentionCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setChatMention); - - chatMentionCompleterCheckbox.setChecked(SettingsCache::instance().getChatMentionCompleter()); - connect(&chatMentionCompleterCheckbox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setChatMentionCompleter); - - explainMessagesLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); - explainMessagesLabel.setOpenExternalLinks(true); - - ignoreUnregUsersMainChat.setChecked(SettingsCache::instance().getIgnoreUnregisteredUsers()); - ignoreUnregUserMessages.setChecked(SettingsCache::instance().getIgnoreUnregisteredUserMessages()); - connect(&ignoreUnregUsersMainChat, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setIgnoreUnregisteredUsers); - connect(&ignoreUnregUserMessages, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setIgnoreUnregisteredUserMessages); - - invertMentionForeground.setChecked(SettingsCache::instance().getChatMentionForeground()); - connect(&invertMentionForeground, &QCheckBox::QT_STATE_CHANGED, this, &MessagesSettingsPage::updateTextColor); - - invertHighlightForeground.setChecked(SettingsCache::instance().getChatHighlightForeground()); - connect(&invertHighlightForeground, &QCheckBox::QT_STATE_CHANGED, this, - &MessagesSettingsPage::updateTextHighlightColor); - - mentionColor = new QLineEdit(); - mentionColor->setText(SettingsCache::instance().getChatMentionColor()); - updateMentionPreview(); - connect(mentionColor, &QLineEdit::textChanged, this, &MessagesSettingsPage::updateColor); - - messagePopups.setChecked(SettingsCache::instance().getShowMessagePopup()); - connect(&messagePopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setShowMessagePopups); - - mentionPopups.setChecked(SettingsCache::instance().getShowMentionPopup()); - connect(&mentionPopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setShowMentionPopups); - - roomHistory.setChecked(SettingsCache::instance().getRoomHistory()); - connect(&roomHistory, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setRoomHistory); - - customAlertString = new QLineEdit(); - customAlertString->setText(SettingsCache::instance().getHighlightWords()); - connect(customAlertString, &QLineEdit::textChanged, &SettingsCache::instance(), &SettingsCache::setHighlightWords); - - auto *chatGrid = new QGridLayout; - chatGrid->addWidget(&chatMentionCheckBox, 0, 0); - chatGrid->addWidget(&invertMentionForeground, 0, 1); - chatGrid->addWidget(mentionColor, 0, 2); - chatGrid->addWidget(&chatMentionCompleterCheckbox, 1, 0); - chatGrid->addWidget(&ignoreUnregUsersMainChat, 2, 0); - chatGrid->addWidget(&hexLabel, 1, 2); - chatGrid->addWidget(&ignoreUnregUserMessages, 3, 0); - chatGrid->addWidget(&messagePopups, 4, 0); - chatGrid->addWidget(&mentionPopups, 5, 0); - chatGrid->addWidget(&roomHistory, 6, 0); - chatGroupBox = new QGroupBox; - chatGroupBox->setLayout(chatGrid); - - highlightColor = new QLineEdit(); - highlightColor->setText(SettingsCache::instance().getChatHighlightColor()); - updateHighlightPreview(); - connect(highlightColor, &QLineEdit::textChanged, this, &MessagesSettingsPage::updateHighlightColor); - - auto *highlightNotice = new QGridLayout; - highlightNotice->addWidget(highlightColor, 0, 2); - highlightNotice->addWidget(&invertHighlightForeground, 0, 1); - highlightNotice->addWidget(&hexHighlightLabel, 1, 2); - highlightNotice->addWidget(customAlertString, 0, 0); - highlightNotice->addWidget(&customAlertStringLabel, 1, 0); - highlightGroupBox = new QGroupBox; - highlightGroupBox->setLayout(highlightNotice); - - messageList = new QListWidget; - - int count = SettingsCache::instance().messages().getCount(); - for (int i = 0; i < count; i++) { - messageList->addItem(SettingsCache::instance().messages().getMessageAt(i)); - } - - aAdd = new QAction(this); - aAdd->setIcon(QPixmap("theme:icons/increment")); - connect(aAdd, &QAction::triggered, this, &MessagesSettingsPage::actAdd); - - aEdit = new QAction(this); - aEdit->setIcon(QPixmap("theme:icons/pencil")); - connect(aEdit, &QAction::triggered, this, &MessagesSettingsPage::actEdit); - - aRemove = new QAction(this); - aRemove->setIcon(QPixmap("theme:icons/decrement")); - connect(aRemove, &QAction::triggered, this, &MessagesSettingsPage::actRemove); - - auto *messageToolBar = new QToolBar; - messageToolBar->setOrientation(Qt::Vertical); - messageToolBar->addAction(aAdd); - messageToolBar->addAction(aRemove); - messageToolBar->addAction(aEdit); - messageToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - - auto *messageListLayout = new QHBoxLayout; - messageListLayout->addWidget(messageToolBar); - messageListLayout->addWidget(messageList); - - auto *messagesLayout = new QVBoxLayout; // combines the explainer label with the actual messages widget pieces - messagesLayout->addLayout(messageListLayout); - messagesLayout->addWidget(&explainMessagesLabel); - - messageGroupBox = new QGroupBox; // draws a box around the above layout and allows it to be titled - messageGroupBox->setLayout(messagesLayout); - - auto *mainLayout = new QVBoxLayout; // combines the messages groupbox with the rest of the menu - mainLayout->addWidget(messageGroupBox); - mainLayout->addWidget(chatGroupBox); - mainLayout->addWidget(highlightGroupBox); - - setLayout(mainLayout); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &MessagesSettingsPage::retranslateUi); - retranslateUi(); -} - -void MessagesSettingsPage::updateColor(const QString &value) -{ -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - QColor colorToSet = QColor::fromString("#" + value); -#else - QColor colorToSet; - colorToSet.setNamedColor("#" + value); -#endif - if (colorToSet.isValid()) { - SettingsCache::instance().setChatMentionColor(value); - updateMentionPreview(); - } -} - -void MessagesSettingsPage::updateHighlightColor(const QString &value) -{ -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - QColor colorToSet = QColor::fromString("#" + value); -#else - QColor colorToSet; - colorToSet.setNamedColor("#" + value); -#endif - if (colorToSet.isValid()) { - SettingsCache::instance().setChatHighlightColor(value); - updateHighlightPreview(); - } -} - -void MessagesSettingsPage::updateTextColor(QT_STATE_CHANGED_T value) -{ - SettingsCache::instance().setChatMentionForeground(value); - updateMentionPreview(); -} - -void MessagesSettingsPage::updateTextHighlightColor(QT_STATE_CHANGED_T value) -{ - SettingsCache::instance().setChatHighlightForeground(value); - updateHighlightPreview(); -} - -void MessagesSettingsPage::updateMentionPreview() -{ - mentionColor->setStyleSheet( - "QLineEdit{background:#" + SettingsCache::instance().getChatMentionColor() + - ";color: " + (SettingsCache::instance().getChatMentionForeground() ? "white" : "black") + ";}"); -} - -void MessagesSettingsPage::updateHighlightPreview() -{ - highlightColor->setStyleSheet( - "QLineEdit{background:#" + SettingsCache::instance().getChatHighlightColor() + - ";color: " + (SettingsCache::instance().getChatHighlightForeground() ? "white" : "black") + ";}"); -} - -void MessagesSettingsPage::storeSettings() -{ - SettingsCache::instance().messages().setCount(messageList->count()); - for (int i = 0; i < messageList->count(); i++) { - SettingsCache::instance().messages().setMessageAt(i, messageList->item(i)->text()); - } - emit SettingsCache::instance().messages().messageMacrosChanged(); -} - -void MessagesSettingsPage::actAdd() -{ - bool ok; - QString msg = - getTextWithMax(this, tr("Add message"), tr("Message:"), QLineEdit::Normal, QString(), &ok, MAX_TEXT_LENGTH); - if (ok) { - messageList->addItem(msg); - storeSettings(); - } -} - -void MessagesSettingsPage::actEdit() -{ - if (messageList->currentItem()) { - QString oldText = messageList->currentItem()->text(); - bool ok; - QString msg = - getTextWithMax(this, tr("Edit message"), tr("Message:"), QLineEdit::Normal, oldText, &ok, MAX_TEXT_LENGTH); - if (ok) { - messageList->currentItem()->setText(msg); - storeSettings(); - } - } -} - -void MessagesSettingsPage::actRemove() -{ - if (messageList->currentItem() != nullptr) { - delete messageList->takeItem(messageList->currentRow()); - storeSettings(); - } -} - -void MessagesSettingsPage::retranslateUi() -{ - chatGroupBox->setTitle(tr("Chat settings")); - highlightGroupBox->setTitle(tr("Custom alert words")); - chatMentionCheckBox.setText(tr("Enable chat mentions")); - chatMentionCompleterCheckbox.setText(tr("Enable mention completer")); - messageGroupBox->setTitle(tr("In-game message macros")); - explainMessagesLabel.setText( - QString("%2").arg(WIKI_CUSTOM_SHORTCUTS).arg(tr("How to use in-game message macros"))); - ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users")); - ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users")); - invertMentionForeground.setText(tr("Invert text color")); - invertHighlightForeground.setText(tr("Invert text color")); - messagePopups.setText(tr("Enable desktop notifications for private messages")); - mentionPopups.setText(tr("Enable desktop notification for mentions")); - roomHistory.setText(tr("Enable room message history on join")); - hexLabel.setText(tr("(Color is hexadecimal)")); - hexHighlightLabel.setText(tr("(Color is hexadecimal)")); - customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only")); - customAlertString->setPlaceholderText(tr("Word1 Word2 Word3")); - aAdd->setText(tr("Add New Message")); - aEdit->setText(tr("Edit Message")); - aRemove->setText(tr("Remove Message")); -} - -SoundSettingsPage::SoundSettingsPage() -{ - soundEnabledCheckBox.setChecked(SettingsCache::instance().getSoundEnabled()); - connect(&soundEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setSoundEnabled); - - QString themeName = SettingsCache::instance().getSoundThemeName(); - - QStringList themeDirs = soundEngine->getAvailableThemes().keys(); - for (int i = 0; i < themeDirs.size(); i++) { - themeBox.addItem(themeDirs[i]); - if (themeDirs[i] == themeName) { - themeBox.setCurrentIndex(i); - } - } - - connect(&themeBox, qOverload(&QComboBox::currentIndexChanged), this, &SoundSettingsPage::themeBoxChanged); - connect(&soundTestButton, &QPushButton::clicked, soundEngine, &SoundEngine::testSound); - - masterVolumeSlider = new QSlider(Qt::Horizontal); - masterVolumeSlider->setMinimum(0); - masterVolumeSlider->setMaximum(100); - masterVolumeSlider->setValue(SettingsCache::instance().getMasterVolume()); - masterVolumeSlider->setToolTip(QString::number(SettingsCache::instance().getMasterVolume())); - connect(&SettingsCache::instance(), &SettingsCache::masterVolumeChanged, this, - &SoundSettingsPage::masterVolumeChanged); - connect(masterVolumeSlider, &QSlider::sliderReleased, soundEngine, &SoundEngine::testSound); - connect(masterVolumeSlider, &QSlider::valueChanged, &SettingsCache::instance(), &SettingsCache::setMasterVolume); - - masterVolumeSpinBox = new QSpinBox(); - masterVolumeSpinBox->setMinimum(0); - masterVolumeSpinBox->setMaximum(100); - masterVolumeSpinBox->setValue(SettingsCache::instance().getMasterVolume()); - connect(masterVolumeSlider, &QSlider::valueChanged, masterVolumeSpinBox, &QSpinBox::setValue); - connect(masterVolumeSpinBox, qOverload(&QSpinBox::valueChanged), masterVolumeSlider, &QSlider::setValue); - - auto *soundGrid = new QGridLayout; - soundGrid->addWidget(&soundEnabledCheckBox, 0, 0, 1, 3); - soundGrid->addWidget(&masterVolumeLabel, 1, 0); - soundGrid->addWidget(masterVolumeSlider, 1, 1); - soundGrid->addWidget(masterVolumeSpinBox, 1, 2); - soundGrid->addWidget(&themeLabel, 2, 0); - soundGrid->addWidget(&themeBox, 2, 1); - soundGrid->addWidget(&soundTestButton, 3, 1); - - soundGroupBox = new QGroupBox; - soundGroupBox->setLayout(soundGrid); - - auto *mainLayout = new QVBoxLayout; - mainLayout->addWidget(soundGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &SoundSettingsPage::retranslateUi); - retranslateUi(); -} - -void SoundSettingsPage::themeBoxChanged(int index) -{ - QStringList themeDirs = soundEngine->getAvailableThemes().keys(); - if (index >= 0 && index < themeDirs.count()) { - SettingsCache::instance().setSoundThemeName(themeDirs.at(index)); - } -} - -void SoundSettingsPage::masterVolumeChanged(int value) -{ - masterVolumeSlider->setToolTip(QString::number(value)); -} - -void SoundSettingsPage::retranslateUi() -{ - soundEnabledCheckBox.setText(tr("Enable &sounds")); - themeLabel.setText(tr("Current sounds theme:")); - soundTestButton.setText(tr("Test system sound engine")); - soundGroupBox->setTitle(tr("Sound settings")); - masterVolumeLabel.setText(tr("Master volume")); -} - -ShortcutSettingsPage::ShortcutSettingsPage() -{ - // search bar - searchEdit = new SearchLineEdit; - searchEdit->setObjectName("searchEdit"); - searchEdit->setClearButtonEnabled(true); - - setFocusProxy(searchEdit); - setFocusPolicy(Qt::ClickFocus); - - // table - shortcutsTable = new ShortcutTreeView(this); - - shortcutsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - shortcutsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - shortcutsTable->setColumnWidth(0, width() / 3 * 2); - searchEdit->setTreeView(shortcutsTable); - - connect(searchEdit, &SearchLineEdit::textChanged, shortcutsTable, &ShortcutTreeView::updateSearchString); - - // edit widget - currentActionGroupLabel = new QLabel(this); - currentActionGroupName = new QLabel(this); - currentActionLabel = new QLabel(this); - currentActionName = new QLabel(this); - currentShortcutLabel = new QLabel(this); - editTextBox = new SequenceEdit("", this); - shortcutsTable->installEventFilter(editTextBox); - - // buttons - faqLabel = new QLabel(this); - faqLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse); - faqLabel->setOpenExternalLinks(true); - - btnResetAll = new QPushButton(this); - btnClearAll = new QPushButton(this); - - btnResetAll->setIcon(QPixmap("theme:icons/update")); - btnClearAll->setIcon(QPixmap("theme:icons/clearsearch")); - - // layout - auto *_editLayout = new QGridLayout; - _editLayout->addWidget(currentActionGroupLabel, 0, 0); - _editLayout->addWidget(currentActionGroupName, 0, 1); - _editLayout->addWidget(currentActionLabel, 1, 0); - _editLayout->addWidget(currentActionName, 1, 1); - _editLayout->addWidget(currentShortcutLabel, 2, 0); - _editLayout->addWidget(editTextBox, 2, 1); - - editShortcutGroupBox = new QGroupBox; - editShortcutGroupBox->setLayout(_editLayout); - - auto *_buttonsLayout = new QHBoxLayout; - _buttonsLayout->addWidget(faqLabel); - _buttonsLayout->addWidget(btnResetAll); - _buttonsLayout->addWidget(btnClearAll); - - auto *_mainLayout = new QVBoxLayout; - _mainLayout->addWidget(searchEdit); - _mainLayout->addWidget(shortcutsTable); - _mainLayout->addWidget(editShortcutGroupBox); - _mainLayout->addLayout(_buttonsLayout); - - setLayout(_mainLayout); - - connect(btnResetAll, &QPushButton::clicked, this, &ShortcutSettingsPage::resetShortcuts); - connect(btnClearAll, &QPushButton::clicked, this, &ShortcutSettingsPage::clearShortcuts); - - connect(shortcutsTable, &ShortcutTreeView::currentItemChanged, this, &ShortcutSettingsPage::currentItemChanged); - - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &ShortcutSettingsPage::retranslateUi); - retranslateUi(); -} - -void ShortcutSettingsPage::currentItemChanged(const QString &key) -{ - if (key.isEmpty()) { - currentActionGroupName->setText(""); - currentActionName->setText(""); - editTextBox->setShortcutName(""); - } else { - QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); - QString action = SettingsCache::instance().shortcuts().getShortcut(key).getName(); - currentActionGroupName->setText(group); - currentActionName->setText(action); - editTextBox->setShortcutName(key); - } -} - -void ShortcutSettingsPage::resetShortcuts() -{ - if (QMessageBox::question(this, tr("Restore all default shortcuts"), - tr("Do you really want to restore all default shortcuts?")) == QMessageBox::Yes) { - SettingsCache::instance().shortcuts().resetAllShortcuts(); - } -} - -void ShortcutSettingsPage::clearShortcuts() -{ - if (QMessageBox::question(this, tr("Clear all default shortcuts"), - tr("Do you really want to clear all shortcuts?")) == QMessageBox::Yes) { - SettingsCache::instance().shortcuts().clearAllShortcuts(); - } -} - -void ShortcutSettingsPage::retranslateUi() -{ - shortcutsTable->retranslateUi(); - - currentActionGroupLabel->setText(tr("Section:")); - currentActionLabel->setText(tr("Action:")); - currentShortcutLabel->setText(tr("Shortcut:")); - editTextBox->retranslateUi(); - faqLabel->setText(QString("%2").arg(WIKI_CUSTOM_SHORTCUTS).arg(tr("How to set custom shortcuts"))); - btnResetAll->setText(tr("Restore all default shortcuts")); - btnClearAll->setText(tr("Clear all shortcuts")); - searchEdit->setPlaceholderText(tr("Search by shortcut name")); -} +#include static QScrollArea *makeScrollable(QWidget *widget) { diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.h b/cockatrice/src/interface/widgets/dialogs/dlg_settings.h index 86266347f..f228bde92 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.h @@ -7,372 +7,15 @@ #ifndef DLG_SETTINGS_H #define DLG_SETTINGS_H -#include #include #include -#include -#include #include -#include -#include -#include inline Q_LOGGING_CATEGORY(DlgSettingsLog, "dlg_settings"); -class ShortcutTreeView; -class SearchLineEdit; -class QTreeView; -class QStandardItemModel; -class CardDatabase; -class QCloseEvent; -class QGridLayout; -class QHBoxLayout; -class QLineEdit; class QListWidget; -class QListWidgetItem; -class QRadioButton; -class QSlider; class QStackedWidget; -class QVBoxLayout; -class SequenceEdit; - -class AbstractSettingsPage : public QWidget -{ -public: - virtual void retranslateUi() = 0; -}; - -class GeneralSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -public: - GeneralSettingsPage(); - void retranslateUi() override; - -private slots: - void deckPathButtonClicked(); - void filtersPathButtonClicked(); - void replaysPathButtonClicked(); - void picsPathButtonClicked(); - void cardDatabasePathButtonClicked(); - void customCardDatabaseButtonClicked(); - void tokenDatabasePathButtonClicked(); - void resetAllPathsClicked(); - void languageBoxChanged(int index); - -private: - QStringList findQmFiles(); - QString languageName(const QString &lang); - QLineEdit *deckPathEdit; - QLineEdit *filtersPathEdit; - QLineEdit *replaysPathEdit; - QLineEdit *picsPathEdit; - QLineEdit *cardDatabasePathEdit; - QLineEdit *customCardDatabasePathEdit; - QLineEdit *tokenDatabasePathEdit; - QPushButton *resetAllPathsButton; - QLabel *allPathsResetLabel; - QGroupBox *personalGroupBox; - QGroupBox *pathsGroupBox; - QComboBox languageBox; - QCheckBox startupUpdateCheckCheckBox; - QLabel startupCardUpdateCheckBehaviorLabel; - QComboBox startupCardUpdateCheckBehaviorSelector; - QLabel cardUpdateCheckIntervalLabel; - QSpinBox cardUpdateCheckIntervalSpinBox; - QLabel lastCardUpdateCheckDateLabel; - QCheckBox updateNotificationCheckBox; - QCheckBox newVersionOracleCheckBox; - QComboBox updateReleaseChannelBox; - QLabel languageLabel; - QLabel deckPathLabel; - QLabel filtersPathLabel; - QLabel replaysPathLabel; - QLabel picsPathLabel; - QLabel cardDatabasePathLabel; - QLabel customCardDatabasePathLabel; - QLabel tokenDatabasePathLabel; - QLabel updateReleaseChannelLabel; - QLabel advertiseTranslationPageLabel; - QCheckBox showTipsOnStartup; -}; - -class AppearanceSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -private slots: - void themeBoxChanged(int index); - void openThemeLocation(); - void editPalette(); - void updateHomeTabSettingsVisibility(); - void showShortcutsChanged(QT_STATE_CHANGED_T enabled); - void overrideAllCardArtWithPersonalPreferenceToggled(QT_STATE_CHANGED_T enabled); - - void cardViewInitialRowsMaxChanged(int value); - void cardViewExpandedRowsMaxChanged(int value); - -private: - QLabel themeLabel; - QComboBox themeBox; - QPushButton openThemeButton; - QLabel schemeComboLabel; - QComboBox schemeCombo; - QPushButton editPaletteButton; - QLabel homeTabBackgroundSourceLabel; - QComboBox homeTabBackgroundSourceBox; - QLabel homeTabBackgroundShuffleFrequencyLabel; - QSpinBox homeTabBackgroundShuffleFrequencySpinBox; - QLabel homeTabDisplayCardNameLabel; - QCheckBox homeTabDisplayCardNameCheckBox; - QLabel minPlayersForMultiColumnLayoutLabel; - QLabel maxFontSizeForCardsLabel; - QCheckBox showShortcutsCheckBox; - QCheckBox showGameSelectorFilterToolbarCheckBox; - QCheckBox displayCardNamesCheckBox; - QCheckBox autoRotateSidewaysLayoutCardsCheckBox; - QCheckBox overrideAllCardArtWithPersonalPreferenceCheckBox; - QCheckBox bumpSetsWithCardsInDeckToTopCheckBox; - QCheckBox cardScalingCheckBox; - QCheckBox roundCardCornersCheckBox; - QLabel verticalCardOverlapPercentLabel; - QSpinBox verticalCardOverlapPercentBox; - QLabel cardViewInitialRowsMaxLabel; - QSpinBox cardViewInitialRowsMaxBox; - QLabel cardViewExpandedRowsMaxLabel; - QSpinBox cardViewExpandedRowsMaxBox; - QCheckBox horizontalHandCheckBox; - QCheckBox leftJustifiedHandCheckBox; - QCheckBox invertVerticalCoordinateCheckBox; - QGroupBox *themeGroupBox; - QGroupBox *menuGroupBox; - QGroupBox *cardsGroupBox; - QGroupBox *handGroupBox; - QGroupBox *tableGroupBox; - QGroupBox *cardCountersGroupBox; - QList cardCounterNames; - QSpinBox minPlayersForMultiColumnLayoutEdit; - QSpinBox maxFontSizeForCardsEdit; - -public: - AppearanceSettingsPage(); - void retranslateUi() override; -}; - -class UserInterfaceSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -private slots: - void setNotificationEnabled(QT_STATE_CHANGED_T); - -private: - QCheckBox notificationsEnabledCheckBox; - QCheckBox specNotificationsEnabledCheckBox; - QCheckBox buddyConnectNotificationsEnabledCheckBox; - QCheckBox doubleClickToPlayCheckBox; - QCheckBox clickPlaysAllSelectedCheckBox; - QCheckBox playToStackCheckBox; - QCheckBox doNotDeleteArrowsInSubPhasesCheckBox; - QCheckBox closeEmptyCardViewCheckBox; - QCheckBox focusCardViewSearchBarCheckBox; - QCheckBox annotateTokensCheckBox; - QCheckBox showDragSelectionCountCheckBox; - QCheckBox showTotalSelectionCountCheckBox; - QCheckBox useTearOffMenusCheckBox; - QCheckBox tapAnimationCheckBox; - QCheckBox openDeckInNewTabCheckBox; - QLabel visualDeckStoragePromptForConversionLabel; - QComboBox visualDeckStoragePromptForConversionSelector; - QCheckBox visualDeckStorageInGameCheckBox; - QCheckBox visualDeckStorageSelectionAnimationCheckBox; - QLabel defaultDeckEditorTypeLabel; - QComboBox defaultDeckEditorTypeSelector; - QLabel rewindBufferingMsLabel; - QSpinBox rewindBufferingMsBox; - QGroupBox *generalGroupBox; - QGroupBox *notificationsGroupBox; - QGroupBox *animationGroupBox; - QGroupBox *deckEditorGroupBox; - QGroupBox *replayGroupBox; - -public: - UserInterfaceSettingsPage(); - void retranslateUi() override; -}; - -class DeckEditorSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -public: - DeckEditorSettingsPage(); - void retranslateUi() override; - QString getLastUpdateTime(); - -private slots: - void storeSettings(); - void urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int); - void setSpoilersEnabled(bool); - void spoilerPathButtonClicked(); - void updateSpoilers(); - void unlockSettings(); - void actAddURL(); - void actRemoveURL(); - void actEditURL(); - void resetDownloadedURLsButtonClicked(); - -private: - QPushButton resetDownloadURLs; - QLabel urlLinkLabel; - QCheckBox picDownloadCheckBox; - QListWidget *urlList; - QAction *aAdd, *aEdit, *aRemove; - QCheckBox mcDownloadSpoilersCheckBox; - QLabel msDownloadSpoilersLabel; - QGroupBox *mpGeneralGroupBox; - QGroupBox *mpSpoilerGroupBox; - - QLineEdit *mpSpoilerSavePathLineEdit; - QLabel mcSpoilerSaveLabel; - QLabel lastUpdatedLabel; - QLabel infoOnSpoilersLabel; - QPushButton *mpSpoilerPathButton; - QPushButton *updateNowButton; -}; - -class StorageSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -public: - StorageSettingsPage(); - void retranslateUi() override; - -private slots: - void clearDownloadedPicsButtonClicked(); - void clearImageBackupsButtonClicked(); - void clearPixmapCacheButtonClicked(); - -private: - QPushButton clearDownloadedPicsButton; - QPushButton clearBackupsButton; - QPushButton clearPixmapCacheButton; - - QGroupBox *mpCacheMethodGroupBox; - QGroupBox *mpNetworkCacheGroupBox; - QGroupBox *mpImageBackupGroupBox; - QGroupBox *mpPixmapCacheGroupBox; - - QLabel networkCacheExplainerLabel; - QLabel imageBackupExplainerLabel; - QLabel pixmapCacheExplainerLabel; - - QLabel cardPictureLoaderCacheMethodLabel; - QComboBox *cardPictureLoaderCacheMethodComboBox; - QLabel networkCacheLabel; - QSpinBox networkCacheEdit; - QLabel networkRedirectCacheTtlLabel; - QSpinBox networkRedirectCacheTtlEdit; - QSpinBox pixmapCacheEdit; - QLabel pixmapCacheLabel; - QLabel localCardImageStorageNamingSchemeLabel; - QComboBox *localCardImageStorageNamingSchemeComboBox; -}; - -class MessagesSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -public: - MessagesSettingsPage(); - void retranslateUi() override; - -private slots: - void actAdd(); - void actEdit(); - void actRemove(); - void updateColor(const QString &value); - void updateHighlightColor(const QString &value); - void updateTextColor(QT_STATE_CHANGED_T value); - void updateTextHighlightColor(QT_STATE_CHANGED_T value); - -private: - QListWidget *messageList; - QAction *aAdd; - QAction *aEdit; - QAction *aRemove; - QCheckBox chatMentionCheckBox; - QCheckBox chatMentionCompleterCheckbox; - QCheckBox invertMentionForeground; - QCheckBox invertHighlightForeground; - QCheckBox ignoreUnregUsersMainChat; - QCheckBox ignoreUnregUserMessages; - QCheckBox messagePopups; - QCheckBox mentionPopups; - QCheckBox roomHistory; - QGroupBox *chatGroupBox; - QGroupBox *highlightGroupBox; - QGroupBox *messageGroupBox; - QLineEdit *mentionColor; - QLineEdit *highlightColor; - QLineEdit *customAlertString; - QLabel hexLabel; - QLabel hexHighlightLabel; - QLabel customAlertStringLabel; - QLabel explainMessagesLabel; - - void storeSettings(); - void updateMentionPreview(); - void updateHighlightPreview(); -}; - -class SoundSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -public: - SoundSettingsPage(); - void retranslateUi() override; - -private: - QLabel themeLabel; - QComboBox themeBox; - QGroupBox *soundGroupBox; - QPushButton soundTestButton; - QCheckBox soundEnabledCheckBox; - QLabel masterVolumeLabel; - QSlider *masterVolumeSlider; - QSpinBox *masterVolumeSpinBox; - -private slots: - void masterVolumeChanged(int value); - void themeBoxChanged(int index); -}; - -class ShortcutSettingsPage : public AbstractSettingsPage -{ - Q_OBJECT -public: - ShortcutSettingsPage(); - void retranslateUi() override; - -private: - SearchLineEdit *searchEdit; - ShortcutTreeView *shortcutsTable; - QVBoxLayout *mainLayout; - QHBoxLayout *buttonsLayout; - QGroupBox *editShortcutGroupBox; - QGridLayout *editLayout; - QLabel *currentActionGroupLabel; - QLabel *currentActionGroupName; - QLabel *currentActionLabel; - QLabel *currentActionName; - QLabel *currentShortcutLabel; - SequenceEdit *editTextBox; - QLabel *faqLabel; - QPushButton *btnResetAll; - QPushButton *btnClearAll; - -private slots: - void resetShortcuts(); - void clearShortcuts(); - void currentItemChanged(const QString &key); -}; +class QListWidgetItem; class DlgSettings : public QDialog { diff --git a/cockatrice/src/interface/widgets/settings_page/abstract_settings_page.h b/cockatrice/src/interface/widgets/settings_page/abstract_settings_page.h new file mode 100644 index 000000000..4cbf2d71a --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/abstract_settings_page.h @@ -0,0 +1,16 @@ +#ifndef COCKATRICE_ABSTRACT_SETTINGS_PAGE_H +#define COCKATRICE_ABSTRACT_SETTINGS_PAGE_H + +#include + +#define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs" +#define WIKI_CUSTOM_SHORTCUTS "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts" +#define WIKI_TRANSLATION_FAQ "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ" + +class AbstractSettingsPage : public QWidget +{ +public: + virtual void retranslateUi() = 0; +}; + +#endif // COCKATRICE_ABSTRACT_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp new file mode 100644 index 000000000..f6740f472 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp @@ -0,0 +1,415 @@ +#include "appearance_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../../client/settings/card_counter_settings.h" +#include "../../palette_editor/palette_editor_dialog.h" +#include "../dialogs/override_printing_warning.h" +#include "../interface/theme_manager.h" +#include "../interface/widgets/general/background_sources.h" + +#include +#include +#include +#include +#include + +AppearanceSettingsPage::AppearanceSettingsPage() +{ + SettingsCache &settings = SettingsCache::instance(); + + // Theme settings + QString themeName = SettingsCache::instance().getThemeName(); + + QStringList themeDirs = themeManager->getAvailableThemes().keys(); + for (int i = 0; i < themeDirs.size(); i++) { + themeBox.addItem(themeDirs[i]); + if (themeDirs[i] == themeName) { + themeBox.setCurrentIndex(i); + } + } + + connect(&themeBox, qOverload(&QComboBox::currentIndexChanged), this, &AppearanceSettingsPage::themeBoxChanged); + connect(&openThemeButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openThemeLocation); + + schemeCombo.addItem(tr("Light"), QStringLiteral("Light")); + schemeCombo.addItem(tr("Dark"), QStringLiteral("Dark")); +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + schemeCombo.addItem(tr("System"), QStringLiteral("System")); +#endif + + // Seed from whatever the current theme already has saved + const QString dirPath = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName()); + const ThemeConfig cfg = ThemeConfig::fromThemeDir(dirPath); + const QString current = cfg.colorScheme; + const int seedIdx = schemeCombo.findData(current); + schemeCombo.setCurrentIndex(seedIdx >= 0 ? seedIdx : 0); + + connect(&schemeCombo, &QComboBox::currentIndexChanged, this, + [this] { themeManager->setColorScheme(schemeCombo.currentData().toString()); }); + + connect(themeManager, &ThemeManager::themeChanged, this, [this, dirPath] { + const QString newDir = themeManager->getAvailableThemes().value(SettingsCache::instance().getThemeName()); + const ThemeConfig cfg = ThemeConfig::fromThemeDir(newDir); + const QString current = cfg.colorScheme; + + schemeCombo.blockSignals(true); + const int idx = schemeCombo.findData(current); + schemeCombo.setCurrentIndex(idx >= 0 ? idx : 0); + schemeCombo.blockSignals(false); + }); + + connect(&editPaletteButton, &QPushButton::clicked, this, &AppearanceSettingsPage::editPalette); + + for (const auto &entry : BackgroundSources::all()) { + homeTabBackgroundSourceBox.addItem(QObject::tr(entry.trKey), QVariant::fromValue(entry.type)); + } + + QString homeTabBackgroundSource = SettingsCache::instance().getHomeTabBackgroundSource(); + int homeTabBackgroundSourceId = + homeTabBackgroundSourceBox.findData(BackgroundSources::fromId(homeTabBackgroundSource)); + if (homeTabBackgroundSourceId != -1) { + homeTabBackgroundSourceBox.setCurrentIndex(homeTabBackgroundSourceId); + } + + connect(&homeTabBackgroundSourceBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { + auto type = homeTabBackgroundSourceBox.currentData().value(); + SettingsCache::instance().setHomeTabBackgroundSource(BackgroundSources::toId(type)); + updateHomeTabSettingsVisibility(); + }); + + homeTabBackgroundShuffleFrequencySpinBox.setRange(0, 3600); + homeTabBackgroundShuffleFrequencySpinBox.setSuffix(tr(" seconds")); + homeTabBackgroundShuffleFrequencySpinBox.setValue(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency()); + connect(&homeTabBackgroundShuffleFrequencySpinBox, qOverload(&QSpinBox::valueChanged), + &SettingsCache::instance(), &SettingsCache::setHomeTabBackgroundShuffleFrequency); + + homeTabDisplayCardNameCheckBox.setChecked(settings.getHomeTabDisplayCardName()); + connect(&homeTabDisplayCardNameCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setHomeTabDisplayCardName); + + updateHomeTabSettingsVisibility(); + + auto *themeGrid = new QGridLayout; + themeGrid->addWidget(&themeLabel, 0, 0); + themeGrid->addWidget(&themeBox, 0, 1); + themeGrid->addWidget(&openThemeButton, 1, 1); + themeGrid->addWidget(&schemeComboLabel, 2, 0); + themeGrid->addWidget(&schemeCombo, 2, 1); + themeGrid->addWidget(&editPaletteButton, 3, 1); + themeGrid->addWidget(&homeTabBackgroundSourceLabel, 4, 0); + themeGrid->addWidget(&homeTabBackgroundSourceBox, 4, 1); + themeGrid->addWidget(&homeTabBackgroundShuffleFrequencyLabel, 5, 0); + themeGrid->addWidget(&homeTabBackgroundShuffleFrequencySpinBox, 5, 1); + themeGrid->addWidget(&homeTabDisplayCardNameLabel, 6, 0); + themeGrid->addWidget(&homeTabDisplayCardNameCheckBox, 6, 1); + + themeGroupBox = new QGroupBox; + themeGroupBox->setLayout(themeGrid); + + // Menu settings + showShortcutsCheckBox.setChecked(settings.getShowShortcuts()); + connect(&showShortcutsCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &AppearanceSettingsPage::showShortcutsChanged); + + showGameSelectorFilterToolbarCheckBox.setChecked(settings.getShowGameSelectorFilterToolbar()); + connect(&showGameSelectorFilterToolbarCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setShowGameSelectorFilterToolbar); + + auto *menuGrid = new QGridLayout; + menuGrid->addWidget(&showShortcutsCheckBox, 0, 0); + menuGrid->addWidget(&showGameSelectorFilterToolbarCheckBox, 1, 0); + + menuGroupBox = new QGroupBox; + menuGroupBox->setLayout(menuGrid); + + // Card rendering + displayCardNamesCheckBox.setChecked(settings.getDisplayCardNames()); + connect(&displayCardNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setDisplayCardNames); + + autoRotateSidewaysLayoutCardsCheckBox.setChecked(settings.getAutoRotateSidewaysLayoutCards()); + connect(&autoRotateSidewaysLayoutCardsCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setAutoRotateSidewaysLayoutCards); + + overrideAllCardArtWithPersonalPreferenceCheckBox.setChecked(settings.getOverrideAllCardArtWithPersonalPreference()); + connect(&overrideAllCardArtWithPersonalPreferenceCheckBox, &QCheckBox::QT_STATE_CHANGED, this, + &AppearanceSettingsPage::overrideAllCardArtWithPersonalPreferenceToggled); + + bumpSetsWithCardsInDeckToTopCheckBox.setChecked(settings.getBumpSetsWithCardsInDeckToTop()); + connect(&bumpSetsWithCardsInDeckToTopCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setBumpSetsWithCardsInDeckToTop); + + cardScalingCheckBox.setChecked(settings.getScaleCards()); + connect(&cardScalingCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setCardScaling); + + roundCardCornersCheckBox.setChecked(settings.getRoundCardCorners()); + connect(&roundCardCornersCheckBox, &QAbstractButton::toggled, &settings, &SettingsCache::setRoundCardCorners); + + verticalCardOverlapPercentBox.setValue(settings.getStackCardOverlapPercent()); + verticalCardOverlapPercentBox.setRange(0, 80); + connect(&verticalCardOverlapPercentBox, qOverload(&QSpinBox::valueChanged), &settings, + &SettingsCache::setStackCardOverlapPercent); + + cardViewInitialRowsMaxBox.setRange(1, 999); + cardViewInitialRowsMaxBox.setValue(SettingsCache::instance().getCardViewInitialRowsMax()); + connect(&cardViewInitialRowsMaxBox, qOverload(&QSpinBox::valueChanged), this, + &AppearanceSettingsPage::cardViewInitialRowsMaxChanged); + + cardViewExpandedRowsMaxBox.setRange(1, 999); + cardViewExpandedRowsMaxBox.setValue(SettingsCache::instance().getCardViewExpandedRowsMax()); + connect(&cardViewExpandedRowsMaxBox, qOverload(&QSpinBox::valueChanged), this, + &AppearanceSettingsPage::cardViewExpandedRowsMaxChanged); + + auto *cardsGrid = new QGridLayout; + cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2); + cardsGrid->addWidget(&autoRotateSidewaysLayoutCardsCheckBox, 1, 0, 1, 2); + cardsGrid->addWidget(&cardScalingCheckBox, 2, 0, 1, 2); + cardsGrid->addWidget(&roundCardCornersCheckBox, 3, 0, 1, 2); + cardsGrid->addWidget(&overrideAllCardArtWithPersonalPreferenceCheckBox, 4, 0, 1, 2); + cardsGrid->addWidget(&bumpSetsWithCardsInDeckToTopCheckBox, 5, 0, 1, 2); + cardsGrid->addWidget(&verticalCardOverlapPercentLabel, 6, 0, 1, 1); + cardsGrid->addWidget(&verticalCardOverlapPercentBox, 6, 1, 1, 1); + cardsGrid->addWidget(&cardViewInitialRowsMaxLabel, 7, 0); + cardsGrid->addWidget(&cardViewInitialRowsMaxBox, 7, 1); + cardsGrid->addWidget(&cardViewExpandedRowsMaxLabel, 8, 0); + cardsGrid->addWidget(&cardViewExpandedRowsMaxBox, 8, 1); + + cardsGroupBox = new QGroupBox; + cardsGroupBox->setLayout(cardsGrid); + + // Card counter colors + + auto *cardCounterColorsLayout = new QGridLayout; + cardCounterColorsLayout->setColumnStretch(1, 1); + cardCounterColorsLayout->setColumnStretch(3, 1); + cardCounterColorsLayout->setColumnStretch(5, 1); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + for (int index = 0; index < 6; ++index) { + auto *pushButton = new QPushButton; + pushButton->setStyleSheet(QString("background-color: %1").arg(cardCounterSettings.color(index).name())); + + connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, pushButton, + [index, pushButton](int changedIndex, const QColor &color) { + if (index == changedIndex) { + pushButton->setStyleSheet(QString("background-color: %1").arg(color.name())); + } + }); + + connect(pushButton, &QPushButton::clicked, this, [index, this]() { + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + + auto newColor = QColorDialog::getColor(cardCounterSettings.color(index), this); + if (!newColor.isValid()) { + return; + } + + cardCounterSettings.setColor(index, newColor); + }); + + auto *colorName = new QLabel; + cardCounterNames.append(colorName); + + int row = index / 3; + int column = 2 * (index % 3); + + cardCounterColorsLayout->addWidget(pushButton, row, column); + cardCounterColorsLayout->addWidget(colorName, row, column + 1); + } + + auto *cardCountersLayout = new QVBoxLayout; + cardCountersLayout->addLayout(cardCounterColorsLayout, 1); + + cardCountersGroupBox = new QGroupBox; + cardCountersGroupBox->setLayout(cardCountersLayout); + + // Hand layout + horizontalHandCheckBox.setChecked(settings.getHorizontalHand()); + connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand); + + leftJustifiedHandCheckBox.setChecked(settings.getLeftJustified()); + connect(&leftJustifiedHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setLeftJustified); + + auto *handGrid = new QGridLayout; + handGrid->addWidget(&horizontalHandCheckBox, 0, 0, 1, 2); + handGrid->addWidget(&leftJustifiedHandCheckBox, 1, 0, 1, 2); + + handGroupBox = new QGroupBox; + handGroupBox->setLayout(handGrid); + + // table grid layout + invertVerticalCoordinateCheckBox.setChecked(settings.getInvertVerticalCoordinate()); + connect(&invertVerticalCoordinateCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setInvertVerticalCoordinate); + + minPlayersForMultiColumnLayoutEdit.setMinimum(2); + minPlayersForMultiColumnLayoutEdit.setValue(settings.getMinPlayersForMultiColumnLayout()); + connect(&minPlayersForMultiColumnLayoutEdit, qOverload(&QSpinBox::valueChanged), &settings, + &SettingsCache::setMinPlayersForMultiColumnLayout); + minPlayersForMultiColumnLayoutLabel.setBuddy(&minPlayersForMultiColumnLayoutEdit); + + connect(&maxFontSizeForCardsEdit, qOverload(&QSpinBox::valueChanged), &settings, + &SettingsCache::setMaxFontSize); + maxFontSizeForCardsEdit.setValue(settings.getMaxFontSize()); + maxFontSizeForCardsLabel.setBuddy(&maxFontSizeForCardsEdit); + maxFontSizeForCardsEdit.setMinimum(9); + maxFontSizeForCardsEdit.setMaximum(100); + + auto *tableGrid = new QGridLayout; + tableGrid->addWidget(&invertVerticalCoordinateCheckBox, 0, 0, 1, 2); + tableGrid->addWidget(&minPlayersForMultiColumnLayoutLabel, 1, 0, 1, 1); + tableGrid->addWidget(&minPlayersForMultiColumnLayoutEdit, 1, 1, 1, 1); + tableGrid->addWidget(&maxFontSizeForCardsLabel, 2, 0, 1, 1); + tableGrid->addWidget(&maxFontSizeForCardsEdit, 2, 1, 1, 1); + + tableGroupBox = new QGroupBox; + tableGroupBox->setLayout(tableGrid); + + // putting it all together + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(themeGroupBox); + mainLayout->addWidget(menuGroupBox); + mainLayout->addWidget(cardsGroupBox); + mainLayout->addWidget(cardCountersGroupBox); + mainLayout->addWidget(handGroupBox); + mainLayout->addWidget(tableGroupBox); + mainLayout->addStretch(); + + setLayout(mainLayout); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &AppearanceSettingsPage::retranslateUi); + retranslateUi(); +} + +void AppearanceSettingsPage::themeBoxChanged(int index) +{ + QStringList themeDirs = themeManager->getAvailableThemes().keys(); + if (index >= 0 && index < themeDirs.count()) { + SettingsCache::instance().setThemeName(themeDirs.at(index)); + } +} + +void AppearanceSettingsPage::openThemeLocation() +{ + QString dir = SettingsCache::instance().getThemesPath(); + QDir dirDir = dir; + dirDir.cdUp(); + // open if dir exists, create if parent dir does exist + if (dirDir.exists() && dirDir.mkpath(dir)) { + QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); + } else { + QMessageBox::critical(this, tr("Error"), tr("Could not create themes directory at '%1'.").arg(dir)); + } +} + +void AppearanceSettingsPage::editPalette() +{ + PaletteEditorDialog dlg(themeManager->getCurrentThemePath(), SettingsCache::instance().getThemeName(), this); + dlg.exec(); +} + +void AppearanceSettingsPage::updateHomeTabSettingsVisibility() +{ + bool visible = + SettingsCache::instance().getHomeTabBackgroundSource() != BackgroundSources::toId(BackgroundSources::Theme); + + homeTabBackgroundShuffleFrequencyLabel.setVisible(visible); + homeTabBackgroundShuffleFrequencySpinBox.setVisible(visible); + homeTabDisplayCardNameLabel.setVisible(visible); + homeTabDisplayCardNameCheckBox.setVisible(visible); +} + +void AppearanceSettingsPage::showShortcutsChanged(QT_STATE_CHANGED_T value) +{ + SettingsCache::instance().setShowShortcuts(value); + qApp->setAttribute(Qt::AA_DontShowShortcutsInContextMenus, value == 0); // 0 = unchecked +} + +void AppearanceSettingsPage::overrideAllCardArtWithPersonalPreferenceToggled(QT_STATE_CHANGED_T value) +{ + bool enable = static_cast(value); + + bool accepted = OverridePrintingWarning::execMessageBox(this, enable); + + if (!accepted) { + // If user cancels, revert the checkbox/state back + QTimer::singleShot(0, this, [this, enable]() { + overrideAllCardArtWithPersonalPreferenceCheckBox.blockSignals(true); + overrideAllCardArtWithPersonalPreferenceCheckBox.setChecked(!enable); + overrideAllCardArtWithPersonalPreferenceCheckBox.blockSignals(false); + }); + } +} + +/** + * Updates the settings for cardViewInitialRowsMax. + * Forces expanded rows max to always be >= initial rows max + * @param value The new value + */ +void AppearanceSettingsPage::cardViewInitialRowsMaxChanged(int value) +{ + SettingsCache::instance().setCardViewInitialRowsMax(value); + if (cardViewExpandedRowsMaxBox.value() < value) { + cardViewExpandedRowsMaxBox.setValue(value); + } +} + +/** + * Updates the settings for cardViewExpandedRowsMax. + * Forces initial rows max to always be <= expanded rows max + * @param value The new value + */ +void AppearanceSettingsPage::cardViewExpandedRowsMaxChanged(int value) +{ + SettingsCache::instance().setCardViewExpandedRowsMax(value); + if (cardViewInitialRowsMaxBox.value() > value) { + cardViewInitialRowsMaxBox.setValue(value); + } +} + +void AppearanceSettingsPage::retranslateUi() +{ + themeGroupBox->setTitle(tr("Theme settings")); + themeLabel.setText(tr("Current theme:")); + openThemeButton.setText(tr("Open themes folder")); + schemeComboLabel.setText(tr("Active theme palette:")); + editPaletteButton.setText(tr("Edit theme palette")); + homeTabBackgroundSourceLabel.setText(tr("Home tab background source:")); + homeTabBackgroundShuffleFrequencyLabel.setText(tr("Home tab background shuffle frequency:")); + homeTabBackgroundShuffleFrequencySpinBox.setSpecialValueText(tr("Disabled")); + homeTabDisplayCardNameLabel.setText(tr("Display card name of background in bottom right:")); + + menuGroupBox->setTitle(tr("Menu settings")); + showShortcutsCheckBox.setText(tr("Show keyboard shortcuts in right-click menus")); + showGameSelectorFilterToolbarCheckBox.setText(tr("Show game filter toolbar above list in room tab")); + + cardsGroupBox->setTitle(tr("Card rendering")); + displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture")); + autoRotateSidewaysLayoutCardsCheckBox.setText(tr("Auto-Rotate cards with sideways layout")); + overrideAllCardArtWithPersonalPreferenceCheckBox.setText( + tr("Override all card art with personal set preference (Pre-ProviderID change behavior)")); + bumpSetsWithCardsInDeckToTopCheckBox.setText( + tr("Bump sets that the deck contains cards from to the top in the printing selector")); + cardScalingCheckBox.setText(tr("Scale cards on mouse over")); + roundCardCornersCheckBox.setText(tr("Use rounded card corners")); + verticalCardOverlapPercentLabel.setText( + tr("Minimum overlap percentage of cards on the stack and in vertical hand")); + cardViewInitialRowsMaxLabel.setText(tr("Maximum initial height for card view window:")); + cardViewInitialRowsMaxBox.setSuffix(tr(" rows")); + cardViewExpandedRowsMaxLabel.setText(tr("Maximum expanded height for card view window:")); + cardViewExpandedRowsMaxBox.setSuffix(tr(" rows")); + + cardCountersGroupBox->setTitle(tr("Card counters")); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + for (int index = 0; index < cardCounterNames.size(); ++index) { + cardCounterNames[index]->setText(tr("Counter %1").arg(cardCounterSettings.displayName(index))); + } + + handGroupBox->setTitle(tr("Hand layout")); + horizontalHandCheckBox.setText(tr("Display hand horizontally (wastes space)")); + leftJustifiedHandCheckBox.setText(tr("Enable left justification")); + + tableGroupBox->setTitle(tr("Table grid layout")); + invertVerticalCoordinateCheckBox.setText(tr("Invert vertical coordinate")); + minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:")); + maxFontSizeForCardsLabel.setText(tr("Maximum font size for information displayed on cards:")); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h new file mode 100644 index 000000000..116311986 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h @@ -0,0 +1,75 @@ +#ifndef COCKATRICE_APPEARANCE_SETTINGS_PAGE_H +#define COCKATRICE_APPEARANCE_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include +#include +#include + +class AppearanceSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +private slots: + void themeBoxChanged(int index); + void openThemeLocation(); + void editPalette(); + void updateHomeTabSettingsVisibility(); + void showShortcutsChanged(QT_STATE_CHANGED_T enabled); + void overrideAllCardArtWithPersonalPreferenceToggled(QT_STATE_CHANGED_T enabled); + + void cardViewInitialRowsMaxChanged(int value); + void cardViewExpandedRowsMaxChanged(int value); + +private: + QLabel themeLabel; + QComboBox themeBox; + QPushButton openThemeButton; + QLabel schemeComboLabel; + QComboBox schemeCombo; + QPushButton editPaletteButton; + QLabel homeTabBackgroundSourceLabel; + QComboBox homeTabBackgroundSourceBox; + QLabel homeTabBackgroundShuffleFrequencyLabel; + QSpinBox homeTabBackgroundShuffleFrequencySpinBox; + QLabel homeTabDisplayCardNameLabel; + QCheckBox homeTabDisplayCardNameCheckBox; + QLabel minPlayersForMultiColumnLayoutLabel; + QLabel maxFontSizeForCardsLabel; + QCheckBox showShortcutsCheckBox; + QCheckBox showGameSelectorFilterToolbarCheckBox; + QCheckBox displayCardNamesCheckBox; + QCheckBox autoRotateSidewaysLayoutCardsCheckBox; + QCheckBox overrideAllCardArtWithPersonalPreferenceCheckBox; + QCheckBox bumpSetsWithCardsInDeckToTopCheckBox; + QCheckBox cardScalingCheckBox; + QCheckBox roundCardCornersCheckBox; + QLabel verticalCardOverlapPercentLabel; + QSpinBox verticalCardOverlapPercentBox; + QLabel cardViewInitialRowsMaxLabel; + QSpinBox cardViewInitialRowsMaxBox; + QLabel cardViewExpandedRowsMaxLabel; + QSpinBox cardViewExpandedRowsMaxBox; + QCheckBox horizontalHandCheckBox; + QCheckBox leftJustifiedHandCheckBox; + QCheckBox invertVerticalCoordinateCheckBox; + QGroupBox *themeGroupBox; + QGroupBox *menuGroupBox; + QGroupBox *cardsGroupBox; + QGroupBox *handGroupBox; + QGroupBox *tableGroupBox; + QGroupBox *cardCountersGroupBox; + QList cardCounterNames; + QSpinBox minPlayersForMultiColumnLayoutEdit; + QSpinBox maxFontSizeForCardsEdit; + +public: + AppearanceSettingsPage(); + void retranslateUi() override; +}; + +#endif // COCKATRICE_APPEARANCE_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp new file mode 100644 index 000000000..b7d699ebc --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.cpp @@ -0,0 +1,241 @@ +#include "deck_editor_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "update/card_spoiler/spoiler_background_updater.h" + +#include +#include +#include +#include +#include +#include +#include + +DeckEditorSettingsPage::DeckEditorSettingsPage() +{ + picDownloadCheckBox.setChecked(SettingsCache::instance().getPicDownload()); + connect(&picDownloadCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPicDownload); + + urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); + urlLinkLabel.setOpenExternalLinks(true); + + connect(&resetDownloadURLs, &QPushButton::clicked, this, &DeckEditorSettingsPage::resetDownloadedURLsButtonClicked); + + auto *lpGeneralGrid = new QGridLayout; + auto *lpSpoilerGrid = new QGridLayout; + + mcDownloadSpoilersCheckBox.setChecked(SettingsCache::instance().getDownloadSpoilersStatus()); + + mpSpoilerSavePathLineEdit = new QLineEdit(SettingsCache::instance().getSpoilerCardDatabasePath()); + mpSpoilerSavePathLineEdit->setReadOnly(true); + mpSpoilerPathButton = new QPushButton("..."); + connect(mpSpoilerPathButton, &QPushButton::clicked, this, &DeckEditorSettingsPage::spoilerPathButtonClicked); + + updateNowButton = new QPushButton; + updateNowButton->setFixedWidth(150); + connect(updateNowButton, &QPushButton::clicked, this, &DeckEditorSettingsPage::updateSpoilers); + + // Update the GUI depending on if the box is ticked or not + setSpoilersEnabled(mcDownloadSpoilersCheckBox.isChecked()); + + urlList = new QListWidget; + urlList->setSelectionMode(QAbstractItemView::SingleSelection); + urlList->setAlternatingRowColors(true); + urlList->setDragEnabled(true); + urlList->setDragDropMode(QAbstractItemView::InternalMove); + connect(urlList->model(), &QAbstractItemModel::rowsMoved, this, &DeckEditorSettingsPage::urlListChanged); + + urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); + + aAdd = new QAction(this); + aAdd->setIcon(QPixmap("theme:icons/increment")); + connect(aAdd, &QAction::triggered, this, &DeckEditorSettingsPage::actAddURL); + + aEdit = new QAction(this); + aEdit->setIcon(QPixmap("theme:icons/pencil")); + connect(aEdit, &QAction::triggered, this, &DeckEditorSettingsPage::actEditURL); + + aRemove = new QAction(this); + aRemove->setIcon(QPixmap("theme:icons/decrement")); + connect(aRemove, &QAction::triggered, this, &DeckEditorSettingsPage::actRemoveURL); + + auto *urlToolBar = new QToolBar; + urlToolBar->setOrientation(Qt::Vertical); + urlToolBar->addAction(aAdd); + urlToolBar->addAction(aRemove); + urlToolBar->addAction(aEdit); + urlToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + + auto *urlListLayout = new QHBoxLayout; + urlListLayout->addWidget(urlToolBar); + urlListLayout->addWidget(urlList); + + // Top Layout + lpGeneralGrid->addWidget(&picDownloadCheckBox, 0, 0); + lpGeneralGrid->addWidget(&resetDownloadURLs, 0, 1); + lpGeneralGrid->addLayout(urlListLayout, 1, 0, 1, 2); + lpGeneralGrid->addWidget(&urlLinkLabel, 4, 0); + + // Spoiler Layout + lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0); + lpSpoilerGrid->addWidget(&mcSpoilerSaveLabel, 1, 0); + lpSpoilerGrid->addWidget(mpSpoilerSavePathLineEdit, 1, 1); + lpSpoilerGrid->addWidget(mpSpoilerPathButton, 1, 2); + lpSpoilerGrid->addWidget(&lastUpdatedLabel, 2, 0); + lpSpoilerGrid->addWidget(updateNowButton, 2, 1); + lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 3, 0, 1, 3, Qt::AlignTop); + + // On a change to the checkbox, hide/un-hide the other fields + connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, &SettingsCache::instance(), + &SettingsCache::setDownloadSpoilerStatus); + connect(&mcDownloadSpoilersCheckBox, &QCheckBox::toggled, this, &DeckEditorSettingsPage::setSpoilersEnabled); + + mpGeneralGroupBox = new QGroupBox; + mpGeneralGroupBox->setLayout(lpGeneralGrid); + + mpSpoilerGroupBox = new QGroupBox; + mpSpoilerGroupBox->setLayout(lpSpoilerGrid); + + auto *lpMainLayout = new QVBoxLayout; + lpMainLayout->addWidget(mpGeneralGroupBox); + lpMainLayout->addWidget(mpSpoilerGroupBox); + + setLayout(lpMainLayout); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &DeckEditorSettingsPage::retranslateUi); + retranslateUi(); +} + +void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked() +{ + SettingsCache::instance().downloads().resetToDefaultURLs(); + urlList->clear(); + urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); + QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset.")); +} + +void DeckEditorSettingsPage::actAddURL() +{ + bool ok; + QString msg = QInputDialog::getText(this, tr("Add URL"), tr("URL:"), QLineEdit::Normal, QString(), &ok); + if (ok) { + urlList->addItem(msg); + storeSettings(); + } +} + +void DeckEditorSettingsPage::actRemoveURL() +{ + if (urlList->currentItem() != nullptr) { + delete urlList->takeItem(urlList->currentRow()); + storeSettings(); + } +} + +void DeckEditorSettingsPage::actEditURL() +{ + if (urlList->currentItem()) { + QString oldText = urlList->currentItem()->text(); + bool ok; + QString msg = QInputDialog::getText(this, tr("Edit URL"), tr("URL:"), QLineEdit::Normal, oldText, &ok); + if (ok) { + urlList->currentItem()->setText(msg); + storeSettings(); + } + } +} + +void DeckEditorSettingsPage::storeSettings() +{ + qInfo() << "URL Priority Reset"; + + QStringList downloadUrls; + for (int i = 0; i < urlList->count(); i++) { + qInfo() << "Priority" << i << ":" << urlList->item(i)->text(); + downloadUrls << urlList->item(i)->text(); + } + SettingsCache::instance().downloads().setDownloadUrls(downloadUrls); +} + +void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int) +{ + storeSettings(); +} + +void DeckEditorSettingsPage::updateSpoilers() +{ + // Disable the button so the user can only press it once at a time + updateNowButton->setDisabled(true); + updateNowButton->setText(tr("Updating...")); + + // Create a new SBU that will act as if the client was just reloaded + auto *sbu = new SpoilerBackgroundUpdater(); + connect(sbu, &SpoilerBackgroundUpdater::spoilerCheckerDone, this, &DeckEditorSettingsPage::unlockSettings); + connect(sbu, &SpoilerBackgroundUpdater::spoilersUpdatedSuccessfully, this, &DeckEditorSettingsPage::unlockSettings); +} + +void DeckEditorSettingsPage::unlockSettings() +{ + updateNowButton->setDisabled(false); + updateNowButton->setText(tr("Update Spoilers")); +} + +QString DeckEditorSettingsPage::getLastUpdateTime() +{ + QString fileName = SettingsCache::instance().getSpoilerCardDatabasePath(); + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + QFile file(fileName); + + if (file.exists()) { + return fi.lastModified().toString("MMM d, hh:mm"); + } + + return QString(); +} + +void DeckEditorSettingsPage::spoilerPathButtonClicked() +{ + QString lsPath = QFileDialog::getExistingDirectory(this, tr("Choose path"), mpSpoilerSavePathLineEdit->text()); + if (lsPath.isEmpty()) { + return; + } + + mpSpoilerSavePathLineEdit->setText(lsPath + "/spoiler.xml"); + SettingsCache::instance().setSpoilerDatabasePath(lsPath + "/spoiler.xml"); +} + +void DeckEditorSettingsPage::setSpoilersEnabled(bool anInput) +{ + msDownloadSpoilersLabel.setEnabled(anInput); + mcSpoilerSaveLabel.setEnabled(anInput); + mpSpoilerSavePathLineEdit->setEnabled(anInput); + mpSpoilerPathButton->setEnabled(anInput); + lastUpdatedLabel.setEnabled(anInput); + updateNowButton->setEnabled(anInput); + infoOnSpoilersLabel.setEnabled(anInput); + + if (!anInput) { + SpoilerBackgroundUpdater::deleteSpoilerFile(); + } +} + +void DeckEditorSettingsPage::retranslateUi() +{ + mpGeneralGroupBox->setTitle(tr("URL Download Priority")); + mpSpoilerGroupBox->setTitle(tr("Spoilers")); + mcDownloadSpoilersCheckBox.setText(tr("Download Spoilers Automatically")); + mcSpoilerSaveLabel.setText(tr("Spoiler Location:")); + lastUpdatedLabel.setText(tr("Last Change") + ": " + getLastUpdateTime()); + infoOnSpoilersLabel.setText(tr("Spoilers download automatically on launch") + "\n" + + tr("Press the button to manually update without relaunching") + "\n\n" + + tr("Do not close settings until manual update is complete")); + picDownloadCheckBox.setText(tr("Download card pictures on the fly")); + urlLinkLabel.setText(QString("%2").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL"))); + resetDownloadURLs.setText(tr("Reset Download URLs")); + updateNowButton->setText(tr("Update Spoilers")); + aAdd->setText(tr("Add New URL")); + aEdit->setText(tr("Edit URL")); + aRemove->setText(tr("Remove URL")); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.h b/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.h new file mode 100644 index 000000000..5db009c8a --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/deck_editor_settings_page.h @@ -0,0 +1,51 @@ +#ifndef COCKATRICE_DECK_EDITOR_SETTINGS_PAGE_H +#define COCKATRICE_DECK_EDITOR_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include + +class DeckEditorSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +public: + DeckEditorSettingsPage(); + void retranslateUi() override; + QString getLastUpdateTime(); + +private slots: + void storeSettings(); + void urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int); + void setSpoilersEnabled(bool); + void spoilerPathButtonClicked(); + void updateSpoilers(); + void unlockSettings(); + void actAddURL(); + void actRemoveURL(); + void actEditURL(); + void resetDownloadedURLsButtonClicked(); + +private: + QPushButton resetDownloadURLs; + QLabel urlLinkLabel; + QCheckBox picDownloadCheckBox; + QListWidget *urlList; + QAction *aAdd, *aEdit, *aRemove; + QCheckBox mcDownloadSpoilersCheckBox; + QLabel msDownloadSpoilersLabel; + QGroupBox *mpGeneralGroupBox; + QGroupBox *mpSpoilerGroupBox; + + QLineEdit *mpSpoilerSavePathLineEdit; + QLabel mcSpoilerSaveLabel; + QLabel lastUpdatedLabel; + QLabel infoOnSpoilersLabel; + QPushButton *mpSpoilerPathButton; + QPushButton *updateNowButton; +}; + +#endif // COCKATRICE_DECK_EDITOR_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp new file mode 100644 index 000000000..a4d34ffb5 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp @@ -0,0 +1,373 @@ +#include "general_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../main.h" +#include "update/client/release_channel.h" + +#include +#include +#include +#include +#include + +enum startupCardUpdateCheckBehaviorIndex +{ + startupCardUpdateCheckBehaviorIndexNone, + startupCardUpdateCheckBehaviorIndexPrompt, + startupCardUpdateCheckBehaviorIndexAlways +}; + +GeneralSettingsPage::GeneralSettingsPage() +{ + QStringList languageCodes = findQmFiles(); + for (const QString &code : languageCodes) { + QString langName = languageName(code); + languageBox.addItem(langName, code); + } + + QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); + int index = languageBox.findText(setLanguage, Qt::MatchExactly); + if (index == -1) { + qWarning() << "could not find language" << setLanguage; + } else { + languageBox.setCurrentIndex(index); + } + + // updates + SettingsCache &settings = SettingsCache::instance(); + startupUpdateCheckCheckBox.setChecked(settings.getCheckUpdatesOnStartup()); + + startupCardUpdateCheckBehaviorSelector.addItem(""); // these will be set in retranslateUI + startupCardUpdateCheckBehaviorSelector.addItem(""); + startupCardUpdateCheckBehaviorSelector.addItem(""); + if (SettingsCache::instance().getStartupCardUpdateCheckPromptForUpdate()) { + startupCardUpdateCheckBehaviorSelector.setCurrentIndex(startupCardUpdateCheckBehaviorIndexPrompt); + } else if (SettingsCache::instance().getStartupCardUpdateCheckAlwaysUpdate()) { + startupCardUpdateCheckBehaviorSelector.setCurrentIndex(startupCardUpdateCheckBehaviorIndexAlways); + } else { + startupCardUpdateCheckBehaviorSelector.setCurrentIndex(startupCardUpdateCheckBehaviorIndexNone); + } + + cardUpdateCheckIntervalSpinBox.setMinimum(1); + cardUpdateCheckIntervalSpinBox.setMaximum(30); + cardUpdateCheckIntervalSpinBox.setValue(settings.getCardUpdateCheckInterval()); + updateNotificationCheckBox.setChecked(settings.getNotifyAboutUpdates()); + newVersionOracleCheckBox.setChecked(settings.getNotifyAboutNewVersion()); + + showTipsOnStartup.setChecked(settings.getShowTipsOnStartup()); + + advertiseTranslationPageLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); + advertiseTranslationPageLabel.setOpenExternalLinks(true); + + connect(&languageBox, qOverload(&QComboBox::currentIndexChanged), this, + &GeneralSettingsPage::languageBoxChanged); + connect(&startupUpdateCheckCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setCheckUpdatesOnStartup); + connect(&startupCardUpdateCheckBehaviorSelector, QOverload::of(&QComboBox::currentIndexChanged), this, + [](int index) { + SettingsCache::instance().setStartupCardUpdateCheckPromptForUpdate( + index == startupCardUpdateCheckBehaviorIndexPrompt); + SettingsCache::instance().setStartupCardUpdateCheckAlwaysUpdate( + index == startupCardUpdateCheckBehaviorIndexAlways); + }); + connect(&cardUpdateCheckIntervalSpinBox, qOverload(&QSpinBox::valueChanged), &settings, + &SettingsCache::setCardUpdateCheckInterval); + connect(&updateNotificationCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setNotifyAboutUpdate); + connect(&newVersionOracleCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setNotifyAboutNewVersion); + connect(&showTipsOnStartup, &QCheckBox::clicked, &settings, &SettingsCache::setShowTipsOnStartup); + + auto *personalGrid = new QGridLayout; + personalGrid->addWidget(&languageLabel, 0, 0); + personalGrid->addWidget(&languageBox, 0, 1); + personalGrid->addWidget(&advertiseTranslationPageLabel, 1, 1, Qt::AlignRight); + personalGrid->addWidget(&updateReleaseChannelLabel, 2, 0); + personalGrid->addWidget(&updateReleaseChannelBox, 2, 1); + personalGrid->addWidget(&startupUpdateCheckCheckBox, 4, 0, 1, 2); + personalGrid->addWidget(&startupCardUpdateCheckBehaviorLabel, 5, 0); + personalGrid->addWidget(&startupCardUpdateCheckBehaviorSelector, 5, 1); + personalGrid->addWidget(&cardUpdateCheckIntervalLabel, 6, 0); + personalGrid->addWidget(&cardUpdateCheckIntervalSpinBox, 6, 1); + personalGrid->addWidget(&lastCardUpdateCheckDateLabel, 7, 1); + personalGrid->addWidget(&updateNotificationCheckBox, 8, 0, 1, 2); + personalGrid->addWidget(&newVersionOracleCheckBox, 9, 0, 1, 2); + personalGrid->addWidget(&showTipsOnStartup, 10, 0, 1, 2); + + personalGroupBox = new QGroupBox; + personalGroupBox->setLayout(personalGrid); + + deckPathEdit = new QLineEdit(settings.getDeckPath()); + deckPathEdit->setReadOnly(true); + auto *deckPathButton = new QPushButton("..."); + connect(deckPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::deckPathButtonClicked); + + filtersPathEdit = new QLineEdit(settings.getFiltersPath()); + filtersPathEdit->setReadOnly(true); + auto *filtersPathButton = new QPushButton("..."); + connect(filtersPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::filtersPathButtonClicked); + + replaysPathEdit = new QLineEdit(settings.getReplaysPath()); + replaysPathEdit->setReadOnly(true); + auto *replaysPathButton = new QPushButton("..."); + connect(replaysPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::replaysPathButtonClicked); + + picsPathEdit = new QLineEdit(settings.getPicsPath()); + picsPathEdit->setReadOnly(true); + auto *picsPathButton = new QPushButton("..."); + connect(picsPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::picsPathButtonClicked); + + cardDatabasePathEdit = new QLineEdit(settings.getCardDatabasePath()); + cardDatabasePathEdit->setReadOnly(true); + auto *cardDatabasePathButton = new QPushButton("..."); + connect(cardDatabasePathButton, &QPushButton::clicked, this, &GeneralSettingsPage::cardDatabasePathButtonClicked); + + customCardDatabasePathEdit = new QLineEdit(settings.getCustomCardDatabasePath()); + customCardDatabasePathEdit->setReadOnly(true); + auto *customCardDatabasePathButton = new QPushButton("..."); + connect(customCardDatabasePathButton, &QPushButton::clicked, this, + &GeneralSettingsPage::customCardDatabaseButtonClicked); + + tokenDatabasePathEdit = new QLineEdit(settings.getTokenDatabasePath()); + tokenDatabasePathEdit->setReadOnly(true); + auto *tokenDatabasePathButton = new QPushButton("..."); + connect(tokenDatabasePathButton, &QPushButton::clicked, this, &GeneralSettingsPage::tokenDatabasePathButtonClicked); + + // Required init here to avoid crashing on Portable builds + resetAllPathsButton = new QPushButton; + + bool isPortable = settings.getIsPortableBuild(); + if (isPortable) { + deckPathEdit->setEnabled(false); + filtersPathEdit->setEnabled(false); + replaysPathEdit->setEnabled(false); + picsPathEdit->setEnabled(false); + cardDatabasePathEdit->setEnabled(false); + customCardDatabasePathEdit->setEnabled(false); + tokenDatabasePathEdit->setEnabled(false); + + deckPathButton->setVisible(false); + replaysPathButton->setVisible(false); + picsPathButton->setVisible(false); + cardDatabasePathButton->setVisible(false); + customCardDatabasePathButton->setVisible(false); + tokenDatabasePathButton->setVisible(false); + } else { + connect(resetAllPathsButton, &QPushButton::clicked, this, &GeneralSettingsPage::resetAllPathsClicked); + allPathsResetLabel = new QLabel(tr("All paths have been reset")); + allPathsResetLabel->setVisible(false); + } + + auto *pathsGrid = new QGridLayout; + pathsGrid->addWidget(&deckPathLabel, 0, 0); + pathsGrid->addWidget(deckPathEdit, 0, 1); + pathsGrid->addWidget(deckPathButton, 0, 2); + pathsGrid->addWidget(&filtersPathLabel, 1, 0); + pathsGrid->addWidget(filtersPathEdit, 1, 1); + pathsGrid->addWidget(filtersPathButton, 1, 2); + pathsGrid->addWidget(&replaysPathLabel, 2, 0); + pathsGrid->addWidget(replaysPathEdit, 2, 1); + pathsGrid->addWidget(replaysPathButton, 2, 2); + pathsGrid->addWidget(&picsPathLabel, 3, 0); + pathsGrid->addWidget(picsPathEdit, 3, 1); + pathsGrid->addWidget(picsPathButton, 3, 2); + pathsGrid->addWidget(&cardDatabasePathLabel, 4, 0); + pathsGrid->addWidget(cardDatabasePathEdit, 4, 1); + pathsGrid->addWidget(cardDatabasePathButton, 4, 2); + pathsGrid->addWidget(&customCardDatabasePathLabel, 5, 0); + pathsGrid->addWidget(customCardDatabasePathEdit, 5, 1); + pathsGrid->addWidget(customCardDatabasePathButton, 5, 2); + pathsGrid->addWidget(&tokenDatabasePathLabel, 6, 0); + pathsGrid->addWidget(tokenDatabasePathEdit, 6, 1); + pathsGrid->addWidget(tokenDatabasePathButton, 6, 2); + if (!isPortable) { + pathsGrid->addWidget(resetAllPathsButton, 7, 0); + pathsGrid->addWidget(allPathsResetLabel, 7, 1); + } + pathsGroupBox = new QGroupBox; + pathsGroupBox->setLayout(pathsGrid); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(personalGroupBox); + mainLayout->addWidget(pathsGroupBox); + mainLayout->addStretch(); + + GeneralSettingsPage::retranslateUi(); + + // connect the ReleaseChannel combo box only after the entries are inserted in retranslateUi + connect(&updateReleaseChannelBox, qOverload(&QComboBox::currentIndexChanged), &settings, + &SettingsCache::setUpdateReleaseChannelIndex); + updateReleaseChannelBox.setCurrentIndex(settings.getUpdateReleaseChannelIndex()); + + setLayout(mainLayout); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &GeneralSettingsPage::retranslateUi); + retranslateUi(); +} + +QStringList GeneralSettingsPage::findQmFiles() +{ + QDir dir(translationPath); + QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); + fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); + return fileNames; +} + +QString GeneralSettingsPage::languageName(const QString &lang) +{ + QTranslator qTranslator; + + QString appNameHint = translationPrefix + "_" + lang; + bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); + if (!appTranslationLoaded) { + qCWarning(GeneralSettingsPageLog) + << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } + + return qTranslator.translate("i18n", DEFAULT_LANG_NAME); +} + +void GeneralSettingsPage::deckPathButtonClicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), deckPathEdit->text()); + if (path.isEmpty()) { + return; + } + + deckPathEdit->setText(path); + SettingsCache::instance().setDeckPath(path); +} + +void GeneralSettingsPage::filtersPathButtonClicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), filtersPathEdit->text()); + if (path.isEmpty()) { + return; + } + + filtersPathEdit->setText(path); + SettingsCache::instance().setFiltersPath(path); +} + +void GeneralSettingsPage::replaysPathButtonClicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), replaysPathEdit->text()); + if (path.isEmpty()) { + return; + } + + replaysPathEdit->setText(path); + SettingsCache::instance().setReplaysPath(path); +} + +void GeneralSettingsPage::picsPathButtonClicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), picsPathEdit->text()); + if (path.isEmpty()) { + return; + } + + picsPathEdit->setText(path); + SettingsCache::instance().setPicsPath(path); +} + +void GeneralSettingsPage::cardDatabasePathButtonClicked() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Choose path"), cardDatabasePathEdit->text()); + if (path.isEmpty()) { + return; + } + + cardDatabasePathEdit->setText(path); + SettingsCache::instance().setCardDatabasePath(path); +} + +void GeneralSettingsPage::customCardDatabaseButtonClicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), customCardDatabasePathEdit->text()); + if (path.isEmpty()) { + return; + } + + customCardDatabasePathEdit->setText(path); + SettingsCache::instance().setCustomCardDatabasePath(path); +} + +void GeneralSettingsPage::tokenDatabasePathButtonClicked() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Choose path"), tokenDatabasePathEdit->text()); + if (path.isEmpty()) { + return; + } + + tokenDatabasePathEdit->setText(path); + SettingsCache::instance().setTokenDatabasePath(path); +} + +void GeneralSettingsPage::resetAllPathsClicked() +{ + SettingsCache &settings = SettingsCache::instance(); + settings.resetPaths(); + deckPathEdit->setText(settings.getDeckPath()); + replaysPathEdit->setText(settings.getReplaysPath()); + picsPathEdit->setText(settings.getPicsPath()); + cardDatabasePathEdit->setText(settings.getCardDatabasePath()); + customCardDatabasePathEdit->setText(settings.getCustomCardDatabasePath()); + tokenDatabasePathEdit->setText(settings.getTokenDatabasePath()); + allPathsResetLabel->setVisible(true); +} + +void GeneralSettingsPage::languageBoxChanged(int index) +{ + SettingsCache::instance().setLang(languageBox.itemData(index).toString()); +} + +void GeneralSettingsPage::retranslateUi() +{ + personalGroupBox->setTitle(tr("Personal settings")); + languageLabel.setText(tr("Language:")); + + if (SettingsCache::instance().getIsPortableBuild()) { + pathsGroupBox->setTitle(tr("Paths (editing disabled in portable mode)")); + } else { + pathsGroupBox->setTitle(tr("Paths")); + } + advertiseTranslationPageLabel.setText( + QString("%2").arg(WIKI_TRANSLATION_FAQ).arg(tr("How to help with translations"))); + deckPathLabel.setText(tr("Decks directory:")); + filtersPathLabel.setText(tr("Filters directory:")); + replaysPathLabel.setText(tr("Replays directory:")); + picsPathLabel.setText(tr("Pictures directory:")); + cardDatabasePathLabel.setText(tr("Card database:")); + customCardDatabasePathLabel.setText(tr("Custom database directory:")); + tokenDatabasePathLabel.setText(tr("Token database:")); + updateReleaseChannelLabel.setText(tr("Update channel")); + startupUpdateCheckCheckBox.setText(tr("Check for client updates on startup")); + startupCardUpdateCheckBehaviorLabel.setText(tr("Check for card database updates on startup")); + startupCardUpdateCheckBehaviorSelector.setItemText(startupCardUpdateCheckBehaviorIndexNone, tr("Don't check")); + startupCardUpdateCheckBehaviorSelector.setItemText(startupCardUpdateCheckBehaviorIndexPrompt, + tr("Prompt for update")); + startupCardUpdateCheckBehaviorSelector.setItemText(startupCardUpdateCheckBehaviorIndexAlways, + tr("Always update in the background")); + cardUpdateCheckIntervalLabel.setText(tr("Check for card database updates every")); + cardUpdateCheckIntervalSpinBox.setSuffix(tr(" days")); + updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client")); + newVersionOracleCheckBox.setText(tr("Automatically run Oracle when running a new version of Cockatrice")); + showTipsOnStartup.setText(tr("Show tips on startup")); + resetAllPathsButton->setText(tr("Reset all paths")); + + const auto &settings = SettingsCache::instance(); + + QDate lastCheckDate = settings.getLastCardUpdateCheck(); + int daysAgo = lastCheckDate.daysTo(QDate::currentDate()); + + lastCardUpdateCheckDateLabel.setText( + tr("Last update check on %1 (%2 days ago)").arg(lastCheckDate.toString()).arg(daysAgo)); + + // We can't change the strings after they're put into the QComboBox, so this is our workaround + int oldIndex = updateReleaseChannelBox.currentIndex(); + updateReleaseChannelBox.clear(); + for (ReleaseChannel *chan : settings.getUpdateReleaseChannels()) { + updateReleaseChannelBox.addItem(tr(chan->getName().toUtf8())); + } + updateReleaseChannelBox.setCurrentIndex(oldIndex); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/general_settings_page.h b/cockatrice/src/interface/widgets/settings_page/general_settings_page.h new file mode 100644 index 000000000..0d3c03804 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/general_settings_page.h @@ -0,0 +1,71 @@ +#ifndef COCKATRICE_GENERAL_SETTINGS_PAGE_H +#define COCKATRICE_GENERAL_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include +#include +#include + +inline Q_LOGGING_CATEGORY(GeneralSettingsPageLog, "general_settings_page"); + +class GeneralSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +public: + GeneralSettingsPage(); + void retranslateUi() override; + +private slots: + void deckPathButtonClicked(); + void filtersPathButtonClicked(); + void replaysPathButtonClicked(); + void picsPathButtonClicked(); + void cardDatabasePathButtonClicked(); + void customCardDatabaseButtonClicked(); + void tokenDatabasePathButtonClicked(); + void resetAllPathsClicked(); + void languageBoxChanged(int index); + +private: + QStringList findQmFiles(); + QString languageName(const QString &lang); + QLineEdit *deckPathEdit; + QLineEdit *filtersPathEdit; + QLineEdit *replaysPathEdit; + QLineEdit *picsPathEdit; + QLineEdit *cardDatabasePathEdit; + QLineEdit *customCardDatabasePathEdit; + QLineEdit *tokenDatabasePathEdit; + QPushButton *resetAllPathsButton; + QLabel *allPathsResetLabel; + QGroupBox *personalGroupBox; + QGroupBox *pathsGroupBox; + QComboBox languageBox; + QCheckBox startupUpdateCheckCheckBox; + QLabel startupCardUpdateCheckBehaviorLabel; + QComboBox startupCardUpdateCheckBehaviorSelector; + QLabel cardUpdateCheckIntervalLabel; + QSpinBox cardUpdateCheckIntervalSpinBox; + QLabel lastCardUpdateCheckDateLabel; + QCheckBox updateNotificationCheckBox; + QCheckBox newVersionOracleCheckBox; + QComboBox updateReleaseChannelBox; + QLabel languageLabel; + QLabel deckPathLabel; + QLabel filtersPathLabel; + QLabel replaysPathLabel; + QLabel picsPathLabel; + QLabel cardDatabasePathLabel; + QLabel customCardDatabasePathLabel; + QLabel tokenDatabasePathLabel; + QLabel updateReleaseChannelLabel; + QLabel advertiseTranslationPageLabel; + QCheckBox showTipsOnStartup; +}; + +#endif // COCKATRICE_GENERAL_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp new file mode 100644 index 000000000..f64398fe5 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp @@ -0,0 +1,252 @@ +#include "messages_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../interface/widgets/utility/get_text_with_max.h" + +#include +#include +#include + +MessagesSettingsPage::MessagesSettingsPage() +{ + chatMentionCheckBox.setChecked(SettingsCache::instance().getChatMention()); + connect(&chatMentionCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setChatMention); + + chatMentionCompleterCheckbox.setChecked(SettingsCache::instance().getChatMentionCompleter()); + connect(&chatMentionCompleterCheckbox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setChatMentionCompleter); + + explainMessagesLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); + explainMessagesLabel.setOpenExternalLinks(true); + + ignoreUnregUsersMainChat.setChecked(SettingsCache::instance().getIgnoreUnregisteredUsers()); + ignoreUnregUserMessages.setChecked(SettingsCache::instance().getIgnoreUnregisteredUserMessages()); + connect(&ignoreUnregUsersMainChat, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setIgnoreUnregisteredUsers); + connect(&ignoreUnregUserMessages, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setIgnoreUnregisteredUserMessages); + + invertMentionForeground.setChecked(SettingsCache::instance().getChatMentionForeground()); + connect(&invertMentionForeground, &QCheckBox::QT_STATE_CHANGED, this, &MessagesSettingsPage::updateTextColor); + + invertHighlightForeground.setChecked(SettingsCache::instance().getChatHighlightForeground()); + connect(&invertHighlightForeground, &QCheckBox::QT_STATE_CHANGED, this, + &MessagesSettingsPage::updateTextHighlightColor); + + mentionColor = new QLineEdit(); + mentionColor->setText(SettingsCache::instance().getChatMentionColor()); + updateMentionPreview(); + connect(mentionColor, &QLineEdit::textChanged, this, &MessagesSettingsPage::updateColor); + + messagePopups.setChecked(SettingsCache::instance().getShowMessagePopup()); + connect(&messagePopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowMessagePopups); + + mentionPopups.setChecked(SettingsCache::instance().getShowMentionPopup()); + connect(&mentionPopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowMentionPopups); + + roomHistory.setChecked(SettingsCache::instance().getRoomHistory()); + connect(&roomHistory, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setRoomHistory); + + customAlertString = new QLineEdit(); + customAlertString->setText(SettingsCache::instance().getHighlightWords()); + connect(customAlertString, &QLineEdit::textChanged, &SettingsCache::instance(), &SettingsCache::setHighlightWords); + + auto *chatGrid = new QGridLayout; + chatGrid->addWidget(&chatMentionCheckBox, 0, 0); + chatGrid->addWidget(&invertMentionForeground, 0, 1); + chatGrid->addWidget(mentionColor, 0, 2); + chatGrid->addWidget(&chatMentionCompleterCheckbox, 1, 0); + chatGrid->addWidget(&ignoreUnregUsersMainChat, 2, 0); + chatGrid->addWidget(&hexLabel, 1, 2); + chatGrid->addWidget(&ignoreUnregUserMessages, 3, 0); + chatGrid->addWidget(&messagePopups, 4, 0); + chatGrid->addWidget(&mentionPopups, 5, 0); + chatGrid->addWidget(&roomHistory, 6, 0); + chatGroupBox = new QGroupBox; + chatGroupBox->setLayout(chatGrid); + + highlightColor = new QLineEdit(); + highlightColor->setText(SettingsCache::instance().getChatHighlightColor()); + updateHighlightPreview(); + connect(highlightColor, &QLineEdit::textChanged, this, &MessagesSettingsPage::updateHighlightColor); + + auto *highlightNotice = new QGridLayout; + highlightNotice->addWidget(highlightColor, 0, 2); + highlightNotice->addWidget(&invertHighlightForeground, 0, 1); + highlightNotice->addWidget(&hexHighlightLabel, 1, 2); + highlightNotice->addWidget(customAlertString, 0, 0); + highlightNotice->addWidget(&customAlertStringLabel, 1, 0); + highlightGroupBox = new QGroupBox; + highlightGroupBox->setLayout(highlightNotice); + + messageList = new QListWidget; + + int count = SettingsCache::instance().messages().getCount(); + for (int i = 0; i < count; i++) { + messageList->addItem(SettingsCache::instance().messages().getMessageAt(i)); + } + + aAdd = new QAction(this); + aAdd->setIcon(QPixmap("theme:icons/increment")); + connect(aAdd, &QAction::triggered, this, &MessagesSettingsPage::actAdd); + + aEdit = new QAction(this); + aEdit->setIcon(QPixmap("theme:icons/pencil")); + connect(aEdit, &QAction::triggered, this, &MessagesSettingsPage::actEdit); + + aRemove = new QAction(this); + aRemove->setIcon(QPixmap("theme:icons/decrement")); + connect(aRemove, &QAction::triggered, this, &MessagesSettingsPage::actRemove); + + auto *messageToolBar = new QToolBar; + messageToolBar->setOrientation(Qt::Vertical); + messageToolBar->addAction(aAdd); + messageToolBar->addAction(aRemove); + messageToolBar->addAction(aEdit); + messageToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + + auto *messageListLayout = new QHBoxLayout; + messageListLayout->addWidget(messageToolBar); + messageListLayout->addWidget(messageList); + + auto *messagesLayout = new QVBoxLayout; // combines the explainer label with the actual messages widget pieces + messagesLayout->addLayout(messageListLayout); + messagesLayout->addWidget(&explainMessagesLabel); + + messageGroupBox = new QGroupBox; // draws a box around the above layout and allows it to be titled + messageGroupBox->setLayout(messagesLayout); + + auto *mainLayout = new QVBoxLayout; // combines the messages groupbox with the rest of the menu + mainLayout->addWidget(messageGroupBox); + mainLayout->addWidget(chatGroupBox); + mainLayout->addWidget(highlightGroupBox); + + setLayout(mainLayout); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &MessagesSettingsPage::retranslateUi); + retranslateUi(); +} + +void MessagesSettingsPage::updateColor(const QString &value) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + QColor colorToSet = QColor::fromString("#" + value); +#else + QColor colorToSet; + colorToSet.setNamedColor("#" + value); +#endif + if (colorToSet.isValid()) { + SettingsCache::instance().setChatMentionColor(value); + updateMentionPreview(); + } +} + +void MessagesSettingsPage::updateHighlightColor(const QString &value) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + QColor colorToSet = QColor::fromString("#" + value); +#else + QColor colorToSet; + colorToSet.setNamedColor("#" + value); +#endif + if (colorToSet.isValid()) { + SettingsCache::instance().setChatHighlightColor(value); + updateHighlightPreview(); + } +} + +void MessagesSettingsPage::updateTextColor(QT_STATE_CHANGED_T value) +{ + SettingsCache::instance().setChatMentionForeground(value); + updateMentionPreview(); +} + +void MessagesSettingsPage::updateTextHighlightColor(QT_STATE_CHANGED_T value) +{ + SettingsCache::instance().setChatHighlightForeground(value); + updateHighlightPreview(); +} + +void MessagesSettingsPage::updateMentionPreview() +{ + mentionColor->setStyleSheet( + "QLineEdit{background:#" + SettingsCache::instance().getChatMentionColor() + + ";color: " + (SettingsCache::instance().getChatMentionForeground() ? "white" : "black") + ";}"); +} + +void MessagesSettingsPage::updateHighlightPreview() +{ + highlightColor->setStyleSheet( + "QLineEdit{background:#" + SettingsCache::instance().getChatHighlightColor() + + ";color: " + (SettingsCache::instance().getChatHighlightForeground() ? "white" : "black") + ";}"); +} + +void MessagesSettingsPage::storeSettings() +{ + SettingsCache::instance().messages().setCount(messageList->count()); + for (int i = 0; i < messageList->count(); i++) { + SettingsCache::instance().messages().setMessageAt(i, messageList->item(i)->text()); + } + emit SettingsCache::instance().messages().messageMacrosChanged(); +} + +void MessagesSettingsPage::actAdd() +{ + bool ok; + QString msg = + getTextWithMax(this, tr("Add message"), tr("Message:"), QLineEdit::Normal, QString(), &ok, MAX_TEXT_LENGTH); + if (ok) { + messageList->addItem(msg); + storeSettings(); + } +} + +void MessagesSettingsPage::actEdit() +{ + if (messageList->currentItem()) { + QString oldText = messageList->currentItem()->text(); + bool ok; + QString msg = + getTextWithMax(this, tr("Edit message"), tr("Message:"), QLineEdit::Normal, oldText, &ok, MAX_TEXT_LENGTH); + if (ok) { + messageList->currentItem()->setText(msg); + storeSettings(); + } + } +} + +void MessagesSettingsPage::actRemove() +{ + if (messageList->currentItem() != nullptr) { + delete messageList->takeItem(messageList->currentRow()); + storeSettings(); + } +} + +void MessagesSettingsPage::retranslateUi() +{ + chatGroupBox->setTitle(tr("Chat settings")); + highlightGroupBox->setTitle(tr("Custom alert words")); + chatMentionCheckBox.setText(tr("Enable chat mentions")); + chatMentionCompleterCheckbox.setText(tr("Enable mention completer")); + messageGroupBox->setTitle(tr("In-game message macros")); + explainMessagesLabel.setText( + QString("%2").arg(WIKI_CUSTOM_SHORTCUTS).arg(tr("How to use in-game message macros"))); + ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users")); + ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users")); + invertMentionForeground.setText(tr("Invert text color")); + invertHighlightForeground.setText(tr("Invert text color")); + messagePopups.setText(tr("Enable desktop notifications for private messages")); + mentionPopups.setText(tr("Enable desktop notification for mentions")); + roomHistory.setText(tr("Enable room message history on join")); + hexLabel.setText(tr("(Color is hexadecimal)")); + hexHighlightLabel.setText(tr("(Color is hexadecimal)")); + customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only")); + customAlertString->setPlaceholderText(tr("Word1 Word2 Word3")); + aAdd->setText(tr("Add New Message")); + aEdit->setText(tr("Edit Message")); + aRemove->setText(tr("Remove Message")); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h new file mode 100644 index 000000000..e8a4a8aa4 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h @@ -0,0 +1,58 @@ +#ifndef COCKATRICE_MESSAGES_SETTINGS_PAGE_H +#define COCKATRICE_MESSAGES_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include + +class MessagesSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +public: + MessagesSettingsPage(); + void retranslateUi() override; + +private slots: + void actAdd(); + void actEdit(); + void actRemove(); + void updateColor(const QString &value); + void updateHighlightColor(const QString &value); + void updateTextColor(QT_STATE_CHANGED_T value); + void updateTextHighlightColor(QT_STATE_CHANGED_T value); + +private: + QListWidget *messageList; + QAction *aAdd; + QAction *aEdit; + QAction *aRemove; + QCheckBox chatMentionCheckBox; + QCheckBox chatMentionCompleterCheckbox; + QCheckBox invertMentionForeground; + QCheckBox invertHighlightForeground; + QCheckBox ignoreUnregUsersMainChat; + QCheckBox ignoreUnregUserMessages; + QCheckBox messagePopups; + QCheckBox mentionPopups; + QCheckBox roomHistory; + QGroupBox *chatGroupBox; + QGroupBox *highlightGroupBox; + QGroupBox *messageGroupBox; + QLineEdit *mentionColor; + QLineEdit *highlightColor; + QLineEdit *customAlertString; + QLabel hexLabel; + QLabel hexHighlightLabel; + QLabel customAlertStringLabel; + QLabel explainMessagesLabel; + + void storeSettings(); + void updateMentionPreview(); + void updateHighlightPreview(); +}; + +#endif // COCKATRICE_MESSAGES_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp new file mode 100644 index 000000000..e7a04ef79 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.cpp @@ -0,0 +1,128 @@ +#include "shortcut_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../../../client/settings/shortcut_treeview.h" +#include "../interface/widgets/utility/custom_line_edit.h" +#include "../interface/widgets/utility/sequence_edit.h" + +#include +#include + +ShortcutSettingsPage::ShortcutSettingsPage() +{ + // search bar + searchEdit = new SearchLineEdit; + searchEdit->setObjectName("searchEdit"); + searchEdit->setClearButtonEnabled(true); + + setFocusProxy(searchEdit); + setFocusPolicy(Qt::ClickFocus); + + // table + shortcutsTable = new ShortcutTreeView(this); + + shortcutsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + shortcutsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + shortcutsTable->setColumnWidth(0, width() / 3 * 2); + searchEdit->setTreeView(shortcutsTable); + + connect(searchEdit, &SearchLineEdit::textChanged, shortcutsTable, &ShortcutTreeView::updateSearchString); + + // edit widget + currentActionGroupLabel = new QLabel(this); + currentActionGroupName = new QLabel(this); + currentActionLabel = new QLabel(this); + currentActionName = new QLabel(this); + currentShortcutLabel = new QLabel(this); + editTextBox = new SequenceEdit("", this); + shortcutsTable->installEventFilter(editTextBox); + + // buttons + faqLabel = new QLabel(this); + faqLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse); + faqLabel->setOpenExternalLinks(true); + + btnResetAll = new QPushButton(this); + btnClearAll = new QPushButton(this); + + btnResetAll->setIcon(QPixmap("theme:icons/update")); + btnClearAll->setIcon(QPixmap("theme:icons/clearsearch")); + + // layout + auto *_editLayout = new QGridLayout; + _editLayout->addWidget(currentActionGroupLabel, 0, 0); + _editLayout->addWidget(currentActionGroupName, 0, 1); + _editLayout->addWidget(currentActionLabel, 1, 0); + _editLayout->addWidget(currentActionName, 1, 1); + _editLayout->addWidget(currentShortcutLabel, 2, 0); + _editLayout->addWidget(editTextBox, 2, 1); + + editShortcutGroupBox = new QGroupBox; + editShortcutGroupBox->setLayout(_editLayout); + + auto *_buttonsLayout = new QHBoxLayout; + _buttonsLayout->addWidget(faqLabel); + _buttonsLayout->addWidget(btnResetAll); + _buttonsLayout->addWidget(btnClearAll); + + auto *_mainLayout = new QVBoxLayout; + _mainLayout->addWidget(searchEdit); + _mainLayout->addWidget(shortcutsTable); + _mainLayout->addWidget(editShortcutGroupBox); + _mainLayout->addLayout(_buttonsLayout); + + setLayout(_mainLayout); + + connect(btnResetAll, &QPushButton::clicked, this, &ShortcutSettingsPage::resetShortcuts); + connect(btnClearAll, &QPushButton::clicked, this, &ShortcutSettingsPage::clearShortcuts); + + connect(shortcutsTable, &ShortcutTreeView::currentItemChanged, this, &ShortcutSettingsPage::currentItemChanged); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &ShortcutSettingsPage::retranslateUi); + retranslateUi(); +} + +void ShortcutSettingsPage::currentItemChanged(const QString &key) +{ + if (key.isEmpty()) { + currentActionGroupName->setText(""); + currentActionName->setText(""); + editTextBox->setShortcutName(""); + } else { + QString group = SettingsCache::instance().shortcuts().getShortcut(key).getGroupName(); + QString action = SettingsCache::instance().shortcuts().getShortcut(key).getName(); + currentActionGroupName->setText(group); + currentActionName->setText(action); + editTextBox->setShortcutName(key); + } +} + +void ShortcutSettingsPage::resetShortcuts() +{ + if (QMessageBox::question(this, tr("Restore all default shortcuts"), + tr("Do you really want to restore all default shortcuts?")) == QMessageBox::Yes) { + SettingsCache::instance().shortcuts().resetAllShortcuts(); + } +} + +void ShortcutSettingsPage::clearShortcuts() +{ + if (QMessageBox::question(this, tr("Clear all default shortcuts"), + tr("Do you really want to clear all shortcuts?")) == QMessageBox::Yes) { + SettingsCache::instance().shortcuts().clearAllShortcuts(); + } +} + +void ShortcutSettingsPage::retranslateUi() +{ + shortcutsTable->retranslateUi(); + + currentActionGroupLabel->setText(tr("Section:")); + currentActionLabel->setText(tr("Action:")); + currentShortcutLabel->setText(tr("Shortcut:")); + editTextBox->retranslateUi(); + faqLabel->setText(QString("%2").arg(WIKI_CUSTOM_SHORTCUTS).arg(tr("How to set custom shortcuts"))); + btnResetAll->setText(tr("Restore all default shortcuts")); + btnClearAll->setText(tr("Clear all shortcuts")); + searchEdit->setPlaceholderText(tr("Search by shortcut name")); +} diff --git a/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.h b/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.h new file mode 100644 index 000000000..05391df77 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/shortcut_settings_page.h @@ -0,0 +1,45 @@ +#ifndef COCKATRICE_SHORTCUT_SETTINGS_PAGE_H +#define COCKATRICE_SHORTCUT_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include + +class SequenceEdit; +class ShortcutTreeView; +class SearchLineEdit; + +class ShortcutSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +public: + ShortcutSettingsPage(); + void retranslateUi() override; + +private: + SearchLineEdit *searchEdit; + ShortcutTreeView *shortcutsTable; + QVBoxLayout *mainLayout; + QHBoxLayout *buttonsLayout; + QGroupBox *editShortcutGroupBox; + QGridLayout *editLayout; + QLabel *currentActionGroupLabel; + QLabel *currentActionGroupName; + QLabel *currentActionLabel; + QLabel *currentActionName; + QLabel *currentShortcutLabel; + SequenceEdit *editTextBox; + QLabel *faqLabel; + QPushButton *btnResetAll; + QPushButton *btnClearAll; + +private slots: + void resetShortcuts(); + void clearShortcuts(); + void currentItemChanged(const QString &key); +}; + +#endif // COCKATRICE_SHORTCUT_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/sound_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/sound_settings_page.cpp new file mode 100644 index 000000000..e7e92ea15 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/sound_settings_page.cpp @@ -0,0 +1,86 @@ +#include "sound_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../client/sound_engine.h" + +#include + +SoundSettingsPage::SoundSettingsPage() +{ + soundEnabledCheckBox.setChecked(SettingsCache::instance().getSoundEnabled()); + connect(&soundEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setSoundEnabled); + + QString themeName = SettingsCache::instance().getSoundThemeName(); + + QStringList themeDirs = soundEngine->getAvailableThemes().keys(); + for (int i = 0; i < themeDirs.size(); i++) { + themeBox.addItem(themeDirs[i]); + if (themeDirs[i] == themeName) { + themeBox.setCurrentIndex(i); + } + } + + connect(&themeBox, qOverload(&QComboBox::currentIndexChanged), this, &SoundSettingsPage::themeBoxChanged); + connect(&soundTestButton, &QPushButton::clicked, soundEngine, &SoundEngine::testSound); + + masterVolumeSlider = new QSlider(Qt::Horizontal); + masterVolumeSlider->setMinimum(0); + masterVolumeSlider->setMaximum(100); + masterVolumeSlider->setValue(SettingsCache::instance().getMasterVolume()); + masterVolumeSlider->setToolTip(QString::number(SettingsCache::instance().getMasterVolume())); + connect(&SettingsCache::instance(), &SettingsCache::masterVolumeChanged, this, + &SoundSettingsPage::masterVolumeChanged); + connect(masterVolumeSlider, &QSlider::sliderReleased, soundEngine, &SoundEngine::testSound); + connect(masterVolumeSlider, &QSlider::valueChanged, &SettingsCache::instance(), &SettingsCache::setMasterVolume); + + masterVolumeSpinBox = new QSpinBox(); + masterVolumeSpinBox->setMinimum(0); + masterVolumeSpinBox->setMaximum(100); + masterVolumeSpinBox->setValue(SettingsCache::instance().getMasterVolume()); + connect(masterVolumeSlider, &QSlider::valueChanged, masterVolumeSpinBox, &QSpinBox::setValue); + connect(masterVolumeSpinBox, qOverload(&QSpinBox::valueChanged), masterVolumeSlider, &QSlider::setValue); + + auto *soundGrid = new QGridLayout; + soundGrid->addWidget(&soundEnabledCheckBox, 0, 0, 1, 3); + soundGrid->addWidget(&masterVolumeLabel, 1, 0); + soundGrid->addWidget(masterVolumeSlider, 1, 1); + soundGrid->addWidget(masterVolumeSpinBox, 1, 2); + soundGrid->addWidget(&themeLabel, 2, 0); + soundGrid->addWidget(&themeBox, 2, 1); + soundGrid->addWidget(&soundTestButton, 3, 1); + + soundGroupBox = new QGroupBox; + soundGroupBox->setLayout(soundGrid); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(soundGroupBox); + mainLayout->addStretch(); + + setLayout(mainLayout); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &SoundSettingsPage::retranslateUi); + retranslateUi(); +} + +void SoundSettingsPage::themeBoxChanged(int index) +{ + QStringList themeDirs = soundEngine->getAvailableThemes().keys(); + if (index >= 0 && index < themeDirs.count()) { + SettingsCache::instance().setSoundThemeName(themeDirs.at(index)); + } +} + +void SoundSettingsPage::masterVolumeChanged(int value) +{ + masterVolumeSlider->setToolTip(QString::number(value)); +} + +void SoundSettingsPage::retranslateUi() +{ + soundEnabledCheckBox.setText(tr("Enable &sounds")); + themeLabel.setText(tr("Current sounds theme:")); + soundTestButton.setText(tr("Test system sound engine")); + soundGroupBox->setTitle(tr("Sound settings")); + masterVolumeLabel.setText(tr("Master volume")); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/sound_settings_page.h b/cockatrice/src/interface/widgets/settings_page/sound_settings_page.h new file mode 100644 index 000000000..f90bcbc5a --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/sound_settings_page.h @@ -0,0 +1,35 @@ +#ifndef COCKATRICE_SOUND_SETTINGS_PAGE_H +#define COCKATRICE_SOUND_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include +#include + +class SoundSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +public: + SoundSettingsPage(); + void retranslateUi() override; + +private: + QLabel themeLabel; + QComboBox themeBox; + QGroupBox *soundGroupBox; + QPushButton soundTestButton; + QCheckBox soundEnabledCheckBox; + QLabel masterVolumeLabel; + QSlider *masterVolumeSlider; + QSpinBox *masterVolumeSpinBox; + +private slots: + void masterVolumeChanged(int value); + void themeBoxChanged(int index); +}; + +#endif // COCKATRICE_SOUND_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/storage_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/storage_settings_page.cpp new file mode 100644 index 000000000..c9d3c7789 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/storage_settings_page.cpp @@ -0,0 +1,244 @@ +#include "storage_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../interface/card_picture_loader/card_picture_loader.h" + +#include +#include +#include + +StorageSettingsPage::StorageSettingsPage() +{ + auto *lpNetworkCacheGrid = new QGridLayout; + auto *lpImageBackupGrid = new QGridLayout; + auto *lpPixmapCacheGrid = new QGridLayout; + + networkCacheExplainerLabel.setWordWrap(true); + imageBackupExplainerLabel.setWordWrap(true); + pixmapCacheExplainerLabel.setWordWrap(true); + + connect(&clearDownloadedPicsButton, &QPushButton::clicked, this, + &StorageSettingsPage::clearDownloadedPicsButtonClicked); + + connect(&clearPixmapCacheButton, &QPushButton::clicked, this, &StorageSettingsPage::clearPixmapCacheButtonClicked); + + // pixmap cache + pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN); + // 2047 is the max value to avoid overflowing of QPixmapCache::setCacheLimit(int size) + pixmapCacheEdit.setMaximum(PIXMAPCACHE_SIZE_MAX); + pixmapCacheEdit.setSingleStep(64); + pixmapCacheEdit.setValue(SettingsCache::instance().getPixmapCacheSize()); + pixmapCacheEdit.setSuffix(" MB"); + + // Caching method + + cardPictureLoaderCacheMethodComboBox = new QComboBox; + for (auto method : CardPictureLoaderCacheMethod::methods()) { + cardPictureLoaderCacheMethodComboBox->addItem(method.displayName, static_cast(method.id)); + } + + int currentCacheMethod = static_cast(SettingsCache::instance().getCardPictureLoaderCacheMethod()); + + int currentIndex = cardPictureLoaderCacheMethodComboBox->findData(currentCacheMethod); + if (currentIndex >= 0) { + cardPictureLoaderCacheMethodComboBox->setCurrentIndex(currentIndex); + } + + connect(cardPictureLoaderCacheMethodComboBox, qOverload(&QComboBox::currentIndexChanged), this, + [this](int index) { + auto cacheMethod = static_cast( + cardPictureLoaderCacheMethodComboBox->itemData(index).toInt()); + + bool useNetworkCache = (cacheMethod == CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE); + + if (useNetworkCache) { + clearImageBackupsButtonClicked(); + } else { + clearDownloadedPicsButtonClicked(); + } + + mpNetworkCacheGroupBox->setEnabled(useNetworkCache); + mpImageBackupGroupBox->setEnabled(!useNetworkCache); + + SettingsCache::instance().setCardImageCacheMethod(cacheMethod); + }); + + // Network Cache + + networkCacheEdit.setMinimum(NETWORK_CACHE_SIZE_MIN); + networkCacheEdit.setMaximum(NETWORK_CACHE_SIZE_MAX); + networkCacheEdit.setSingleStep(1); + networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB()); + networkCacheEdit.setSuffix(" MB"); + + networkRedirectCacheTtlEdit.setMinimum(NETWORK_REDIRECT_CACHE_TTL_MIN); + networkRedirectCacheTtlEdit.setMaximum(NETWORK_REDIRECT_CACHE_TTL_MAX); + networkRedirectCacheTtlEdit.setSingleStep(1); + networkRedirectCacheTtlEdit.setValue(SettingsCache::instance().getRedirectCacheTtl()); + + // Image Backup + localCardImageStorageNamingSchemeComboBox = new QComboBox; + for (const auto &scheme : CardPictureLoaderLocalSchemes::exportSchemes()) { + localCardImageStorageNamingSchemeComboBox->addItem(scheme.displayName, static_cast(scheme.id)); + } + + int current = static_cast(SettingsCache::instance().getLocalCardImageStorageNamingScheme()); + + int index = localCardImageStorageNamingSchemeComboBox->findData(current); + if (index >= 0) { + localCardImageStorageNamingSchemeComboBox->setCurrentIndex(index); + } + + connect(localCardImageStorageNamingSchemeComboBox, qOverload(&QComboBox::currentIndexChanged), this, + [this](int index) { + auto scheme = static_cast( + localCardImageStorageNamingSchemeComboBox->itemData(index).toInt()); + SettingsCache::instance().setLocalCardImageStorageNamingScheme(scheme); + }); + + connect(&clearBackupsButton, &QPushButton::clicked, this, &StorageSettingsPage::clearImageBackupsButtonClicked); + + auto cacheMethodLayout = new QHBoxLayout; + cacheMethodLayout->addWidget(&cardPictureLoaderCacheMethodLabel); + cacheMethodLayout->addWidget(cardPictureLoaderCacheMethodComboBox); + + auto networkCacheLayout = new QHBoxLayout; + networkCacheLayout->addWidget(&clearDownloadedPicsButton); + networkCacheLayout->addStretch(); + networkCacheLayout->addWidget(&networkCacheLabel); + networkCacheLayout->addWidget(&networkCacheEdit); + + auto networkRedirectCacheLayout = new QHBoxLayout; + networkRedirectCacheLayout->addStretch(); + networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlLabel); + networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlEdit); + + auto pixmapCacheLayout = new QHBoxLayout; + pixmapCacheLayout->addWidget(&clearPixmapCacheButton); + pixmapCacheLayout->addStretch(); + pixmapCacheLayout->addWidget(&pixmapCacheLabel); + pixmapCacheLayout->addWidget(&pixmapCacheEdit); + + lpNetworkCacheGrid->addWidget(&networkCacheExplainerLabel, 0, 0); + lpNetworkCacheGrid->addLayout(networkCacheLayout, 1, 0); + lpNetworkCacheGrid->addLayout(networkRedirectCacheLayout, 2, 0); + + // Image Backup Layout + lpImageBackupGrid->addWidget(&imageBackupExplainerLabel, 0, 0, 1, 2); + lpImageBackupGrid->addWidget(&localCardImageStorageNamingSchemeLabel, 1, 0); + lpImageBackupGrid->addWidget(localCardImageStorageNamingSchemeComboBox, 1, 1); + lpImageBackupGrid->addWidget(&clearBackupsButton, 2, 0); + + lpPixmapCacheGrid->addWidget(&pixmapCacheExplainerLabel, 0, 0); + lpPixmapCacheGrid->addLayout(pixmapCacheLayout, 1, 0); + + connect(&pixmapCacheEdit, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), + &SettingsCache::setPixmapCacheSize); + connect(&networkCacheEdit, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), + &SettingsCache::setNetworkCacheSizeInMB); + connect(&networkRedirectCacheTtlEdit, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), + &SettingsCache::setNetworkRedirectCacheTtl); + + mpCacheMethodGroupBox = new QGroupBox; + mpCacheMethodGroupBox->setLayout(cacheMethodLayout); + + mpNetworkCacheGroupBox = new QGroupBox; + mpNetworkCacheGroupBox->setLayout(lpNetworkCacheGrid); + + mpImageBackupGroupBox = new QGroupBox; + mpImageBackupGroupBox->setLayout(lpImageBackupGrid); + + mpPixmapCacheGroupBox = new QGroupBox; + mpPixmapCacheGroupBox->setLayout(lpPixmapCacheGrid); + + auto *lpMainLayout = new QVBoxLayout; + + lpMainLayout->addWidget(mpCacheMethodGroupBox); + lpMainLayout->addWidget(mpNetworkCacheGroupBox); + lpMainLayout->addWidget(mpImageBackupGroupBox); + lpMainLayout->addWidget(mpPixmapCacheGroupBox); + lpMainLayout->addStretch(); + + setLayout(lpMainLayout); + + bool useNetworkCache = SettingsCache::instance().getCardPictureLoaderCacheMethod() == + CardPictureLoaderCacheMethod::CacheMethod::NETWORK_CACHE; + + mpNetworkCacheGroupBox->setEnabled(useNetworkCache); + mpImageBackupGroupBox->setEnabled(!useNetworkCache); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &StorageSettingsPage::retranslateUi); + retranslateUi(); +} + +void StorageSettingsPage::clearDownloadedPicsButtonClicked() +{ + CardPictureLoader::clearNetworkCache(); + CardPictureLoader::clearPixmapCache(); + QMessageBox::information(this, tr("Success"), tr("Cached card pictures have been reset.")); +} + +void StorageSettingsPage::clearImageBackupsButtonClicked() +{ + QString picsPath = SettingsCache::instance().getPicsPath() + "/downloadedPics"; + + QDir dir(picsPath); + bool success = dir.removeRecursively(); + + CardPictureLoader::clearPixmapCache(); + + if (success) { + QMessageBox::information(this, tr("Success"), tr("Downloaded card pictures have been reset.")); + } else { + QMessageBox::critical(this, tr("Error"), tr("One or more downloaded card pictures could not be cleared.")); + } +} + +void StorageSettingsPage::clearPixmapCacheButtonClicked() +{ + CardPictureLoader::clearPixmapCache(); + QMessageBox::information(this, tr("Success"), tr("In-memory (currently loaded) card pictures have been reset.")); +} + +void StorageSettingsPage::retranslateUi() +{ + cardPictureLoaderCacheMethodLabel.setText(tr("Card Picture Loader Caching Method:")); + + networkCacheExplainerLabel.setText( + tr("The network cache is the preferred way of storing images. Downloaded images " + "are stored here until the size of the cache exceeds the configured size. Cockatrice automatically monitors " + "this cache and deletes the least recently seen card images to ensure the cache does not exceed the " + "configured size.")); + imageBackupExplainerLabel.setText( + tr("Writing card images directly to a folder on your hard drive is another way " + "of storing images. This does not change how Cockatrice accesses or downloads " + "images. Cockatrice will NOT automatically monitor and clear this folder, so if you enable this option, it " + "is up to you to ensure sufficient available space. It should also be noted that if a provider outage " + "causes you to download the wrong picture (i.e. wrong printing) you will be stuck with it until you " + "manually delete the file, as opposed to using the network cache, which automatically rotates and thus " + "correct errors after a while.")); + pixmapCacheExplainerLabel.setText( + tr("This is the in-memory picture cache used by the application at runtime. It determines how much memory " + "(RAM) Cockatrice can use before it has to fetch card images from the hard disk again. Increasing this will " + "allow more card images to be displayed at once but shouldn't be necessary. Clearing this will make " + "Cockatrice reload all images from the network cache or the disk.")); + + clearDownloadedPicsButton.setText(tr("Delete Cached Images")); + clearBackupsButton.setText(tr("Delete Saved Images")); + clearPixmapCacheButton.setText(tr("Clear In-Memory Images")); + + mpCacheMethodGroupBox->setTitle(tr("Card Picture Loader Cache Method")); + mpNetworkCacheGroupBox->setTitle(tr("Network Cache")); + mpImageBackupGroupBox->setTitle(tr("Filesystem")); + mpPixmapCacheGroupBox->setTitle(tr("In-Memory Picture Cache")); + + networkCacheLabel.setText(tr("Network Cache Size:")); + networkCacheEdit.setToolTip(tr("On-disk cache for downloaded pictures")); + networkRedirectCacheTtlLabel.setText(tr("Redirect Cache TTL:")); + networkRedirectCacheTtlEdit.setToolTip(tr("How long cached redirects for urls are valid for.")); + pixmapCacheLabel.setText(tr("Picture Cache Size:")); + pixmapCacheEdit.setToolTip(tr("In-memory cache for pictures not currently on screen")); + localCardImageStorageNamingSchemeLabel.setText(tr("Naming scheme:")); + + networkRedirectCacheTtlEdit.setSuffix(" " + tr("Day(s)")); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/storage_settings_page.h b/cockatrice/src/interface/widgets/settings_page/storage_settings_page.h new file mode 100644 index 000000000..5c8a00981 --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/storage_settings_page.h @@ -0,0 +1,50 @@ +#ifndef COCKATRICE_STORAGE_SETTINGS_PAGE_H +#define COCKATRICE_STORAGE_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include + +class StorageSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +public: + StorageSettingsPage(); + void retranslateUi() override; + +private slots: + void clearDownloadedPicsButtonClicked(); + void clearImageBackupsButtonClicked(); + void clearPixmapCacheButtonClicked(); + +private: + QPushButton clearDownloadedPicsButton; + QPushButton clearBackupsButton; + QPushButton clearPixmapCacheButton; + + QGroupBox *mpCacheMethodGroupBox; + QGroupBox *mpNetworkCacheGroupBox; + QGroupBox *mpImageBackupGroupBox; + QGroupBox *mpPixmapCacheGroupBox; + + QLabel networkCacheExplainerLabel; + QLabel imageBackupExplainerLabel; + QLabel pixmapCacheExplainerLabel; + + QLabel cardPictureLoaderCacheMethodLabel; + QComboBox *cardPictureLoaderCacheMethodComboBox; + QLabel networkCacheLabel; + QSpinBox networkCacheEdit; + QLabel networkRedirectCacheTtlLabel; + QSpinBox networkRedirectCacheTtlEdit; + QSpinBox pixmapCacheEdit; + QLabel pixmapCacheLabel; + QLabel localCardImageStorageNamingSchemeLabel; + QComboBox *localCardImageStorageNamingSchemeComboBox; +}; + +#endif // COCKATRICE_STORAGE_SETTINGS_PAGE_H diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp new file mode 100644 index 000000000..dfa736a1a --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp @@ -0,0 +1,234 @@ +#include "user_interface_settings_page.h" + +#include "../../../client/settings/cache_settings.h" +#include "../interface/widgets/tabs/tab_supervisor.h" + +#include + +enum visualDeckStoragePromptForConversionIndex +{ + visualDeckStoragePromptForConversionIndexNone, + visualDeckStoragePromptForConversionIndexPrompt, + visualDeckStoragePromptForConversionIndexAlways +}; + +UserInterfaceSettingsPage::UserInterfaceSettingsPage() +{ + // general settings and notification settings + notificationsEnabledCheckBox.setChecked(SettingsCache::instance().getNotificationsEnabled()); + connect(¬ificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setNotificationsEnabled); + connect(¬ificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, this, + &UserInterfaceSettingsPage::setNotificationEnabled); + + specNotificationsEnabledCheckBox.setChecked(SettingsCache::instance().getSpectatorNotificationsEnabled()); + specNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled()); + connect(&specNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setSpectatorNotificationsEnabled); + + buddyConnectNotificationsEnabledCheckBox.setChecked( + SettingsCache::instance().getBuddyConnectNotificationsEnabled()); + buddyConnectNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled()); + connect(&buddyConnectNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setBuddyConnectNotificationsEnabled); + + doubleClickToPlayCheckBox.setChecked(SettingsCache::instance().getDoubleClickToPlay()); + connect(&doubleClickToPlayCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setDoubleClickToPlay); + + clickPlaysAllSelectedCheckBox.setChecked(SettingsCache::instance().getClickPlaysAllSelected()); + connect(&clickPlaysAllSelectedCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setClickPlaysAllSelected); + + playToStackCheckBox.setChecked(SettingsCache::instance().getPlayToStack()); + connect(&playToStackCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPlayToStack); + + doNotDeleteArrowsInSubPhasesCheckBox.setChecked(SettingsCache::instance().getDoNotDeleteArrowsInSubPhases()); + connect(&doNotDeleteArrowsInSubPhasesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setDoNotDeleteArrowsInSubPhases); + + closeEmptyCardViewCheckBox.setChecked(SettingsCache::instance().getCloseEmptyCardView()); + connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setCloseEmptyCardView); + + focusCardViewSearchBarCheckBox.setChecked(SettingsCache::instance().getFocusCardViewSearchBar()); + connect(&focusCardViewSearchBarCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setFocusCardViewSearchBar); + + annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens()); + connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setAnnotateTokens); + + showDragSelectionCountCheckBox.setChecked(SettingsCache::instance().getShowDragSelectionCount()); + connect(&showDragSelectionCountCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowDragSelectionCount); + + showTotalSelectionCountCheckBox.setChecked(SettingsCache::instance().getShowTotalSelectionCount()); + connect(&showTotalSelectionCountCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowTotalSelectionCount); + + useTearOffMenusCheckBox.setChecked(SettingsCache::instance().getUseTearOffMenus()); + connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); + + auto *generalGrid = new QGridLayout; + generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0); + generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); + generalGrid->addWidget(&playToStackCheckBox, 2, 0); + generalGrid->addWidget(&doNotDeleteArrowsInSubPhasesCheckBox, 3, 0); + generalGrid->addWidget(&closeEmptyCardViewCheckBox, 4, 0); + generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 5, 0); + generalGrid->addWidget(&annotateTokensCheckBox, 6, 0); + generalGrid->addWidget(&showDragSelectionCountCheckBox, 7, 0); + generalGrid->addWidget(&showTotalSelectionCountCheckBox, 8, 0); + generalGrid->addWidget(&useTearOffMenusCheckBox, 9, 0); + + generalGroupBox = new QGroupBox; + generalGroupBox->setLayout(generalGrid); + + auto *notificationsGrid = new QGridLayout; + notificationsGrid->addWidget(¬ificationsEnabledCheckBox, 0, 0); + notificationsGrid->addWidget(&specNotificationsEnabledCheckBox, 1, 0); + notificationsGrid->addWidget(&buddyConnectNotificationsEnabledCheckBox, 2, 0); + + notificationsGroupBox = new QGroupBox; + notificationsGroupBox->setLayout(notificationsGrid); + + // animation settings + tapAnimationCheckBox.setChecked(SettingsCache::instance().getTapAnimation()); + connect(&tapAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setTapAnimation); + + auto *animationGrid = new QGridLayout; + animationGrid->addWidget(&tapAnimationCheckBox, 0, 0); + + animationGroupBox = new QGroupBox; + animationGroupBox->setLayout(animationGrid); + + // deck editor settings + openDeckInNewTabCheckBox.setChecked(SettingsCache::instance().getOpenDeckInNewTab()); + connect(&openDeckInNewTabCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setOpenDeckInNewTab); + + visualDeckStorageInGameCheckBox.setChecked(SettingsCache::instance().getVisualDeckStorageInGame()); + connect(&visualDeckStorageInGameCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setVisualDeckStorageInGame); + + visualDeckStorageSelectionAnimationCheckBox.setChecked( + SettingsCache::instance().getVisualDeckStorageSelectionAnimation()); + connect(&visualDeckStorageSelectionAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setVisualDeckStorageSelectionAnimation); + + visualDeckStoragePromptForConversionSelector.addItem(""); // these will be set in retranslateUI + visualDeckStoragePromptForConversionSelector.addItem(""); + visualDeckStoragePromptForConversionSelector.addItem(""); + if (SettingsCache::instance().getVisualDeckStoragePromptForConversion()) { + visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexPrompt); + } else if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) { + visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexAlways); + } else { + visualDeckStoragePromptForConversionSelector.setCurrentIndex(visualDeckStoragePromptForConversionIndexNone); + } + connect(&visualDeckStoragePromptForConversionSelector, QOverload::of(&QComboBox::currentIndexChanged), this, + [](int index) { + SettingsCache::instance().setVisualDeckStoragePromptForConversion( + index == visualDeckStoragePromptForConversionIndexPrompt); + SettingsCache::instance().setVisualDeckStorageAlwaysConvert( + index == visualDeckStoragePromptForConversionIndexAlways); + }); + + defaultDeckEditorTypeSelector.addItem(""); // these will be set in retranslateUI + defaultDeckEditorTypeSelector.addItem(""); + defaultDeckEditorTypeSelector.setCurrentIndex(SettingsCache::instance().getDefaultDeckEditorType()); + connect(&defaultDeckEditorTypeSelector, QOverload::of(&QComboBox::currentIndexChanged), + &SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType); + + auto *deckEditorGrid = new QGridLayout; + deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); + deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0); + deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0); + deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0); + deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1); + deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0); + deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1); + + deckEditorGroupBox = new QGroupBox; + deckEditorGroupBox->setLayout(deckEditorGrid); + + // replay settings + rewindBufferingMsBox.setRange(0, 9999); + rewindBufferingMsBox.setValue(SettingsCache::instance().getRewindBufferingMs()); + connect(&rewindBufferingMsBox, qOverload(&QSpinBox::valueChanged), &SettingsCache::instance(), + &SettingsCache::setRewindBufferingMs); + + auto *replayGrid = new QGridLayout; + replayGrid->addWidget(&rewindBufferingMsLabel, 0, 0, 1, 1); + replayGrid->addWidget(&rewindBufferingMsBox, 0, 1, 1, 1); + + replayGroupBox = new QGroupBox; + replayGroupBox->setLayout(replayGrid); + + // putting it all together + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(generalGroupBox); + mainLayout->addWidget(notificationsGroupBox); + mainLayout->addWidget(animationGroupBox); + mainLayout->addWidget(deckEditorGroupBox); + mainLayout->addWidget(replayGroupBox); + mainLayout->addStretch(); + + setLayout(mainLayout); + + connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &UserInterfaceSettingsPage::retranslateUi); + retranslateUi(); +} + +void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i) +{ + specNotificationsEnabledCheckBox.setEnabled(i != 0); + buddyConnectNotificationsEnabledCheckBox.setEnabled(i != 0); + if (i == 0) { + specNotificationsEnabledCheckBox.setChecked(false); + buddyConnectNotificationsEnabledCheckBox.setChecked(false); + } +} + +void UserInterfaceSettingsPage::retranslateUi() +{ + generalGroupBox->setTitle(tr("General interface settings")); + doubleClickToPlayCheckBox.setText(tr("&Double-click cards to play them (instead of single-click)")); + clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)")); + playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default")); + doNotDeleteArrowsInSubPhasesCheckBox.setText(tr("Do not delete &arrows inside of subphases")); + closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed")); + focusCardViewSearchBarCheckBox.setText(tr("Auto focus search bar when card view window is opened")); + annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); + showDragSelectionCountCheckBox.setText(tr("Show selection counter during drag selection")); + showTotalSelectionCountCheckBox.setText(tr("Show total selection counter")); + useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); + notificationsGroupBox->setTitle(tr("Notifications settings")); + notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar")); + specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating")); + buddyConnectNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar when users in your buddy list connect")); + animationGroupBox->setTitle(tr("Animation settings")); + tapAnimationCheckBox.setText(tr("&Tap/untap animation")); + deckEditorGroupBox->setTitle(tr("Deck editor/storage settings")); + openDeckInNewTabCheckBox.setText(tr("Open deck in new tab by default")); + visualDeckStorageInGameCheckBox.setText(tr("Use visual deck storage in game lobby")); + visualDeckStorageSelectionAnimationCheckBox.setText(tr("Use selection animation for Visual Deck Storage")); + visualDeckStoragePromptForConversionLabel.setText( + tr("When adding a tag in the visual deck storage to a .txt deck:")); + visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexNone, + tr("do nothing")); + visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexPrompt, + tr("ask to convert to .cod")); + visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways, + tr("always convert to .cod")); + defaultDeckEditorTypeLabel.setText(tr("Default deck editor type")); + defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor")); + defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor")); + replayGroupBox->setTitle(tr("Replay settings")); + rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:")); + rewindBufferingMsBox.setSuffix(" ms"); +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h new file mode 100644 index 000000000..6dd43ceae --- /dev/null +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h @@ -0,0 +1,54 @@ +#ifndef COCKATRICE_USER_INTERFACE_SETTINGS_PAGE_H +#define COCKATRICE_USER_INTERFACE_SETTINGS_PAGE_H + +#include "abstract_settings_page.h" + +#include +#include +#include +#include +#include +#include + +class UserInterfaceSettingsPage : public AbstractSettingsPage +{ + Q_OBJECT +private slots: + void setNotificationEnabled(QT_STATE_CHANGED_T); + +private: + QCheckBox notificationsEnabledCheckBox; + QCheckBox specNotificationsEnabledCheckBox; + QCheckBox buddyConnectNotificationsEnabledCheckBox; + QCheckBox doubleClickToPlayCheckBox; + QCheckBox clickPlaysAllSelectedCheckBox; + QCheckBox playToStackCheckBox; + QCheckBox doNotDeleteArrowsInSubPhasesCheckBox; + QCheckBox closeEmptyCardViewCheckBox; + QCheckBox focusCardViewSearchBarCheckBox; + QCheckBox annotateTokensCheckBox; + QCheckBox showDragSelectionCountCheckBox; + QCheckBox showTotalSelectionCountCheckBox; + QCheckBox useTearOffMenusCheckBox; + QCheckBox tapAnimationCheckBox; + QCheckBox openDeckInNewTabCheckBox; + QLabel visualDeckStoragePromptForConversionLabel; + QComboBox visualDeckStoragePromptForConversionSelector; + QCheckBox visualDeckStorageInGameCheckBox; + QCheckBox visualDeckStorageSelectionAnimationCheckBox; + QLabel defaultDeckEditorTypeLabel; + QComboBox defaultDeckEditorTypeSelector; + QLabel rewindBufferingMsLabel; + QSpinBox rewindBufferingMsBox; + QGroupBox *generalGroupBox; + QGroupBox *notificationsGroupBox; + QGroupBox *animationGroupBox; + QGroupBox *deckEditorGroupBox; + QGroupBox *replayGroupBox; + +public: + UserInterfaceSettingsPage(); + void retranslateUi() override; +}; + +#endif // COCKATRICE_USER_INTERFACE_SETTINGS_PAGE_H