diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 9609e8f7e..1952f2884 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -108,8 +108,10 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) vdeStep.validationTiming = ValidationTiming::OnSignal; vdeStep.autoAdvanceOnValid = true; vdeStep.validator = []() { return true; }; - vdeStep.signalSource = visualDeckEditorButton; - vdeStep.signalName = SIGNAL(clicked()); + vdeStep.signalHook = [this](TutorialController *controller) { + return connect(visualDeckEditorButton, &QAbstractButton::clicked, controller, + &TutorialController::checkValidation); + }; sequence.addStep(vdeStep); sequence.addStep({visualDeckStorageButton, "Browse the decks in your local collection."}); diff --git a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp index 5e17da8cb..2b50804ee 100644 --- a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp +++ b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.cpp @@ -294,10 +294,8 @@ void TutorialController::setupValidationMonitoring() const auto &step = sequences[currentSequence].steps[currentStep]; // Handle OnSignal validation - connect to any custom signal - if (step.validationTiming == ValidationTiming::OnSignal && step.validator) { - if (step.signalSource && step.signalName) { - validationConnection = connect(step.signalSource, step.signalName, this, SLOT(checkValidation())); - } + if (step.validationTiming == ValidationTiming::OnSignal && step.signalHook) { + validationConnection = step.signalHook(this); return; } diff --git a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h index 02544c8be..9b9889dd3 100644 --- a/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h +++ b/cockatrice/src/interface/widgets/general/tutorial/tutorial_controller.h @@ -15,6 +15,8 @@ enum class ValidationTiming Manual // Only validate when explicitly triggered }; +class TutorialController; + struct TutorialStep { QWidget *targetWidget = nullptr; @@ -35,9 +37,11 @@ struct TutorialStep // Custom interaction hint (overrides default "Click to continue") QString customInteractionHint = nullptr; - // Signal-based validation (for ValidationTiming::OnSignal) - QObject *signalSource = nullptr; // Object that emits the signal - const char *signalName = nullptr; // Signal to connect to (use SIGNAL() macro) + // Establishes the connection used for signal-based validation (ValidationTiming::OnSignal). + // Called with the controller so call sites can connect a specific signal to + // TutorialController::checkValidation using pointer-to-member, which is checked at + // compile time unlike string-based SIGNAL() connections. + std::function signalHook = nullptr; }; struct TutorialSequence diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index abf9aeea4..1c5df9a4f 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -234,8 +234,10 @@ void TabGame::finishTutorialInitialization() loadDeckStep.requiresInteraction = true; loadDeckStep.autoAdvanceOnValid = true; loadDeckStep.validationTiming = ValidationTiming::OnSignal; - loadDeckStep.signalSource = game->getGameEventHandler(); - loadDeckStep.signalName = SIGNAL(logDeckSelect(PlayerLogic *, QString, int)); + loadDeckStep.signalHook = [this](TutorialController *controller) { + return connect(game->getGameEventHandler(), &GameEventHandler::logDeckSelect, controller, + &TutorialController::checkValidation); + }; loadDeckStep.validator = [] { return true; }; deckSelectSequence.addStep(loadDeckStep); @@ -247,8 +249,9 @@ void TabGame::finishTutorialInitialization() readyUpStep.requiresInteraction = true; readyUpStep.autoAdvanceOnValid = true; readyUpStep.validationTiming = ValidationTiming::OnSignal; - readyUpStep.signalSource = this; - readyUpStep.signalName = SIGNAL(localPlayerReadyStateChanged(bool)); + readyUpStep.signalHook = [this](TutorialController *controller) { + return connect(this, &TabGame::localPlayerReadyStateChanged, controller, &TutorialController::checkValidation); + }; readyUpStep.validator = [] { return true; }; deckSelectSequence.addStep(readyUpStep); @@ -304,10 +307,12 @@ void TabGame::finishTutorialInitialization() lifeCounterStep.allowClickThrough = true; lifeCounterStep.autoAdvanceOnValid = true; lifeCounterStep.validationTiming = ValidationTiming::OnSignal; - lifeCounterStep.signalSource = game->getPlayerManager() - ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) - ->getPlayerEventHandler(); - lifeCounterStep.signalName = SIGNAL(logSetCounter(PlayerLogic *, QString, int, int)); + lifeCounterStep.signalHook = [this](TutorialController *controller) { + return connect(game->getPlayerManager() + ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) + ->getPlayerEventHandler(), + &PlayerEventHandler::logSetCounter, controller, &TutorialController::checkValidation); + }; lifeCounterStep.validator = [this] { auto counters = game->getPlayerManager()->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId())->getCounters(); @@ -331,10 +336,12 @@ void TabGame::finishTutorialInitialization() diceRollStep.allowClickThrough = true; diceRollStep.autoAdvanceOnValid = true; diceRollStep.validationTiming = ValidationTiming::OnSignal; - diceRollStep.signalSource = game->getPlayerManager() - ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) - ->getPlayerEventHandler(); - diceRollStep.signalName = SIGNAL(logRollDie(PlayerLogic *, int, const QList &)); + diceRollStep.signalHook = [this](TutorialController *controller) { + return connect(game->getPlayerManager() + ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) + ->getPlayerEventHandler(), + &PlayerEventHandler::logRollDie, controller, &TutorialController::checkValidation); + }; diceRollStep.validator = [] { return true; }; diceRollStep.validationHint = tr("Roll a dice using any of these methods."); @@ -350,10 +357,12 @@ void TabGame::finishTutorialInitialization() mulliganStep.allowClickThrough = true; mulliganStep.autoAdvanceOnValid = true; mulliganStep.validationTiming = ValidationTiming::OnSignal; - mulliganStep.signalSource = game->getPlayerManager() - ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) - ->getPlayerEventHandler(); - mulliganStep.signalName = SIGNAL(logDrawCards(PlayerLogic *, int, bool)); + mulliganStep.signalHook = [this](TutorialController *controller) { + return connect(game->getPlayerManager() + ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) + ->getPlayerEventHandler(), + &PlayerEventHandler::logDrawCards, controller, &TutorialController::checkValidation); + }; mulliganStep.validator = [this] { return game->getPlayerManager() ->getActiveLocalPlayer(game->getPlayerManager()->getLocalPlayerId()) 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 2aa38f0ac..371714baa 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 @@ -207,8 +207,9 @@ TutorialSequence VisualDatabaseDisplayWidget::addTutorialSteps() setFilterStep.requiresInteraction = true; setFilterStep.autoAdvanceOnValid = true; setFilterStep.validationTiming = ValidationTiming::OnSignal; - setFilterStep.signalSource = filterModel; - setFilterStep.signalName = SIGNAL(layoutChanged()); + setFilterStep.signalHook = [this](TutorialController *controller) { + return connect(filterModel, &FilterTreeModel::layoutChanged, controller, &TutorialController::checkValidation); + }; setFilterStep.validator = [] { return true; }; sequence.addStep(setFilterStep); @@ -228,9 +229,11 @@ TutorialSequence VisualDatabaseDisplayWidget::addTutorialSteps() deckEditor->deckStateManager->clearDeck(); } }; - explorationStep.signalSource = - QtUtils::findParentOfType(this)->deckStateManager->getModel(); - explorationStep.signalName = SIGNAL(cardNodesChanged()); + auto deckModel = QtUtils::findParentOfType(this)->deckStateManager->getModel(); + explorationStep.signalHook = [deckModel](TutorialController *controller) { + return connect(deckModel, &DeckListModel::cardNodesChanged, controller, + &TutorialController::checkValidation); + }; explorationStep.validator = [this] { if (QtUtils::findParentOfType(this)) { return QtUtils::findParentOfType(this) diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp index 2c47246d6..6f85ef730 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp @@ -175,8 +175,9 @@ TutorialSequence VisualDeckDisplayOptionsWidget::generateTutorialSequence(Tutori displayTypeStep.allowClickThrough = true; displayTypeStep.requiresInteraction = true; displayTypeStep.validationTiming = ValidationTiming::OnSignal; - displayTypeStep.signalSource = displayTypeButton; - displayTypeStep.signalName = SIGNAL(clicked()); + displayTypeStep.signalHook = [this](TutorialController *controller) { + return connect(displayTypeButton, &QAbstractButton::clicked, controller, &TutorialController::checkValidation); + }; displayTypeStep.autoAdvanceOnValid = true; displayTypeStep.validator = [] { return true; }; @@ -205,8 +206,10 @@ TutorialSequence VisualDeckDisplayOptionsWidget::generateTutorialSequence(Tutori sortStep.requiresInteraction = true; sortStep.autoAdvanceOnValid = true; sortStep.validationTiming = ValidationTiming::OnSignal; - sortStep.signalSource = this; - sortStep.signalName = SIGNAL(sortCriteriaChanged(const QStringList &)); + sortStep.signalHook = [this](TutorialController *controller) { + return connect(this, &VisualDeckDisplayOptionsWidget::sortCriteriaChanged, controller, + &TutorialController::checkValidation); + }; sortStep.validator = []() { return true; }; sequence.addStep(sortStep); 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 911485eec..7672f4612 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 @@ -496,8 +496,9 @@ TutorialSequence VisualDeckEditorWidget::addTutorialSteps() addStep.requiresInteraction = true; addStep.autoAdvanceOnValid = true; addStep.validationTiming = ValidationTiming::OnSignal; - addStep.signalSource = deckListModel; - addStep.signalName = SIGNAL(cardAddedAt(const QModelIndex &)); + addStep.signalHook = [this](TutorialController *controller) { + return connect(deckListModel, &DeckListModel::cardAddedAt, controller, &TutorialController::checkValidation); + }; addStep.validator = [this]() { return deckListModel->getCardNodes().size() >= 1; }; sequence.addStep(addStep);