[App] Add onboarding wizard (#7064)
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions

* [App] Add onboarding wizard

Took 10 minutes

Took 3 minutes

Took 7 minutes

Took 9 minutes

Took 2 minutes

Took 1 minute


Took 7 minutes

* Adjust CI

Took 14 minutes

Took 56 seconds

Took 2 seconds

Took 3 seconds

* Adjust CI again

Took 14 minutes

Took 2 seconds

* Comments and fixes

Took 9 seconds


Took 1 minute

* Rebase.

Took 5 minutes

Took 50 seconds

Took 15 seconds

* Comments.

Took 7 minutes

* CI lol

Took 3 minutes

* CI again lol

Took 4 minutes

* Drop some settings, add some new ones.

Took 19 minutes

* Resize when expanding section

Took 4 minutes

Took 3 minutes

Took 7 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-24 23:05:58 +02:00 • committed by GitHub
parent 815c5987b4
commit 2b7b4e8168
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 3207 additions and 336 deletions

View file

@ -0,0 +1,173 @@
#include "preferences_setup_page.h"
#include "../../client/settings/cache_settings.h"
#include "../../client/sound_engine.h"
#include "libcockatrice/settings/interface_settings.h"
#include "libcockatrice/settings/sound_settings.h"
#include "libcockatrice/settings/tabs_settings.h"
#include <QCheckBox>
#include <QComboBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QScrollArea>
#include <QVBoxLayout>
namespace
{
// The server destinations are omitted: during first run their tabs are not
// open yet, and the wizard offers no way to fill in the server/room details.
QList<StartupTab> wizardStartupTabOrder()
{
return {StartupTabHome, StartupTabVisualDeckStorage, StartupTabDeckStorage,
StartupTabReplays, StartupTabDeckEditor, StartupTabVisualDeckEditor};
}
} // namespace
PreferencesSetupPage::PreferencesSetupPage(QWidget *parent) : FirstRunWizardPage(parent)
{
auto *content = new QWidget;
auto *contentLayout = new QVBoxLayout(content);
gameplayGroup = new QGroupBox(content);
auto *gameplayLayout = new QVBoxLayout(gameplayGroup);
contentLayout->addWidget(gameplayGroup);
doubleClickToPlayCheckBox = new QCheckBox(gameplayGroup);
horizontalHandCheckBox = new QCheckBox(gameplayGroup);
playToStackCheckBox = new QCheckBox(gameplayGroup);
gameplayLayout->addWidget(doubleClickToPlayCheckBox);
gameplayLayout->addWidget(horizontalHandCheckBox);
gameplayLayout->addWidget(playToStackCheckBox);
notificationsGroup = new QGroupBox(content);
auto *notificationsLayout = new QVBoxLayout(notificationsGroup);
contentLayout->addWidget(notificationsGroup);
notificationsEnabledCheckBox = new QCheckBox(notificationsGroup);
soundEnabledCheckBox = new QCheckBox(notificationsGroup);
notificationsLayout->addWidget(notificationsEnabledCheckBox);
notificationsLayout->addWidget(soundEnabledCheckBox);
startupGroup = new QGroupBox(content);
auto *startupForm = new QFormLayout(startupGroup);
contentLayout->addWidget(startupGroup);
startupTabLabel = new QLabel(startupGroup);
startupTabSelector = new QComboBox(startupGroup);
startupTabSelector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
for (StartupTab tab : wizardStartupTabOrder()) {
startupTabSelector->addItem(QString(), tab); // texts set in retranslateUi
}
startupForm->addRow(startupTabLabel, startupTabSelector);
contentLayout->addStretch();
auto *scrollArea = new QScrollArea(this);
scrollArea->setWidget(content);
scrollArea->setWidgetResizable(true);
scrollArea->setFrameShape(QFrame::NoFrame);
auto *layout = new QVBoxLayout(this);
layout->addWidget(scrollArea);
SettingsCache &settings = SettingsCache::instance();
connect(doubleClickToPlayCheckBox, &QCheckBox::toggled, &settings.userInterface(),
&InterfaceSettings::setDoubleClickToPlay);
connect(horizontalHandCheckBox, &QCheckBox::toggled, &settings.userInterface(),
&InterfaceSettings::setHorizontalHand);
connect(playToStackCheckBox, &QCheckBox::toggled, &settings.userInterface(), &InterfaceSettings::setPlayToStack);
connect(notificationsEnabledCheckBox, &QCheckBox::toggled, &settings.userInterface(),
&InterfaceSettings::setNotificationsEnabled);
connect(soundEnabledCheckBox, &QCheckBox::toggled, &settings.sound(), &SoundSettings::setSoundEnabled);
connect(soundEnabledCheckBox, &QCheckBox::toggled, soundEngine, &SoundEngine::testSound);
connect(startupTabSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (index < 0) {
return;
}
SettingsCache::instance().tabs().setStartupTabIndex(startupTabSelector->itemData(index).toInt());
});
retranslateUi();
}
void PreferencesSetupPage::initializePage()
{
SettingsCache &settings = SettingsCache::instance();
doubleClickToPlayCheckBox->setChecked(settings.userInterface().getDoubleClickToPlay());
horizontalHandCheckBox->setChecked(settings.userInterface().getHorizontalHand());
playToStackCheckBox->setChecked(settings.userInterface().getPlayToStack());
notificationsEnabledCheckBox->setChecked(settings.userInterface().getNotificationsEnabled());
soundEnabledCheckBox->setChecked(settings.sound().getSoundEnabled());
startupTabSelector->setCurrentIndex(startupTabSelector->findData(settings.tabs().getStartupTabIndex()));
}
bool PreferencesSetupPage::isSkippable() const
{
return true;
}
QString PreferencesSetupPage::stepTitle() const
{
return tr("A Few Preferences");
}
QString PreferencesSetupPage::stepSubtitle() const
{
return tr("Defaults are fine — tweak these now or from Settings anytime.");
}
void PreferencesSetupPage::retranslateUi()
{
gameplayGroup->setTitle(tr("Gameplay"));
doubleClickToPlayCheckBox->setText(tr("Double-click cards to play them"));
doubleClickToPlayCheckBox->setToolTip(tr("When disabled, a single click plays the selected card onto the table."));
horizontalHandCheckBox->setText(tr("Display hand horizontally"));
horizontalHandCheckBox->setToolTip(
tr("Shows your hand as a row along the bottom of the table instead of a column beside it."));
playToStackCheckBox->setText(tr("Play all nonlands onto the stack by default"));
playToStackCheckBox->setToolTip(
tr("Cards you play appear on the stack so other players can respond to them, as in a tabletop game."));
notificationsGroup->setTitle(tr("Notifications && Sound"));
notificationsEnabledCheckBox->setText(tr("Show desktop notifications"));
soundEnabledCheckBox->setText(tr("Play sound effects"));
startupGroup->setTitle(tr("Startup"));
startupTabLabel->setText(tr("Startup tab:"));
const QList<StartupTab> tabs = wizardStartupTabOrder();
for (int i = 0; i < tabs.size(); ++i) {
QString name;
switch (tabs[i]) {
case StartupTabHome:
name = tr("Home");
break;
case StartupTabVisualDeckStorage:
name = tr("Visual Deck Storage");
break;
case StartupTabDeckStorage:
name = tr("Deck Storage");
break;
case StartupTabReplays:
name = tr("Game Replays");
break;
case StartupTabDeckEditor:
name = tr("Deck Editor");
break;
case StartupTabVisualDeckEditor:
name = tr("Visual Deck Editor");
break;
case StartupTabServer:
name = tr("Server");
break;
case StartupTabServerRoom:
name = tr("Server Room");
break;
}
startupTabSelector->setItemText(i, name);
}
}