mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-10 20:13:24 -08:00
Address review findings
This commit is contained in:
parent
178d392361
commit
7c90211247
13 changed files with 229 additions and 67 deletions
|
|
@ -169,10 +169,28 @@ SettingsCache::SettingsCache()
|
|||
cardsDisplaySettings = new CardsDisplaySettings(settingsPath, this);
|
||||
interfaceSettings = new InterfaceSettings(settingsPath, this);
|
||||
if (hadExistingSettings) {
|
||||
// Installs that predate the tutorial have no tutorial keys at all. Seed each
|
||||
// per-tutorial key from the legacy flag, but never overwrite a key that is already
|
||||
// there. Fresh installs keep the false defaults so every tutorial runs.
|
||||
QSettings iface(settingsPath + "interface.ini", QSettings::IniFormat);
|
||||
if (!iface.contains("interface/tutorialCompleted")) {
|
||||
const bool hasLegacyCompleted = iface.contains("interface/tutorialCompleted");
|
||||
const bool hasHomeCompleted = iface.contains("interface/tutorialHomeCompleted");
|
||||
const bool hasDeckEditorCompleted = iface.contains("interface/tutorialDeckEditorCompleted");
|
||||
const bool hasGameCompleted = iface.contains("interface/tutorialGameCompleted");
|
||||
if (!hasLegacyCompleted) {
|
||||
// No legacy key means an install from before the tutorial existed: treat it as done.
|
||||
interfaceSettings->setTutorialCompleted(true);
|
||||
}
|
||||
const bool legacyCompleted = interfaceSettings->getTutorialCompleted();
|
||||
if (!hasHomeCompleted) {
|
||||
interfaceSettings->setHomeTutorialCompleted(legacyCompleted);
|
||||
}
|
||||
if (!hasDeckEditorCompleted) {
|
||||
interfaceSettings->setDeckEditorTutorialCompleted(legacyCompleted);
|
||||
}
|
||||
if (!hasGameCompleted) {
|
||||
interfaceSettings->setGameTutorialCompleted(legacyCompleted);
|
||||
}
|
||||
}
|
||||
deckEditorSettings = new DeckEditorSettings(settingsPath, this);
|
||||
pathsSettings = new PathsSettings(settingsPath, this);
|
||||
|
|
|
|||
|
|
@ -98,13 +98,13 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
|||
connect(&SettingsCache::instance().appearance(), &AppearanceSettings::homeTabBackgroundShuffleFrequencyChanged,
|
||||
this, &HomeWidget::onBackgroundShuffleFrequencyChanged);
|
||||
|
||||
tutorialController = new TutorialController(this);
|
||||
tutorialController = new TutorialController(TutorialId::Home, 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!"};
|
||||
sequence.addStep({connectButton, tr("Connect to a server to play here!")});
|
||||
auto vdeStep = TutorialStep{visualDeckEditorButton, tr("Create a new deck from cards in the database here!")};
|
||||
vdeStep.requiresInteraction = true;
|
||||
vdeStep.allowClickThrough = true;
|
||||
vdeStep.validationHint = "Open the deck editor to try it out!";
|
||||
vdeStep.validationHint = tr("Open the deck editor to try it out!");
|
||||
vdeStep.validationTiming = ValidationTiming::OnSignal;
|
||||
vdeStep.autoAdvanceOnValid = true;
|
||||
vdeStep.validator = []() { return true; };
|
||||
|
|
@ -114,14 +114,14 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
|||
};
|
||||
|
||||
sequence.addStep(vdeStep);
|
||||
sequence.addStep({visualDeckStorageButton, "Browse the decks in your local collection."});
|
||||
sequence.addStep({visualDatabaseDisplayButton, "View the card database here."});
|
||||
sequence.addStep({visualDeckStorageButton, tr("Browse the decks in your local collection.")});
|
||||
sequence.addStep({visualDatabaseDisplayButton, tr("View the card database here.")});
|
||||
sequence.addStep(
|
||||
{edhrecButton, "Browse EDHRec, an external service designed to provide card recommendations for decks."});
|
||||
sequence.addStep({archidektButton, "Browse Archidekt, an external service that allows users to store "
|
||||
"decklists and import them to your local collection."});
|
||||
sequence.addStep({replayButton, "View replays of your past games here."});
|
||||
sequence.addStep({exitButton, "Exit the application."});
|
||||
{edhrecButton, tr("Browse EDHRec, an external service designed to provide card recommendations for decks.")});
|
||||
sequence.addStep({archidektButton, tr("Browse Archidekt, an external service that allows users to store "
|
||||
"decklists and import them to your local collection.")});
|
||||
sequence.addStep({replayButton, tr("View replays of your past games here.")});
|
||||
sequence.addStep({exitButton, tr("Exit the application.")});
|
||||
tutorialController->addSequence(sequence);
|
||||
|
||||
// Lambda is cleaner to read than overloading this
|
||||
|
|
@ -171,7 +171,7 @@ void HomeWidget::retranslateUi()
|
|||
void HomeWidget::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
if (!tutorialStarted && !SettingsCache::instance().userInterface().getTutorialCompleted()) {
|
||||
if (!tutorialStarted && !SettingsCache::instance().userInterface().getHomeTutorialCompleted()) {
|
||||
tutorialStarted = true;
|
||||
// Start on next event loop iteration so everything is fully painted
|
||||
QTimer::singleShot(3, tutorialController, [this] { tutorialController->start(); });
|
||||
|
|
|
|||
|
|
@ -68,10 +68,10 @@ void BubbleWidget::setText(const QString &text)
|
|||
void BubbleWidget::setProgress(int stepNum, int totalSteps, int overallStep, int overallTotal)
|
||||
{
|
||||
// Per-sequence progress
|
||||
counterLabel->setText(QString("Step %1 of %2").arg(stepNum).arg(totalSteps));
|
||||
counterLabel->setText(tr("Step %1 of %2").arg(stepNum).arg(totalSteps));
|
||||
|
||||
// Overall progress across all sequences
|
||||
progressLabel->setText(QString("(%1 of %2 total)").arg(overallStep).arg(overallTotal));
|
||||
progressLabel->setText(tr("(%1 of %2 total)").arg(overallStep).arg(overallTotal));
|
||||
progressLabel->show();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,21 @@
|
|||
#include <QTimer>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
|
||||
TutorialController::TutorialController(QWidget *_tutorializedWidget)
|
||||
: QObject(_tutorializedWidget), tutorializedWidget(_tutorializedWidget)
|
||||
namespace
|
||||
{
|
||||
/// Every controller that currently exists, across all screens, so the overlay's X can end
|
||||
/// all of them at once. Only touched from the GUI thread, which creates and destroys them.
|
||||
QList<TutorialController *> liveControllers;
|
||||
|
||||
/// True while skipTutorials() closes the controllers, so a nested call is a no-op.
|
||||
bool skippingTutorials = false;
|
||||
} // namespace
|
||||
|
||||
TutorialController::TutorialController(TutorialId _tutorialId, QWidget *_tutorializedWidget)
|
||||
: QObject(_tutorializedWidget), tutorialId(_tutorialId), tutorializedWidget(_tutorializedWidget)
|
||||
{
|
||||
liveControllers.append(this);
|
||||
|
||||
tutorialOverlay = new TutorialOverlay(tutorializedWidget->window());
|
||||
|
||||
tutorialOverlay->setWindowFlags(tutorialOverlay->windowFlags() | Qt::FramelessWindowHint);
|
||||
|
|
@ -22,12 +34,14 @@ TutorialController::TutorialController(QWidget *_tutorializedWidget)
|
|||
connect(tutorialOverlay, &TutorialOverlay::prevStep, this, &TutorialController::prevStep);
|
||||
connect(tutorialOverlay, &TutorialOverlay::nextSequence, this, &TutorialController::nextSequence);
|
||||
connect(tutorialOverlay, &TutorialOverlay::prevSequence, this, &TutorialController::prevSequence);
|
||||
connect(tutorialOverlay, &TutorialOverlay::skipTutorial, this, &TutorialController::exitTutorial);
|
||||
connect(tutorialOverlay, &TutorialOverlay::skipTutorial, this, &TutorialController::skipTutorials);
|
||||
connect(tutorialOverlay, &TutorialOverlay::targetClicked, this, &TutorialController::handleTargetClicked);
|
||||
}
|
||||
|
||||
TutorialController::~TutorialController()
|
||||
{
|
||||
liveControllers.removeAll(this);
|
||||
|
||||
// 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().
|
||||
|
|
@ -185,6 +199,9 @@ void TutorialController::prevSequence()
|
|||
|
||||
void TutorialController::exitTutorial()
|
||||
{
|
||||
if (tutorialCompleted) {
|
||||
return;
|
||||
}
|
||||
cleanupValidationMonitoring();
|
||||
if (tutorialOverlay) {
|
||||
tutorialOverlay->hide();
|
||||
|
|
@ -194,10 +211,46 @@ void TutorialController::exitTutorial()
|
|||
currentSequence = -1;
|
||||
currentStep = -1;
|
||||
tutorialCompleted = true;
|
||||
SettingsCache::instance().userInterface().setTutorialCompleted(true);
|
||||
|
||||
// Only this screen's tutorial is done; the others keep their own state.
|
||||
auto &iface = SettingsCache::instance().userInterface();
|
||||
switch (tutorialId) {
|
||||
case TutorialId::Home:
|
||||
iface.setHomeTutorialCompleted(true);
|
||||
break;
|
||||
case TutorialId::DeckEditor:
|
||||
iface.setDeckEditorTutorialCompleted(true);
|
||||
break;
|
||||
case TutorialId::Game:
|
||||
iface.setGameTutorialCompleted(true);
|
||||
break;
|
||||
}
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void TutorialController::skipTutorials()
|
||||
{
|
||||
if (skippingTutorials) {
|
||||
return;
|
||||
}
|
||||
skippingTutorials = true;
|
||||
|
||||
// The X means "I am done with the tutorial", not "close this one window", so every
|
||||
// screen is marked done — including screens with no live controller right now.
|
||||
auto &iface = SettingsCache::instance().userInterface();
|
||||
iface.setHomeTutorialCompleted(true);
|
||||
iface.setDeckEditorTutorialCompleted(true);
|
||||
iface.setGameTutorialCompleted(true);
|
||||
|
||||
// exitTutorial() only schedules deletion, so iterating a copy cannot dangle.
|
||||
const QList<TutorialController *> controllers = liveControllers;
|
||||
for (TutorialController *controller : controllers) {
|
||||
controller->exitTutorial();
|
||||
}
|
||||
|
||||
skippingTutorials = false;
|
||||
}
|
||||
|
||||
void TutorialController::runExitForCurrentStep()
|
||||
{
|
||||
if (currentSequence < 0 || currentSequence >= sequences.size()) {
|
||||
|
|
@ -269,7 +322,7 @@ void TutorialController::showStep()
|
|||
if (!step.customInteractionHint.isEmpty()) {
|
||||
tutorialOverlay->setInteractionHint(step.customInteractionHint);
|
||||
} else if (step.requiresInteraction) {
|
||||
tutorialOverlay->setInteractionHint("👆 Click the highlighted area to continue");
|
||||
tutorialOverlay->setInteractionHint(tr("👆 Click the highlighted area to continue"));
|
||||
} else {
|
||||
tutorialOverlay->setInteractionHint("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@ enum class ValidationTiming
|
|||
Manual // Only validate when explicitly triggered
|
||||
};
|
||||
|
||||
/// Which screen a controller belongs to; each tutorial tracks its own completion flag.
|
||||
enum class TutorialId
|
||||
{
|
||||
Home,
|
||||
DeckEditor,
|
||||
Game
|
||||
};
|
||||
|
||||
class TutorialController;
|
||||
|
||||
struct TutorialStep
|
||||
|
|
@ -67,7 +75,7 @@ class TutorialController : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TutorialController(QWidget *_tutorializedWidget);
|
||||
TutorialController(TutorialId _tutorialId, QWidget *_tutorializedWidget);
|
||||
~TutorialController() override;
|
||||
|
||||
void addSequence(const TutorialSequence &seq);
|
||||
|
|
@ -85,6 +93,7 @@ public slots:
|
|||
void prevSequence();
|
||||
void runExitForCurrentStep();
|
||||
void exitTutorial();
|
||||
void skipTutorials(); // Close the tutorial on every live screen and mark all three done
|
||||
void handleTargetClicked(); // Handle clicks on highlighted widget
|
||||
void attemptAdvance(); // Try to advance with validation
|
||||
void checkValidation(); // Check validation for OnChange timing
|
||||
|
|
@ -96,6 +105,7 @@ private:
|
|||
void setupValidationMonitoring(); // Setup automatic validation checking
|
||||
void cleanupValidationMonitoring(); // Cleanup validation watchers
|
||||
|
||||
TutorialId tutorialId;
|
||||
QWidget *tutorializedWidget;
|
||||
TutorialOverlay *tutorialOverlay;
|
||||
QList<TutorialSequence> sequences;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent)
|
|||
QHBoxLayout *barLayout = new QHBoxLayout(controlBar);
|
||||
barLayout->setContentsMargins(8, 4, 8, 4);
|
||||
|
||||
titleLabel = new QLabel("Tutorial", controlBar);
|
||||
titleLabel = new QLabel(tr("Tutorial"), controlBar);
|
||||
titleLabel->setStyleSheet("color:white; font-weight:bold;");
|
||||
barLayout->addWidget(titleLabel);
|
||||
barLayout->addStretch();
|
||||
|
|
@ -52,11 +52,11 @@ TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent)
|
|||
return b;
|
||||
};
|
||||
|
||||
QPushButton *prevSeq = mkBtn("⏮", "Previous chapter");
|
||||
QPushButton *prev = mkBtn("◀", "Previous step");
|
||||
nextButton = mkBtn("▶", "Next step");
|
||||
nextSeqButton = mkBtn("⏭", "Next chapter");
|
||||
QPushButton *close = mkBtn("✕", "Exit tutorial");
|
||||
QPushButton *prevSeq = mkBtn("⏮", tr("Previous chapter"));
|
||||
QPushButton *prev = mkBtn("◀", tr("Previous step"));
|
||||
nextButton = mkBtn("▶", tr("Next step"));
|
||||
nextSeqButton = mkBtn("⏭", tr("Next chapter"));
|
||||
QPushButton *close = mkBtn("✕", tr("Exit tutorial"));
|
||||
|
||||
barLayout->addWidget(prevSeq);
|
||||
barLayout->addWidget(prev);
|
||||
|
|
@ -121,12 +121,12 @@ void TutorialOverlay::setInteractive(bool interactive, bool clickThrough)
|
|||
|
||||
if (nextButton) {
|
||||
nextButton->setEnabled(!interactive);
|
||||
nextButton->setToolTip(interactive ? "Complete the highlighted action to continue" : "Next step");
|
||||
nextButton->setToolTip(interactive ? tr("Complete the highlighted action to continue") : tr("Next step"));
|
||||
}
|
||||
|
||||
if (nextSeqButton) {
|
||||
nextSeqButton->setEnabled(!interactive);
|
||||
nextSeqButton->setToolTip(interactive ? "Complete the highlighted action to continue" : "Next chapter");
|
||||
nextSeqButton->setToolTip(interactive ? tr("Complete the highlighted action to continue") : tr("Next chapter"));
|
||||
}
|
||||
|
||||
updateMask();
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor,
|
|||
|
||||
QTimer::singleShot(0, this, &TabGame::loadLayout);
|
||||
|
||||
tutorialController = new TutorialController(this);
|
||||
tutorialController = new TutorialController(TutorialId::Game, this);
|
||||
|
||||
TutorialSequence lobbySequence;
|
||||
|
||||
|
|
@ -202,7 +202,8 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor,
|
|||
void TabGame::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
if (!tutorialController || tutorialStarted || SettingsCache::instance().userInterface().getTutorialCompleted()) {
|
||||
if (!tutorialController || tutorialStarted ||
|
||||
SettingsCache::instance().userInterface().getGameTutorialCompleted()) {
|
||||
return;
|
||||
}
|
||||
// Only start once all sequences are registered so advancing can't run past the
|
||||
|
|
@ -223,7 +224,7 @@ void TabGame::finishTutorialInitialization()
|
|||
}
|
||||
|
||||
// No sequence building (and no widget access) once the tutorial is done.
|
||||
if (SettingsCache::instance().userInterface().getTutorialCompleted()) {
|
||||
if (SettingsCache::instance().userInterface().getGameTutorialCompleted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra
|
|||
loadLayout();
|
||||
filterDockWidget->setHidden(true);
|
||||
cardDatabaseDockWidget->setHidden(true);
|
||||
tutorialController = new TutorialController(this);
|
||||
tutorialController = new TutorialController(TutorialId::DeckEditor, this);
|
||||
createTutorialSequences();
|
||||
}
|
||||
|
||||
|
|
@ -88,31 +88,31 @@ void TabDeckEditorVisual::createTutorialSequences()
|
|||
|
||||
auto sequence = TutorialSequence();
|
||||
|
||||
sequence.addStep({tabContainer->tabBar(),
|
||||
"The Visual Deck Editor has multiple different functionalities.\n\nYou can cycle "
|
||||
"through them by using these tabs.\n\nLet's start with the Visual Deck View."});
|
||||
sequence.addStep(
|
||||
{tabContainer->tabBar(), tr("The Visual Deck Editor has multiple different functionalities.\n\nYou can cycle "
|
||||
"through them by using these tabs.\n\nLet's start with the Visual Deck View.")});
|
||||
sequence.addStep({tabContainer->visualDeckView,
|
||||
"The cards in your deck will be displayed here, allowing for an easy overview.\n\nLet's try "
|
||||
"adding some now, so you can see it in action!",
|
||||
tr("The cards in your deck will be displayed here, allowing for an easy overview.\n\nLet's try "
|
||||
"adding some now, so you can see it in action!"),
|
||||
[this]() { tabContainer->setCurrentWidget(tabContainer->visualDeckView); }});
|
||||
|
||||
tutorialController->addSequence(sequence);
|
||||
|
||||
auto vdeSequence = tabContainer->visualDeckView->addTutorialSteps();
|
||||
vdeSequence.addStep({tabContainer->tabBar(), "Let's look at the database tab now."});
|
||||
vdeSequence.addStep({tabContainer->tabBar(), tr("Let's look at the database tab now.")});
|
||||
tutorialController->addSequence(vdeSequence);
|
||||
|
||||
auto vddSequence = tabContainer->visualDatabaseDisplay->addTutorialSteps();
|
||||
vddSequence.steps.prepend(
|
||||
{tabContainer->visualDatabaseDisplay,
|
||||
"You can view the database here, either as card images or in the old table display "
|
||||
"style.\n\nAdditionally, there are many powerful and easy to use filters available.\n\nLet's dive in!",
|
||||
tr("You can view the database here, either as card images or in the old table display "
|
||||
"style.\n\nAdditionally, there are many powerful and easy to use filters available.\n\nLet's dive in!"),
|
||||
[this]() { tabContainer->setCurrentWidget(tabContainer->visualDatabaseDisplay); }});
|
||||
|
||||
tutorialController->addSequence(vddSequence);
|
||||
|
||||
auto analyticsSequence = tabContainer->deckAnalytics->generateTutorialSequence();
|
||||
analyticsSequence.steps.prepend({tabContainer->tabBar(), "Let's look at the analytics tab now."});
|
||||
analyticsSequence.steps.prepend({tabContainer->tabBar(), tr("Let's look at the analytics tab now.")});
|
||||
|
||||
TutorialStep analyticsConclusionStep;
|
||||
analyticsConclusionStep.targetWidget = tabContainer->tabBar();
|
||||
|
|
@ -150,7 +150,7 @@ void TabDeckEditorVisual::createTutorialSequences()
|
|||
void TabDeckEditorVisual::showEvent(QShowEvent *ev)
|
||||
{
|
||||
QWidget::showEvent(ev);
|
||||
if (!tutorialStarted && !SettingsCache::instance().userInterface().getTutorialCompleted()) {
|
||||
if (!tutorialStarted && !SettingsCache::instance().userInterface().getDeckEditorTutorialCompleted()) {
|
||||
tutorialStarted = true;
|
||||
// Start on next event loop iteration so everything is fully painted
|
||||
QTimer::singleShot(0, tutorialController, [this] { tutorialController->start(); });
|
||||
|
|
|
|||
|
|
@ -194,13 +194,13 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
|||
TutorialSequence VisualDatabaseDisplayWidget::addTutorialSteps()
|
||||
{
|
||||
auto sequence = TutorialSequence();
|
||||
sequence.addStep({colorFilterWidget, "Filter the database by colors with these controls"});
|
||||
sequence.addStep({colorFilterWidget, tr("Filter the database by colors with these controls")});
|
||||
TutorialStep displayModeStep;
|
||||
displayModeStep.targetWidget = displayModeButton;
|
||||
displayModeStep.text = tr("You can change back to the old table display-style with this button.");
|
||||
displayModeStep.allowClickThrough = true;
|
||||
sequence.addStep(displayModeStep);
|
||||
sequence.addStep({filterContainer, "Use these controls for quick access to common filters."});
|
||||
sequence.addStep({filterContainer, tr("Use these controls for quick access to common filters.")});
|
||||
|
||||
TutorialStep setFilterStep;
|
||||
setFilterStep.targetWidget = filterContainer->getSetFilterWidget();
|
||||
|
|
|
|||
|
|
@ -464,20 +464,20 @@ void VisualDeckEditorWidget::onSelectionChanged(const QItemSelection &selected,
|
|||
TutorialSequence VisualDeckEditorWidget::addTutorialSteps()
|
||||
{
|
||||
TutorialSequence sequence;
|
||||
sequence.name = "Adding Cards to Your Deck";
|
||||
sequence.name = tr("Adding Cards to Your Deck");
|
||||
|
||||
TutorialStep introStep;
|
||||
introStep.targetWidget = displayOptionsAndSearch;
|
||||
introStep.text = "There are two ways of adding cards to your deck:\n\n"
|
||||
"The first is by using the quick search bar in the deck view tab.\n"
|
||||
"This is helpful if you already know which card you would like to add "
|
||||
"and will provide name suggestions as you type.\n\n"
|
||||
"We'll look at the second way, through the database tab, later.";
|
||||
introStep.text = tr("There are two ways of adding cards to your deck:\n\n"
|
||||
"The first is by using the quick search bar in the deck view tab.\n"
|
||||
"This is helpful if you already know which card you would like to add "
|
||||
"and will provide name suggestions as you type.\n\n"
|
||||
"We'll look at the second way, through the database tab, later.");
|
||||
sequence.addStep(introStep);
|
||||
|
||||
TutorialStep searchStep;
|
||||
searchStep.targetWidget = searchBar;
|
||||
searchStep.text = "Let's try it out now!\nType the name of a card into the search bar.";
|
||||
searchStep.text = tr("Let's try it out now!\nType the name of a card into the search bar.");
|
||||
searchStep.allowClickThrough = true;
|
||||
searchStep.requiresInteraction = true;
|
||||
searchStep.autoAdvanceOnValid = true;
|
||||
|
|
@ -485,14 +485,14 @@ TutorialSequence VisualDeckEditorWidget::addTutorialSteps()
|
|||
searchStep.validator = [this]() {
|
||||
return CardDatabaseManager::query()->getCard({searchBar->text()}) != ExactCard();
|
||||
};
|
||||
searchStep.validationHint = "Please enter a valid card name.";
|
||||
searchStep.customInteractionHint = "✏️ Type a valid card name to continue";
|
||||
searchStep.validationHint = tr("Please enter a valid card name.");
|
||||
searchStep.customInteractionHint = tr("✏️ Type a valid card name to continue");
|
||||
|
||||
sequence.addStep(searchStep);
|
||||
|
||||
TutorialStep addStep;
|
||||
addStep.targetWidget = searchPushButton;
|
||||
addStep.text = "Click this button to add the card to your deck.";
|
||||
addStep.text = tr("Click this button to add the card to your deck.");
|
||||
addStep.allowClickThrough = true;
|
||||
addStep.requiresInteraction = true;
|
||||
addStep.autoAdvanceOnValid = true;
|
||||
|
|
@ -506,8 +506,8 @@ TutorialSequence VisualDeckEditorWidget::addTutorialSteps()
|
|||
|
||||
TutorialStep organizationStep;
|
||||
organizationStep.targetWidget = this;
|
||||
organizationStep.text = "Let's look at how cards are organized and displayed now.\n\nWe'll add some random cards "
|
||||
"from the database to your deck, so you can see it in action properly.";
|
||||
organizationStep.text = tr("Let's look at how cards are organized and displayed now.\n\nWe'll add some random "
|
||||
"cards from the database to your deck, so you can see it in action properly.");
|
||||
organizationStep.onExit = [this]() {
|
||||
// getCardList() counts distinct card nodes, not copies, so once every random pick only bumps an
|
||||
// existing node's count the size never grows. With fewer than ~60 distinct cards in the database
|
||||
|
|
@ -524,11 +524,12 @@ TutorialSequence VisualDeckEditorWidget::addTutorialSteps()
|
|||
|
||||
TutorialStep hoverStep;
|
||||
hoverStep.targetWidget = this;
|
||||
hoverStep.text = "Great! Take some time to explore these new cards in the current display mode.\n\nYou can select "
|
||||
"a card by clicking on it with the left mouse button.\nYou can select multiple cards by holding "
|
||||
"down CTRL or Shift.\nYou can clear the current selection by clicking on an area without a "
|
||||
"card.\nDouble-clicking a card will move it between main and sideboard.\nRight-clicking a card "
|
||||
"will remove it from the deck.\n\nYou can hover over a card to see a zoomed version of it.";
|
||||
hoverStep.text = tr("Great! Take some time to explore these new cards in the current display mode.\n\nYou can "
|
||||
"select a card by clicking on it with the left mouse button.\nYou can select multiple cards "
|
||||
"by holding down CTRL or Shift.\nYou can clear the current selection by clicking on an area "
|
||||
"without a card.\nDouble-clicking a card will move it between main and sideboard.\n"
|
||||
"Right-clicking a card will remove it from the deck.\n\nYou can hover over a card to see a "
|
||||
"zoomed version of it.");
|
||||
hoverStep.allowClickThrough = true;
|
||||
|
||||
sequence.addStep(hoverStep);
|
||||
|
|
@ -538,9 +539,9 @@ TutorialSequence VisualDeckEditorWidget::addTutorialSteps()
|
|||
TutorialStep conclusionStep;
|
||||
conclusionStep.targetWidget = this;
|
||||
conclusionStep.text =
|
||||
"Great!\n\nNow that you've learned about all the different ways of displaying the cards in "
|
||||
"your deck, it's time to move on to searching for new cards for your deck in style and ease.\n\nYou can stay "
|
||||
"on this screen to play around with the display options and advance when you are ready.";
|
||||
tr("Great!\n\nNow that you've learned about all the different ways of displaying the cards in "
|
||||
"your deck, it's time to move on to searching for new cards for your deck in style and ease.\n\nYou can "
|
||||
"stay on this screen to play around with the display options and advance when you are ready.");
|
||||
conclusionStep.allowClickThrough = true;
|
||||
|
||||
sequence.addStep(conclusionStep);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
#include "libcockatrice/utility/playmat_params.h"
|
||||
|
||||
#include <QChar>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
|
|
@ -253,6 +251,21 @@ bool InterfaceSettings::getTutorialCompleted() const
|
|||
return getValue("tutorialCompleted", QString(), QString(), false).toBool();
|
||||
}
|
||||
|
||||
bool InterfaceSettings::getHomeTutorialCompleted() const
|
||||
{
|
||||
return getValue("tutorialHomeCompleted", QString(), QString(), false).toBool();
|
||||
}
|
||||
|
||||
bool InterfaceSettings::getDeckEditorTutorialCompleted() const
|
||||
{
|
||||
return getValue("tutorialDeckEditorCompleted", QString(), QString(), false).toBool();
|
||||
}
|
||||
|
||||
bool InterfaceSettings::getGameTutorialCompleted() const
|
||||
{
|
||||
return getValue("tutorialGameCompleted", QString(), QString(), false).toBool();
|
||||
}
|
||||
|
||||
void InterfaceSettings::setUseTearOffMenus(bool _useTearOffMenus)
|
||||
{
|
||||
setValue(_useTearOffMenus, "useTearOffMenus");
|
||||
|
|
@ -481,3 +494,18 @@ void InterfaceSettings::setTutorialCompleted(bool value)
|
|||
{
|
||||
setValue(value, "tutorialCompleted");
|
||||
}
|
||||
|
||||
void InterfaceSettings::setHomeTutorialCompleted(bool value)
|
||||
{
|
||||
setValue(value, "tutorialHomeCompleted");
|
||||
}
|
||||
|
||||
void InterfaceSettings::setDeckEditorTutorialCompleted(bool value)
|
||||
{
|
||||
setValue(value, "tutorialDeckEditorCompleted");
|
||||
}
|
||||
|
||||
void InterfaceSettings::setGameTutorialCompleted(bool value)
|
||||
{
|
||||
setValue(value, "tutorialGameCompleted");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ public:
|
|||
[[nodiscard]] bool getBattlefieldFlashEnabled() const override;
|
||||
[[nodiscard]] QStringList getUserListExpandedSections() const override;
|
||||
[[nodiscard]] bool getTutorialCompleted() const;
|
||||
[[nodiscard]] bool getHomeTutorialCompleted() const;
|
||||
[[nodiscard]] bool getDeckEditorTutorialCompleted() const;
|
||||
[[nodiscard]] bool getGameTutorialCompleted() const;
|
||||
|
||||
void setUseTearOffMenus(bool _useTearOffMenus);
|
||||
void setCardViewInitialRowsMax(int _cardViewInitialRowsMax);
|
||||
|
|
@ -98,6 +101,9 @@ public:
|
|||
void setBattlefieldFlashEnabled(bool _battlefieldFlashEnabled);
|
||||
void setUserListExpandedSections(const QStringList §ions);
|
||||
void setTutorialCompleted(bool value);
|
||||
void setHomeTutorialCompleted(bool value);
|
||||
void setDeckEditorTutorialCompleted(bool value);
|
||||
void setGameTutorialCompleted(bool value);
|
||||
|
||||
signals:
|
||||
void useTearOffMenusChanged(bool state);
|
||||
|
|
|
|||
|
|
@ -543,6 +543,51 @@ TEST_F(SettingsDefaultsTest, Interface_BuddyConnectNotificationsEnabled_Default)
|
|||
ASSERT_EQ(s.getBuddyConnectNotificationsEnabled(), true);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Interface_HomeTutorialCompleted_Default)
|
||||
{
|
||||
InterfaceSettings s(settingsPath, nullptr);
|
||||
ASSERT_EQ(s.getHomeTutorialCompleted(), false);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Interface_HomeTutorialCompleted_SetAndGet)
|
||||
{
|
||||
InterfaceSettings s(settingsPath, nullptr);
|
||||
s.setHomeTutorialCompleted(true);
|
||||
ASSERT_EQ(s.getHomeTutorialCompleted(), true);
|
||||
s.setHomeTutorialCompleted(false);
|
||||
ASSERT_EQ(s.getHomeTutorialCompleted(), false);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Interface_DeckEditorTutorialCompleted_Default)
|
||||
{
|
||||
InterfaceSettings s(settingsPath, nullptr);
|
||||
ASSERT_EQ(s.getDeckEditorTutorialCompleted(), false);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Interface_DeckEditorTutorialCompleted_SetAndGet)
|
||||
{
|
||||
InterfaceSettings s(settingsPath, nullptr);
|
||||
s.setDeckEditorTutorialCompleted(true);
|
||||
ASSERT_EQ(s.getDeckEditorTutorialCompleted(), true);
|
||||
s.setDeckEditorTutorialCompleted(false);
|
||||
ASSERT_EQ(s.getDeckEditorTutorialCompleted(), false);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Interface_GameTutorialCompleted_Default)
|
||||
{
|
||||
InterfaceSettings s(settingsPath, nullptr);
|
||||
ASSERT_EQ(s.getGameTutorialCompleted(), false);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Interface_GameTutorialCompleted_SetAndGet)
|
||||
{
|
||||
InterfaceSettings s(settingsPath, nullptr);
|
||||
s.setGameTutorialCompleted(true);
|
||||
ASSERT_EQ(s.getGameTutorialCompleted(), true);
|
||||
s.setGameTutorialCompleted(false);
|
||||
ASSERT_EQ(s.getGameTutorialCompleted(), false);
|
||||
}
|
||||
|
||||
// --- DeckEditorSettings ---
|
||||
|
||||
TEST_F(SettingsDefaultsTest, DeckEditor_OpenDeckInNewTab_Default)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue