From 569b531113a94dbf685636c52c6ce6ec7997a4d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Sun, 9 Aug 2026 03:16:22 +0200 Subject: [PATCH] Various fixes Took 14 minutes --- .../deckview/deck_view_container.cpp | 1 - .../deckview/tabbed_deck_view_container.h | 1 + .../interface/widgets/general/home_widget.cpp | 12 ++--- .../interface/widgets/general/home_widget.h | 2 + .../general/tutorial/tutorial_controller.cpp | 52 ++++++++++--------- .../general/tutorial/tutorial_controller.h | 5 ++ .../general/tutorial/tutorial_overlay.cpp | 4 +- .../src/interface/widgets/tabs/tab_game.cpp | 43 ++++++++------- .../src/interface/widgets/tabs/tab_game.h | 4 +- .../tab_deck_editor_visual.cpp | 5 +- .../visual_database_display_widget.cpp | 23 +++----- .../visual_deck_display_options_widget.h | 1 + .../visual_deck_editor_widget.cpp | 2 +- .../settings/interface_settings.cpp | 10 ++++ .../settings/interface_settings.h | 2 + 15 files changed, 88 insertions(+), 79 deletions(-) diff --git a/cockatrice/src/game_graphics/deckview/deck_view_container.cpp b/cockatrice/src/game_graphics/deckview/deck_view_container.cpp index ec6676a0b..733da82df 100644 --- a/cockatrice/src/game_graphics/deckview/deck_view_container.cpp +++ b/cockatrice/src/game_graphics/deckview/deck_view_container.cpp @@ -101,7 +101,6 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) this, &DeckViewContainer::setVisualDeckStorageExists); switchToDeckSelectView(); - generateTutorialSequence(); } TutorialSequence DeckViewContainer::generateTutorialSequence() diff --git a/cockatrice/src/game_graphics/deckview/tabbed_deck_view_container.h b/cockatrice/src/game_graphics/deckview/tabbed_deck_view_container.h index 5685de9c0..e5ded30de 100644 --- a/cockatrice/src/game_graphics/deckview/tabbed_deck_view_container.h +++ b/cockatrice/src/game_graphics/deckview/tabbed_deck_view_container.h @@ -6,6 +6,7 @@ #ifndef TABBED_DECK_VIEW_CONTAINER_H #define TABBED_DECK_VIEW_CONTAINER_H +#include "../../interface/widgets/general/tutorial/tutorial_controller.h" #include "deck_view_container.h" #include diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index c9fd14837..7cabba7b3 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) : QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice") @@ -50,13 +50,7 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabBackgroundShuffleFrequencyChanged, this, &HomeWidget::onBackgroundShuffleFrequencyChanged); - auto mainWindow = QtUtils::findParentOfType(this); - - if (mainWindow) { - tutorialController = new TutorialController(mainWindow); - } else { - tutorialController = new TutorialController(this); - } + tutorialController = new TutorialController(this); auto sequence = TutorialSequence(); sequence.addStep({connectButton, "Connect to a server to play here!"}); auto vdeStep = TutorialStep{visualDeckEditorButton, "Create a new deck from cards in the database here!"}; @@ -92,7 +86,7 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) void HomeWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); - if (!tutorialStarted) { + if (!tutorialStarted && !SettingsCache::instance().userInterface().getTutorialCompleted()) { tutorialStarted = true; // Start on next event loop iteration so everything is fully painted QTimer::singleShot(3, tutorialController, [this] { tutorialController->start(); }); diff --git a/cockatrice/src/interface/widgets/general/home_widget.h b/cockatrice/src/interface/widgets/general/home_widget.h index d50a8d75f..50f9689c5 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.h +++ b/cockatrice/src/interface/widgets/general/home_widget.h @@ -15,6 +15,8 @@ #include #include +class TutorialController; + class HomeWidget : public QWidget { diff --git a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp index f221395e6..5e17da8cb 100644 --- a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp +++ b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp @@ -1,12 +1,14 @@ #include "tutorial_controller.h" +#include "../../../../client/settings/cache_settings.h" + #include -#include #include #include #include #include #include +#include TutorialController::TutorialController(QWidget *_tutorializedWidget) : QObject(_tutorializedWidget), tutorializedWidget(_tutorializedWidget) @@ -24,6 +26,18 @@ TutorialController::TutorialController(QWidget *_tutorializedWidget) connect(tutorialOverlay, &TutorialOverlay::targetClicked, this, &TutorialController::handleTargetClicked); } +TutorialController::~TutorialController() +{ + // The overlay is parented to the top-level window, which outlives the widget + // this controller is attached to (e.g. a closed tab), so it must be cleaned + // up explicitly when the controller goes away without exitTutorial(). + if (tutorialOverlay) { + tutorialOverlay->hide(); + tutorialOverlay->deleteLater(); + } + tutorialOverlay = nullptr; +} + void TutorialController::addSequence(const TutorialSequence &seq) { sequences.append(seq); @@ -172,12 +186,15 @@ void TutorialController::prevSequence() void TutorialController::exitTutorial() { cleanupValidationMonitoring(); - tutorialOverlay->hide(); - // TODO Maybe not the best idea: - tutorialOverlay->deleteLater(); + if (tutorialOverlay) { + tutorialOverlay->hide(); + tutorialOverlay->deleteLater(); + } + tutorialOverlay = nullptr; currentSequence = -1; currentStep = -1; tutorialCompleted = true; + SettingsCache::instance().userInterface().setTutorialCompleted(true); deleteLater(); } @@ -237,6 +254,7 @@ void TutorialController::showStep() } cleanupValidationMonitoring(); + advanceScheduled = false; const auto &step = seq.steps[currentStep]; @@ -278,13 +296,7 @@ void TutorialController::setupValidationMonitoring() // Handle OnSignal validation - connect to any custom signal if (step.validationTiming == ValidationTiming::OnSignal && step.validator) { if (step.signalSource && step.signalName) { - qInfo() << "Setting up signal-based validation for signal:" << step.signalName; validationConnection = connect(step.signalSource, step.signalName, this, SLOT(checkValidation())); - if (!validationConnection) { - qInfo() << "Warning: Failed to connect to signal" << step.signalName; - } - } else { - qInfo() << "Warning: OnSignal validation timing set but signalSource or signalName is null"; } return; } @@ -292,24 +304,17 @@ void TutorialController::setupValidationMonitoring() // Handle OnChange validation - widget-specific if (step.validationTiming == ValidationTiming::OnChange && step.validator) { if (QLineEdit *lineEdit = qobject_cast(step.targetWidget)) { - qInfo() << "Setting up validation monitoring for QLineEdit"; validationConnection = connect(lineEdit, &QLineEdit::textChanged, this, &TutorialController::checkValidation); } else if (QTextEdit *textEdit = qobject_cast(step.targetWidget)) { - qInfo() << "Setting up validation monitoring for QTextEdit"; validationConnection = connect(textEdit, &QTextEdit::textChanged, this, &TutorialController::checkValidation); } else if (QPlainTextEdit *plainText = qobject_cast(step.targetWidget)) { - qInfo() << "Setting up validation monitoring for QPlainTextEdit"; validationConnection = connect(plainText, &QPlainTextEdit::textChanged, this, &TutorialController::checkValidation); } else if (QComboBox *combo = qobject_cast(step.targetWidget)) { - qInfo() << "Setting up validation monitoring for QComboBox"; validationConnection = connect(combo, QOverload::of(&QComboBox::currentIndexChanged), this, &TutorialController::checkValidation); - } else { - qInfo() << "Warning: OnChange validation timing set but widget type not supported:" - << (step.targetWidget ? step.targetWidget->metaObject()->className() : "null"); } } } @@ -317,7 +322,6 @@ void TutorialController::setupValidationMonitoring() void TutorialController::cleanupValidationMonitoring() { if (validationConnection) { - qInfo() << "Cleaning up validation connection"; disconnect(validationConnection); validationConnection = QMetaObject::Connection(); } @@ -325,8 +329,6 @@ void TutorialController::cleanupValidationMonitoring() void TutorialController::checkValidation() { - qInfo() << "checkValidation() called"; - if (currentSequence < 0 || currentSequence >= sequences.size()) { return; } @@ -338,16 +340,18 @@ void TutorialController::checkValidation() if (step.validator) { bool isValid = step.validator(); - qInfo() << "Validation result:" << isValid; if (isValid) { // Clear any validation hints tutorialOverlay->showValidationHint(""); // Auto-advance if enabled - if (step.autoAdvanceOnValid) { - qInfo() << "Auto-advancing to next step"; - QTimer::singleShot(500, this, &TutorialController::nextStep); + if (step.autoAdvanceOnValid && !advanceScheduled) { + advanceScheduled = true; + QTimer::singleShot(500, this, [this]() { + advanceScheduled = false; + nextStep(); + }); } } } diff --git a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h index 845620128..02544c8be 100644 --- a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h +++ b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h @@ -57,6 +57,7 @@ class TutorialController : public QObject public: explicit TutorialController(QWidget *_tutorializedWidget); + ~TutorialController() override; void addSequence(const TutorialSequence &seq); void start(); @@ -95,6 +96,10 @@ private: // For OnChange validation monitoring QMetaObject::Connection validationConnection; + + // True while an auto-advance timer is pending, so repeated signal emissions + // can't queue more than one advance. + bool advanceScheduled = false; }; #endif // COCKATRICE_TUTORIAL_CONTROLLER_H diff --git a/cockatrice/src/interface/widgets/general/tutorial/tutorial_overlay.cpp b/cockatrice/src/interface/widgets/general/tutorial/tutorial_overlay.cpp index 40e9607a1..271556fad 100644 --- a/cockatrice/src/interface/widgets/general/tutorial/tutorial_overlay.cpp +++ b/cockatrice/src/interface/widgets/general/tutorial/tutorial_overlay.cpp @@ -300,7 +300,9 @@ void TutorialOverlay::recomputeLayout() return; } - resize(parentWidget()->window()->geometry().size()); + // The overlay is parented to the top-level window; its client size (rect()) + // is the visible area, whereas window()->geometry() includes the frame. + resize(parentWidget()->size()); bubble->adjustSize(); diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index 2faad5481..04443e959 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -25,7 +25,6 @@ #include "../interface/window_main.h" #include "../main.h" #include "../utility/visibility_change_listener.h" -#include "libcockatrice/utility/qt_utils.h" #include "tab_supervisor.h" #include @@ -140,13 +139,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QTimer::singleShot(0, this, &TabGame::loadLayout); - auto mainWindow = QtUtils::findParentOfType(this); - - if (mainWindow) { - tutorialController = new TutorialController(mainWindow); - } else { - tutorialController = new TutorialController(this); - } + tutorialController = new TutorialController(this); TutorialSequence lobbySequence; @@ -159,7 +152,12 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, void TabGame::showEvent(QShowEvent *event) { QWidget::showEvent(event); - if (!tutorialStarted) { + if (!tutorialController || tutorialStarted || SettingsCache::instance().userInterface().getTutorialCompleted()) { + return; + } + // Only start once all sequences are registered so advancing can't run past the + // end of an incomplete sequence list. + if (tutorialInitialized) { tutorialStarted = true; // Start on next event loop iteration so everything is fully painted QTimer::singleShot(3, tutorialController, [this] { tutorialController->start(); }); @@ -189,7 +187,7 @@ void TabGame::finishTutorialInitialization() loadDeckStep.autoAdvanceOnValid = true; loadDeckStep.validationTiming = ValidationTiming::OnSignal; loadDeckStep.signalSource = game->getGameEventHandler(); - loadDeckStep.signalName = SIGNAL(logDeckSelect(Player *, QString, int)); + loadDeckStep.signalName = SIGNAL(logDeckSelect(PlayerLogic *, QString, int)); loadDeckStep.validator = [] { return true; }; deckSelectSequence.addStep(loadDeckStep); @@ -215,9 +213,6 @@ void TabGame::finishTutorialInitialization() gamePlaySequence.addStep( {gamePlayAreaWidget, tr("Welcome to your first game! It's just a singleplayer game for now to teach you the controls.")}); - gamePlaySequence.addStep( - {gamePlayAreaWidget, - tr("Welcome to your first game! It's just a singleplayer game for now to teach you the controls.")}); gamePlaySequence.addStep( {gamePlayAreaWidget, tr("Unfortunately, due to the way the game tab works, we can't highlight any specific gameplay elements but " @@ -264,7 +259,7 @@ void TabGame::finishTutorialInitialization() lifeCounterStep.signalSource = game->getPlayerManager() ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) ->getPlayerEventHandler(); - lifeCounterStep.signalName = SIGNAL(logSetCounter(Player *, QString, int, int)); + lifeCounterStep.signalName = SIGNAL(logSetCounter(PlayerLogic *, QString, int, int)); lifeCounterStep.validator = [this] { auto counters = game->getPlayerManager()->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId())->getCounters(); @@ -291,7 +286,7 @@ void TabGame::finishTutorialInitialization() diceRollStep.signalSource = game->getPlayerManager() ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) ->getPlayerEventHandler(); - diceRollStep.signalName = SIGNAL(logRollDie(Player *, int, const QList &)); + diceRollStep.signalName = SIGNAL(logRollDie(PlayerLogic *, int, const QList &)); diceRollStep.validator = [] { return true; }; diceRollStep.validationHint = tr("Roll a dice using any of these methods."); @@ -310,7 +305,7 @@ void TabGame::finishTutorialInitialization() mulliganStep.signalSource = game->getPlayerManager() ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) ->getPlayerEventHandler(); - mulliganStep.signalName = SIGNAL(logDrawCards(Player *, int, bool)); + mulliganStep.signalName = SIGNAL(logDrawCards(PlayerLogic *, int, bool)); mulliganStep.validator = [this] { return game->getPlayerManager() ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) @@ -322,11 +317,17 @@ void TabGame::finishTutorialInitialization() gamePlaySequence.addStep(mulliganStep); - gamePlaySequence.addStep({gamePlayAreaWidget, tr("")}); - gamePlaySequence.addStep({gamePlayAreaWidget, tr("")}); - - gamePlaySequence.addStep({gamePlayAreaWidget, tr("")}); tutorialController->addSequence(gamePlaySequence); + + if (tutorialStarted || SettingsCache::instance().userInterface().getTutorialCompleted()) { + return; + } + // The tab may have been shown before the local player joined; start now that + // the full sequence list is registered. + if (isVisible()) { + tutorialStarted = true; + QTimer::singleShot(3, tutorialController, [this] { tutorialController->start(); }); + } } void TabGame::connectToGameState() @@ -953,8 +954,6 @@ void TabGame::loadDeckForLocalPlayer(PlayerLogic *localPlayer, int playerId, Ser CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(deckList.getCardRefList())); deckViewContainer->playerDeckView->setDeck(deckList); localPlayer->setDeck(deckList); - - emit localPlayerDeckSelected(); } } diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.h b/cockatrice/src/interface/widgets/tabs/tab_game.h index 929d11240..e70802e82 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.h +++ b/cockatrice/src/interface/widgets/tabs/tab_game.h @@ -28,6 +28,7 @@ inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game"); class UserListProxy; class DeckViewContainer; class AbstractClient; +class TutorialController; class CardDatabase; class GameView; class GameScene; @@ -58,7 +59,7 @@ class TabGame : public Tab Q_OBJECT private: AbstractGame *game; - TutorialController *tutorialController; + TutorialController *tutorialController = nullptr; bool tutorialStarted = false; bool tutorialInitialized = false; const UserListProxy *userListProxy; @@ -130,7 +131,6 @@ private: void createDeckViewContainerWidget(bool bReplay = false); void createReplayDock(GameReplay *replay); signals: - void localPlayerDeckSelected(); void localPlayerReadyStateChanged(bool ready); void gameClosing(TabGame *tab); void containerProcessingStarted(const GameEventContext &context); diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index 6f14a2960..ec39c41bd 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include /** @@ -74,8 +75,6 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra "adding some now, so you can see it in action!", [this]() { tabContainer->setCurrentWidget(tabContainer->visualDeckView); }}); - // sequence.addStep({printingSelectorDockWidget, "Change the printings in your deck here."}); - tutorialController->addSequence(sequence); auto vdeSequence = tabContainer->visualDeckView->addTutorialSteps(); @@ -130,7 +129,7 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra void TabDeckEditorVisual::showEvent(QShowEvent *ev) { QWidget::showEvent(ev); - if (!tutorialStarted) { + if (!tutorialStarted && !SettingsCache::instance().userInterface().getTutorialCompleted()) { tutorialStarted = true; // Start on next event loop iteration so everything is fully painted QTimer::singleShot(0, tutorialController, [this] { tutorialController->start(); }); diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp index f41553f71..43971b65f 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp @@ -163,15 +163,19 @@ TutorialSequence VisualDatabaseDisplayWidget::addTutorialSteps() TutorialStep explorationStep; explorationStep.targetWidget = this; - explorationStep.text = tr( - "Try it out!\n\nWe've cleared the previous deck. Add 5 different new cards to the deck by clicking on them!"); + explorationStep.text = tr("Try it out!\n\nAdd 5 different new cards to the deck by clicking on them!"); explorationStep.allowClickThrough = true; explorationStep.requiresInteraction = true; explorationStep.autoAdvanceOnValid = true; explorationStep.validationTiming = ValidationTiming::OnSignal; if (QtUtils::findParentOfType(this)) { explorationStep.onEnter = [this] { - QtUtils::findParentOfType(this)->deckStateManager->clearDeck(); + auto deckEditor = QtUtils::findParentOfType(this); + // Only clear an empty starter deck; never destroy a deck the user may + // have opened with existing cards. + if (deckEditor->deckStateManager->getModel()->getDeckList()->getCardList().isEmpty()) { + deckEditor->deckStateManager->clearDeck(); + } }; explorationStep.signalSource = QtUtils::findParentOfType(this)->deckStateManager->getModel(); @@ -203,19 +207,6 @@ TutorialSequence VisualDatabaseDisplayWidget::addTutorialSteps() sequence.addStep(conclusionStep); - /*sequence.addStep( - {quickFilterSaveLoadWidget, "This button will let you save and load all currently applied filters to files."}); - sequence.addStep({quickFilterNameWidget, - "This button will let you apply name filters. Optionally, you can import every card in " - "your deck as a name filter and then save this as a filter using the save/load button " - "to make your own quick access collections!"}); - sequence.addStep({mainTypeFilterWidget, "Use these buttons to quickly filter by card types."}); - sequence.addStep({quickFilterSubTypeWidget, "This button will let you apply filters for card sub-types."}); - sequence.addStep( - {quickFilterSetWidget, - "This button will let you apply filters for card sets. You can also filter to the X most recent sets. " - "Filtering to a set will display all printings of a card within that set."});*/ - return sequence; } diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.h b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.h index fa512bb26..06404d781 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.h +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.h @@ -1,6 +1,7 @@ #ifndef COCKATRICE_VISUAL_DECK_DISPLAY_OPTIONS_WIDGET_H #define COCKATRICE_VISUAL_DECK_DISPLAY_OPTIONS_WIDGET_H +#include "../general/tutorial/tutorial_controller.h" #include "visual_deck_editor_widget.h" #include diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index 2862d1512..534bfbabd 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -479,7 +479,7 @@ TutorialSequence VisualDeckEditorWidget::addTutorialSteps() searchStep.allowClickThrough = true; searchStep.requiresInteraction = true; searchStep.autoAdvanceOnValid = true; - searchStep.validationTiming = ValidationTiming::OnChange; // Make sure this is set! + searchStep.validationTiming = ValidationTiming::OnChange; searchStep.validator = [this]() { return CardDatabaseManager::query()->getCard({searchBar->text()}) != ExactCard(); }; diff --git a/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp b/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp index 0fa56ee33..4e666dad0 100644 --- a/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp @@ -155,6 +155,11 @@ bool InterfaceSettings::getShowGameSelectorFilterToolbar() const return getValue("showGameSelectorFilterToolbar", QString(), QString(), true).toBool(); } +bool InterfaceSettings::getTutorialCompleted() const +{ + return getValue("tutorialCompleted", QString(), QString(), false).toBool(); +} + void InterfaceSettings::setUseTearOffMenus(bool _useTearOffMenus) { setValue(_useTearOffMenus, "useTearOffMenus"); @@ -316,3 +321,8 @@ void InterfaceSettings::setShowGameSelectorFilterToolbar(bool _showGameSelectorF setValue(_showGameSelectorFilterToolbar, "showGameSelectorFilterToolbar"); emit showGameSelectorFilterToolbarChanged(_showGameSelectorFilterToolbar); } + +void InterfaceSettings::setTutorialCompleted(bool value) +{ + setValue(value, "tutorialCompleted"); +} diff --git a/libcockatrice_settings/libcockatrice/settings/interface_settings.h b/libcockatrice_settings/libcockatrice/settings/interface_settings.h index 7ef367cb9..9833bcbb4 100644 --- a/libcockatrice_settings/libcockatrice/settings/interface_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/interface_settings.h @@ -41,6 +41,7 @@ public: [[nodiscard]] bool getShowStatusBar() const override; [[nodiscard]] bool getShowShortcuts() const override; [[nodiscard]] bool getShowGameSelectorFilterToolbar() const override; + [[nodiscard]] bool getTutorialCompleted() const; void setUseTearOffMenus(bool _useTearOffMenus); void setCardViewInitialRowsMax(int _cardViewInitialRowsMax); @@ -72,6 +73,7 @@ public: void setShowStatusBar(bool _showStatusBar); void setShowShortcuts(bool _showShortcuts); void setShowGameSelectorFilterToolbar(bool _showGameSelectorFilterToolbar); + void setTutorialCompleted(bool value); signals: void useTearOffMenusChanged(bool state);