diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 282f5a3c6..cdd2a77d0 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -408,6 +408,7 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/tab_replays.cpp src/interface/widgets/tabs/tab_room.cpp src/interface/widgets/tabs/tab_server.cpp + src/interface/widgets/tabs/tab_settings.cpp src/interface/widgets/tabs/tab_supervisor.cpp src/interface/widgets/tabs/tab_visual_database_display.cpp src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp diff --git a/cockatrice/src/interface/widgets/tabs/tab_room.cpp b/cockatrice/src/interface/widgets/tabs/tab_room.cpp index 67106ba34..42689b2e1 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_room.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_room.cpp @@ -3,7 +3,7 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" #include "../../pixel_map_generator.h" -#include "../interface/widgets/dialogs/dlg_settings.h" +#include "../interface/widgets/dialogs/settings_view.h" #include "../interface/widgets/server/chat_view/chat_view.h" #include "../interface/widgets/server/game_link.h" #include "../interface/widgets/server/game_selector.h" @@ -272,9 +272,7 @@ void TabRoom::actClearChat() void TabRoom::actOpenChatSettings() { - DlgSettings settings(this); - settings.setTab(DlgSettings::MessagesPage); - settings.exec(); + tabSupervisor->openTabSettings(SettingsView::MessagesPage); } void TabRoom::actCompleterChanged() diff --git a/cockatrice/src/interface/widgets/tabs/tab_settings.cpp b/cockatrice/src/interface/widgets/tabs/tab_settings.cpp new file mode 100644 index 000000000..80ed1925a --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/tab_settings.cpp @@ -0,0 +1,129 @@ +/** + * @file tab_settings.cpp + * @ingroup Tabs + */ +#include "tab_settings.h" + +#include "../../client/settings/cache_settings.h" +#include "../dialogs/settings_view.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +inline Q_LOGGING_CATEGORY(TabSettingsLog, "tab_settings"); + +TabSettings::TabSettings(TabSupervisor *_tabSupervisor, int pageIndex) : Tab(_tabSupervisor) +{ + settingsView = new SettingsView(this); + setCentralWidget(settingsView); + if (pageIndex >= 0) { + settingsView->setTab(pageIndex); + } +} + +void TabSettings::setTab(int pageIndex) +{ + settingsView->setTab(pageIndex); +} + +void TabSettings::retranslateUi() +{ + // Nothing to do: SettingsView retranslates itself via langChanged and the + // TabSupervisor refreshes the tab title from getTabText(). +} + +/** + * @brief Validates paths and card database before the tab closes + * + * Moved verbatim from DlgSettings::closeEvent. The "DlgSettings" translation + * context below is deliberate and preserves the existing translations. + * + * @return False when the user wants to go back and fix a setting, true otherwise + */ +bool TabSettings::closeRequest() +{ + // NOTE: "DlgSettings" contexts are intentional (translation continuity, see SettingsView). + bool showLoadError = true; + QString loadErrorMessage = QCoreApplication::translate("DlgSettings", "Unknown Error loading card database"); + LoadStatus loadStatus = CardDatabaseManager::getInstance()->getLoadStatus(); + qCInfo(TabSettingsLog) << "Card Database load status: " << loadStatus; + switch (loadStatus) { + case Ok: + showLoadError = false; + break; + case Invalid: + loadErrorMessage = QCoreApplication::translate( + "DlgSettings", "Your card database is invalid.\n\n" + "Cockatrice may not function correctly with an invalid database\n\n" + "You may need to rerun Oracle to update your card database.\n\n" + "Would you like to change your database location setting?"); + break; + case VersionTooOld: + loadErrorMessage = QCoreApplication::translate( + "DlgSettings", "Your card database version is too old.\n\n" + "This can cause problems loading card information or images\n\n" + "Usually this can be fixed by rerunning Oracle to to update your card database.\n\n" + "Would you like to change your database location setting?"); + break; + case NotLoaded: + loadErrorMessage = QCoreApplication::translate( + "DlgSettings", "Your card database did not finish loading\n\n" + "Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues with your " + "cards.xml attached\n\n" + "Would you like to change your database location setting?"); + break; + case FileError: + loadErrorMessage = QCoreApplication::translate( + "DlgSettings", "File Error loading your card database.\n\n" + "Would you like to change your database location setting?"); + break; + case NoCards: + loadErrorMessage = QCoreApplication::translate( + "DlgSettings", "Your card database was loaded but contains no cards.\n\n" + "Would you like to change your database location setting?"); + break; + default: + loadErrorMessage = QCoreApplication::translate( + "DlgSettings", "Unknown card database load status\n\n" + "Please file a ticket at https://github.com/Cockatrice/Cockatrice/issues\n\n" + "Would you like to change your database location setting?"); + + break; + } + + if (showLoadError) { + if (QMessageBox::critical(this, QCoreApplication::translate("DlgSettings", "Error"), loadErrorMessage, + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + return false; + } + } + + if (!QDir(SettingsCache::instance().paths().getDeckPath()).exists() || + SettingsCache::instance().paths().getDeckPath().isEmpty()) { + if (QMessageBox::critical(this, QCoreApplication::translate("DlgSettings", "Error"), + QCoreApplication::translate( + "DlgSettings", "The path to your deck directory is invalid. Would you like to " + "go back and set the correct path?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + return false; + } + } + + if (!QDir(SettingsCache::instance().paths().getPicsPath()).exists() || + SettingsCache::instance().paths().getPicsPath().isEmpty()) { + if (QMessageBox::critical( + this, QCoreApplication::translate("DlgSettings", "Error"), + QCoreApplication::translate("DlgSettings", "The path to your card pictures directory is invalid. " + "Would you like to go back and set the correct path?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + return false; + } + } + return close(); +} diff --git a/cockatrice/src/interface/widgets/tabs/tab_settings.h b/cockatrice/src/interface/widgets/tabs/tab_settings.h new file mode 100644 index 000000000..ef3921c68 --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/tab_settings.h @@ -0,0 +1,46 @@ +/** + * @file tab_settings.h + * @brief Settings tab hosting the shared SettingsView + * @ingroup Tabs + */ +#ifndef TAB_SETTINGS_H +#define TAB_SETTINGS_H + +#include "tab.h" + +#include +#include + +class SettingsView; +class TabSupervisor; + +/** + * @brief Singleton settings tab owned by the TabSupervisor + * + * Thin Tab frame around SettingsView: page selection, tab title and the close + * validation live here. All settings content is owned by SettingsView. + */ +class TabSettings : public Tab +{ + Q_OBJECT +private: + SettingsView *settingsView; + +public: + explicit TabSettings(TabSupervisor *_tabSupervisor, int pageIndex = -1); + /** @brief Selects the given SettingsView page (range-checked, no-op when out of range) */ + void setTab(int pageIndex); + /** @brief The hosted settings content */ + [[nodiscard]] SettingsView *getSettingsView() const + { + return settingsView; + } + void retranslateUi() override; + [[nodiscard]] QString getTabText() const override + { + return tr("Settings"); + } + bool closeRequest() override; +}; + +#endif // TAB_SETTINGS_H diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp index 2352d7d7b..e3cfb1510 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp @@ -39,6 +39,7 @@ #include "tab_report.h" #include "tab_room.h" #include "tab_server.h" +#include "tab_settings.h" #include "tab_visual_database_display.h" #include "user_level.h" #include "visual_deck_editor/tab_deck_editor_visual.h" @@ -154,9 +155,9 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/) TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *parent) : QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabHome(nullptr), - tabVisualDeckStorage(nullptr), tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr), - tabReplays(nullptr), tabAdmin(nullptr), tabCardArtRules(nullptr), tabLog(nullptr), tabReport(nullptr), - tabModeration(nullptr), tabDeveloper(nullptr), isLocalGame(false) + tabVisualDeckStorage(nullptr), tabServer(nullptr), tabSettings(nullptr), tabAccount(nullptr), + tabDeckStorage(nullptr), tabReplays(nullptr), tabAdmin(nullptr), tabCardArtRules(nullptr), tabLog(nullptr), + tabReport(nullptr), tabModeration(nullptr), tabDeveloper(nullptr), isLocalGame(false) { setElideMode(Qt::ElideRight); setMovable(true); @@ -293,6 +294,7 @@ void TabSupervisor::retranslateUi() // tabs QList tabs; tabs.append(tabServer); + tabs.append(tabSettings); tabs.append(tabReplays); tabs.append(tabDeckStorage); tabs.append(tabAdmin); @@ -753,6 +755,25 @@ void TabSupervisor::openTabServer() aTabServer->setChecked(true); } +/** + * @brief Opens the singleton settings tab, creating it on first use + * + * @param pageIndex Initial SettingsView page, or -1 to keep the current page + * @return The settings tab + */ +TabSettings *TabSupervisor::openTabSettings(int pageIndex) +{ + if (!tabSettings) { + tabSettings = new TabSettings(this, pageIndex); + myAddTab(tabSettings, nullptr); + connect(tabSettings, &QObject::destroyed, this, [this] { tabSettings = nullptr; }); + } else if (pageIndex >= 0) { + tabSettings->setTab(pageIndex); + } + setCurrentWidget(tabSettings); + return tabSettings; +} + void TabSupervisor::actTabAccount(bool checked) { SettingsCache::instance().tabs().setTabAccountOpen(checked); diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h index 9c7dc48d1..6d1219e33 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h @@ -41,6 +41,7 @@ class UserListManager; class AbstractClient; class Tab; class TabServer; +class TabSettings; class TabRoom; class TabHome; class TabGame; @@ -108,6 +109,7 @@ private: TabHome *tabHome; TabDeckStorageVisual *tabVisualDeckStorage; TabServer *tabServer; + TabSettings *tabSettings; TabAccount *tabAccount; TabDeckStorage *tabDeckStorage; TabReplays *tabReplays; @@ -214,6 +216,7 @@ public slots: void actTabVisualDeckStorage(bool checked); void actTabReplays(bool checked); void openTabServer(); + TabSettings *openTabSettings(int pageIndex = -1); void addRoomTab(const ServerInfo_Room &info, bool setCurrent); void openTabPublicDecks(const QString &userName); private slots: diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp index 4f363181e..c090d97e4 100644 --- a/cockatrice/src/interface/window_main.cpp +++ b/cockatrice/src/interface/window_main.cpp @@ -26,14 +26,15 @@ #include "../interface/widgets/dialogs/dlg_edit_tokens.h" #include "../interface/widgets/dialogs/dlg_local_game_options.h" #include "../interface/widgets/dialogs/dlg_manage_sets.h" -#include "../interface/widgets/dialogs/dlg_settings.h" #include "../interface/widgets/dialogs/dlg_startup_card_check.h" #include "../interface/widgets/dialogs/dlg_tip_of_the_day.h" #include "../interface/widgets/dialogs/dlg_update.h" #include "../interface/widgets/dialogs/dlg_view_log.h" +#include "../interface/widgets/dialogs/settings_view.h" #include "../interface/widgets/onboarding/first_run_wizard.h" #include "../interface/widgets/settings_page/general_settings_page.h" #include "../interface/widgets/tabs/tab_server.h" +#include "../interface/widgets/tabs/tab_settings.h" #include "../interface/widgets/tabs/tab_supervisor.h" #include "../main.h" #include "abstract_client.h" @@ -271,10 +272,13 @@ void MainWindow::actFullScreen(bool checked) void MainWindow::actSettings() { - DlgSettings dlg(this); - auto *generalPage = qobject_cast(dlg.page(DlgSettings::GeneralPage)); - connect(generalPage, &GeneralSettingsPage::cardDatabaseUpdateRequested, this, &MainWindow::actCheckCardUpdates); - dlg.exec(); + TabSettings *settingsTab = tabSupervisor->openTabSettings(); + auto *generalPage = + qobject_cast(settingsTab->getSettingsView()->page(SettingsView::GeneralPage)); + if (generalPage) { + connect(generalPage, &GeneralSettingsPage::cardDatabaseUpdateRequested, this, &MainWindow::actCheckCardUpdates, + Qt::UniqueConnection); + } } void MainWindow::actExit()