diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 8a981ce14..9a26d58e7 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -1,5 +1,6 @@ #include "cache_settings.h" +#include "../../interface/widgets/dialogs/dlg_settings.h" #include "../network/update/client/release_channel.h" #include "card_counter_settings.h" #include "version_string.h" @@ -303,6 +304,14 @@ SettingsCache::SettingsCache() deckEditorBannerCardComboBoxVisible = settings->value("interface/deckeditorbannercardcomboboxvisible", true).toBool(); deckEditorTagsWidgetVisible = settings->value("interface/deckeditortagswidgetvisible", true).toBool(); + deckEditorCommanderSpellbookIntegrationEnabled = + settings + ->value("interface/deck_editor/commander_spellbook_integration/enabled", + deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted) + .toInt(); + deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames = + settings->value("interface/deck_editor/commander_spellbook_integration/use_official_bracket_names", false) + .toBool(); visualDeckStorageCardSize = settings->value("interface/visualdeckstoragecardsize", 100).toInt(); visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt(); visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool(); @@ -798,6 +807,26 @@ void SettingsCache::setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T _deckEdito emit deckEditorTagsWidgetVisibleChanged(deckEditorTagsWidgetVisible); } +void SettingsCache::setDeckEditorCommanderSpellbookIntegrationEnabled( + int _deckEditorCommanderSpellbookIntegrationEnabled) +{ + deckEditorCommanderSpellbookIntegrationEnabled = _deckEditorCommanderSpellbookIntegrationEnabled; + settings->setValue("interface/deck_editor/commander_spellbook_integration/enabled", + deckEditorCommanderSpellbookIntegrationEnabled); + emit deckEditorCommanderSpellbookIntegrationEnabledChanged(deckEditorCommanderSpellbookIntegrationEnabled); +} + +void SettingsCache::setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames( + bool _deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames) +{ + deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames = + _deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames; + settings->setValue("interface/deck_editor/commander_spellbook_integration/use_official_bracket_names", + deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames); + emit deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesChanged( + deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames); +} + void SettingsCache::setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder) { visualDeckStorageSortingOrder = _visualDeckStorageSortingOrder; diff --git a/cockatrice/src/client/settings/cache_settings.h b/cockatrice/src/client/settings/cache_settings.h index 57adb394a..2f027dfad 100644 --- a/cockatrice/src/client/settings/cache_settings.h +++ b/cockatrice/src/client/settings/cache_settings.h @@ -156,6 +156,8 @@ signals: void printingSelectorNavigationButtonsVisibleChanged(); void deckEditorBannerCardComboBoxVisibleChanged(bool _visible); void deckEditorTagsWidgetVisibleChanged(bool _visible); + void deckEditorCommanderSpellbookIntegrationEnabledChanged(int _enabled); + void deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesChanged(bool _useOfficialBracketNames); void visualDeckStorageShowTagFilterChanged(bool _visible); void visualDeckStorageDefaultTagsListChanged(); void visualDeckStorageShowColorIdentityChanged(bool _visible); @@ -250,6 +252,8 @@ private: bool printingSelectorNavigationButtonsVisible; bool deckEditorBannerCardComboBoxVisible; bool deckEditorTagsWidgetVisible; + int deckEditorCommanderSpellbookIntegrationEnabled; + bool deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames; int visualDeckStorageSortingOrder; bool visualDeckStorageShowFolders; bool visualDeckStorageShowColorIdentity; @@ -723,6 +727,14 @@ public: { return openDeckInNewTab; } + [[nodiscard]] int getDeckEditorCommanderSpellbookIntegrationEnabled() const + { + return deckEditorCommanderSpellbookIntegrationEnabled; + } + [[nodiscard]] bool getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames() const + { + return deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames; + } [[nodiscard]] int getRewindBufferingMs() const { return rewindBufferingMs; @@ -1046,6 +1058,9 @@ public slots: void setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T _navigationButtonsVisible); void setDeckEditorBannerCardComboBoxVisible(QT_STATE_CHANGED_T _deckEditorBannerCardComboBoxVisible); void setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T _deckEditorTagsWidgetVisible); + void setDeckEditorCommanderSpellbookIntegrationEnabled(int _deckEditorCommanderSpellbookIntegrationEnabled); + void setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames( + bool _deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames); void setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder); void setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T value); void setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T _showTags); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 0a65d6c6b..541b88144 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -1,6 +1,7 @@ #include "deck_editor_deck_dock_widget.h" #include "../../../client/settings/cache_settings.h" +#include "../dialogs/dlg_settings.h" #include "../tabs/api/commander_spellbook/commander_spellbook_api_accessor.h" #include "../tabs/api/commander_spellbook/commander_spellbook_bracket_explainer.h" #include "deck_list_style_proxy.h" @@ -8,8 +9,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -310,6 +313,92 @@ void DeckEditorDeckDockWidget::createDeckDock() } } +bool DeckEditorDeckDockWidget::promptCommanderSpellbookIntegration() +{ + QDialog dialog(this); + dialog.setWindowTitle(tr("CommanderSpellbook integration")); + + auto *mainLayout = new QVBoxLayout(&dialog); + + // Main text + auto *label = new QLabel(tr("CommanderSpellbook can analyze your deck and estimate its Commander bracket.\n\n" + "This sends your deck list to an external service.\n\n" + "CommanderSpellbook uses its own bracket naming system based on their own algorithm. " + "These names can be mapped to the official Commander brackets, but the mapping " + "is only an approximation.")); + label->setWordWrap(true); + mainLayout->addWidget(label); + + // Naming selector + auto *formLayout = new QFormLayout; + auto *namingCombo = new QComboBox(&dialog); + namingCombo->addItem(tr("CommanderSpellbook bracket names")); + namingCombo->addItem(tr("Official Commander bracket names (approximate)")); + namingCombo->setCurrentIndex( + SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames() ? 1 : 0); + + // Create label + explainer button + auto *labelWidget = new QWidget(&dialog); + auto *labelLayout = new QHBoxLayout(labelWidget); + labelLayout->setContentsMargins(0, 0, 0, 0); + + auto *namingLabel = new QLabel(tr("Bracket naming:"), labelWidget); + auto *explainerButton = new QToolButton(labelWidget); + explainerButton->setText("?"); + explainerButton->setAutoRaise(true); + explainerButton->setEnabled(false); + explainerButton->setToolTip(CommanderBracketNames::Explainer); + + labelLayout->addWidget(namingLabel); + labelLayout->addWidget(explainerButton); + labelLayout->addStretch(); // push the button next to label, combo stays aligned + + // Add row with the custom label widget + formLayout->addRow(labelWidget, namingCombo); + mainLayout->addLayout(formLayout); + + // Buttons + auto *buttonBox = new QDialogButtonBox(&dialog); + auto *enableBtn = buttonBox->addButton(tr("Enable"), QDialogButtonBox::AcceptRole); + auto *automaticBtn = buttonBox->addButton(tr("Automatic"), QDialogButtonBox::ApplyRole); + auto *disableBtn = buttonBox->addButton(tr("Disable"), QDialogButtonBox::RejectRole); + mainLayout->addWidget(buttonBox); + + // Track which button was clicked + QAbstractButton *clickedButton = nullptr; + QObject::connect(buttonBox, &QDialogButtonBox::clicked, &dialog, [&](QAbstractButton *btn) { + clickedButton = btn; + dialog.accept(); + }); + + dialog.exec(); + + // Persist naming choice (if not disabled) + if (clickedButton != disableBtn) { + bool useOfficial = namingCombo->currentIndex() == 1; + SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(useOfficial); + } + + // Persist integration mode + if (clickedButton == disableBtn) { + SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled( + deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled); + return false; + } + if (clickedButton == enableBtn) { + SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled( + deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled); + return true; + } + if (clickedButton == automaticBtn) { + SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled( + deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic); + return true; + } + + return false; +} + void DeckEditorDeckDockWidget::requestBracketEstimate() { bracketRefreshButton->setEnabled(false); @@ -334,7 +423,11 @@ void DeckEditorDeckDockWidget::onEstimateBracketFinished(CommanderSpellbookApiAc lastBracketExplanation = explainer.explain(result); // Display bracket - bracketValueLabel->setText(CommanderSpellbookBracketTag::bracketTagToOfficialString(result.bracketTag)); + if (SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames()) { + bracketValueLabel->setText(CommanderSpellbookBracketTag::bracketTagToOfficialString(result.bracketTag)); + } else { + bracketValueLabel->setText(CommanderSpellbookBracketTag::bracketTagToString(result.bracketTag)); + } bracketRefreshButton->setEnabled(true); // Build tooltip @@ -383,11 +476,16 @@ void DeckEditorDeckDockWidget::initializeFormats() emit deckModified(); const bool isCommander = (formatKey.compare("commander", Qt::CaseInsensitive) == 0); + const bool commanderSpellbookIntegrationEnabled = + SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled() != + deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled; - bracketLabel->setVisible(isCommander); - bracketValueLabel->setVisible(isCommander); - bracketInfoButton->setVisible(isCommander); - bracketRefreshButton->setVisible(isCommander); + const bool bracketVisible = isCommander && commanderSpellbookIntegrationEnabled; + + bracketLabel->setVisible(bracketVisible); + bracketValueLabel->setVisible(bracketVisible); + bracketInfoButton->setVisible(bracketVisible); + bracketRefreshButton->setVisible(bracketVisible); if (!isCommander) { bracketValueLabel->setText("-"); @@ -413,6 +511,23 @@ void DeckEditorDeckDockWidget::maybeAutoEstimateBracket() return; } + int mode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled(); + + if (mode == deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted) { + if (!promptCommanderSpellbookIntegration()) { + bracketLabel->setVisible(false); + bracketValueLabel->setVisible(false); + bracketInfoButton->setVisible(false); + bracketRefreshButton->setVisible(false); + return; // user chose Disabled + } + // user chose Enabled or Automatic → fall through + } + mode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled(); + if (mode != deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic) { + return; + } + // Avoid firing if we already have a result or a request in flight if (!bracketRefreshButton->isEnabled()) { return; diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index a2153a28f..0b670d67f 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -35,6 +35,7 @@ public: QTreeView *deckView; QComboBox *bannerCardComboBox; void createDeckDock(); + bool promptCommanderSpellbookIntegration(); void requestBracketEstimate(); ExactCard getCurrentCard(); void retranslateUi(); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp index e7286f078..a7aaf8a1c 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp @@ -895,6 +895,56 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() connect(&defaultDeckEditorTypeSelector, QOverload::of(&QComboBox::currentIndexChanged), &SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType); + deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setText("?"); + deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setAutoRaise(true); + deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setEnabled(false); + + // Add items with userData = internal enum + deckEditorCommanderSpellbookIntegrationEnabledSelector.addItem( + tr("Disabled"), deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled); + deckEditorCommanderSpellbookIntegrationEnabledSelector.addItem( + tr("Enabled"), deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled); + deckEditorCommanderSpellbookIntegrationEnabledSelector.addItem( + tr("Automatic"), deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic); + + int storedMode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled(); + for (int i = 0; i < deckEditorCommanderSpellbookIntegrationEnabledSelector.count(); ++i) { + if (deckEditorCommanderSpellbookIntegrationEnabledSelector.itemData(i).toInt() == storedMode) { + deckEditorCommanderSpellbookIntegrationEnabledSelector.setCurrentIndex(i); + break; + } + } + + connect(&deckEditorCommanderSpellbookIntegrationEnabledSelector, + QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { + int mode = deckEditorCommanderSpellbookIntegrationEnabledSelector.itemData(index).toInt(); + SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled(mode); + updateCommanderSpellbookUiState(); + }); + + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.addItem( + tr("CommanderSpellbook bracket names")); // index 0 = false + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.addItem( + tr("Official Commander bracket names (approximate)")); // index 1 = true + + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setCurrentIndex( + SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames() ? 1 : 0); + + connect(&deckEditorCommanderSpellbookIntegrationBracketNamingSelector, + QOverload::of(&QComboBox::currentIndexChanged), &SettingsCache::instance(), [](int index) { + SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(index == 1); + }); + + updateCommanderSpellbookUiState(); + + auto *labelLayout = new QHBoxLayout; + labelLayout->setContentsMargins(0, 0, 0, 0); + labelLayout->addWidget(&deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel); + labelLayout->addWidget(&deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer); + + auto *labelWidget = new QWidget; + labelWidget->setLayout(labelLayout); + auto *deckEditorGrid = new QGridLayout; deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0); @@ -903,6 +953,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1); deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0); deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1); + deckEditorGrid->addWidget(&deckEditorCommanderSpellbookIntegrationEnabledLabel, 5, 0); + deckEditorGrid->addWidget(&deckEditorCommanderSpellbookIntegrationEnabledSelector, 5, 1); + deckEditorGrid->addWidget(labelWidget, 6, 0); + deckEditorGrid->addWidget(&deckEditorCommanderSpellbookIntegrationBracketNamingSelector, 6, 1); deckEditorGroupBox = new QGroupBox; deckEditorGroupBox->setLayout(deckEditorGrid); @@ -945,6 +999,27 @@ void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i) } } +void UserInterfaceSettingsPage::updateCommanderSpellbookUiState() +{ + const int mode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled(); + + const bool enabled = mode != deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled && + mode != deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted; + + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setEnabled(enabled); + deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setEnabled(enabled); + deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel.setVisible(enabled); + deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setVisible(enabled); + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setVisible(enabled); + + if (enabled) { + // Sync selector with the current stored bool + const bool useOfficial = + SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(); + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setCurrentIndex(useOfficial ? 1 : 0); + } +} + void UserInterfaceSettingsPage::retranslateUi() { generalGroupBox->setTitle(tr("General interface settings")); @@ -977,6 +1052,22 @@ void UserInterfaceSettingsPage::retranslateUi() defaultDeckEditorTypeLabel.setText(tr("Default deck editor type")); defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor")); defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor")); + deckEditorCommanderSpellbookIntegrationEnabledLabel.setText( + tr("CommanderSpellbook integration to estimate commander bracket")); + deckEditorCommanderSpellbookIntegrationEnabledSelector.setItemText( + deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled, tr("Disabled")); + deckEditorCommanderSpellbookIntegrationEnabledSelector.setItemText( + deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled, tr("Enabled")); + deckEditorCommanderSpellbookIntegrationEnabledSelector.setItemText( + deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic, tr("Automatic")); + deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel.setText(tr("Bracket naming")); + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setItemText( + 0, CommanderBracketNames::CommanderSpellbookBracketNames); + deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setItemText( + 1, CommanderBracketNames::OfficialCommanderBracketNames); + + deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setToolTip( + CommanderBracketNames::Explainer); replayGroupBox->setTitle(tr("Replay settings")); rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:")); rewindBufferingMsBox.setSuffix(" ms"); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.h b/cockatrice/src/interface/widgets/dialogs/dlg_settings.h index db107c6e2..b7bd1385e 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.h @@ -15,6 +15,7 @@ #include #include #include +#include #include inline Q_LOGGING_CATEGORY(DlgSettingsLog, "dlg_settings"); @@ -154,11 +155,35 @@ public: void retranslateUi() override; }; +enum deckEditorCommanderSpellbookIntegrationEnabledIndex +{ + deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled, + deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled, + deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic, + deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted, +}; + +namespace CommanderBracketNames +{ +inline const char *CommanderSpellbookBracketNames = QT_TR_NOOP("CommanderSpellbook"); +inline const char *OfficialCommanderBracketNames = QT_TR_NOOP("Official (approximate)"); +inline const char *Explainer = QT_TR_NOOP( + "The bracket system combines both objective data, as well as subjective play experience to estimate a " + "bracket for a deck.\nCommanderSpellbook's estimation is algorithmical, which means that it can only operate " + "on the objective data, not the subjective intent. \nThey have chosen to represent this by defining their " + "own bracket system which matches their algorithm.\n" + "This custom bracket system maps loosely to the standard system. \nYou may choose to use these mapped " + "standardized names if these are more familiar to you, however, you should keep in mind that these are just " + "rough estimations.\n\nAlways consider the subjective factors of the bracket system when determing a deck's " + "final bracket!"); +} // namespace CommanderBracketNames + class UserInterfaceSettingsPage : public AbstractSettingsPage { Q_OBJECT private slots: void setNotificationEnabled(QT_STATE_CHANGED_T); + void updateCommanderSpellbookUiState(); private: QCheckBox notificationsEnabledCheckBox; @@ -180,6 +205,11 @@ private: QCheckBox visualDeckStorageSelectionAnimationCheckBox; QLabel defaultDeckEditorTypeLabel; QComboBox defaultDeckEditorTypeSelector; + QLabel deckEditorCommanderSpellbookIntegrationEnabledLabel; + QComboBox deckEditorCommanderSpellbookIntegrationEnabledSelector; + QLabel deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel; + QToolButton deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer; + QComboBox deckEditorCommanderSpellbookIntegrationBracketNamingSelector; QLabel rewindBufferingMsLabel; QSpinBox rewindBufferingMsBox; QGroupBox *generalGroupBox;