mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-29 09:22:18 -07:00
[Playmats] Add fixed override and configurable fallbacks to settings.
Took 29 minutes Took 43 seconds
This commit is contained in:
parent
92c8f1a1a1
commit
fa669544d3
19 changed files with 845 additions and 16 deletions
|
|
@ -240,8 +240,12 @@ set(cockatrice_SOURCES
|
|||
src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp
|
||||
src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
|
||||
src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
|
||||
src/interface/widgets/playmat/playmat_collection_dialog.cpp
|
||||
src/interface/widgets/playmat/playmat_collection_dialog.h
|
||||
src/interface/widgets/playmat/playmat_preview_widget.cpp
|
||||
src/interface/widgets/playmat/playmat_settings_dialog.cpp
|
||||
src/interface/widgets/playmat/playmat_settings_utils.cpp
|
||||
src/interface/widgets/playmat/playmat_settings_utils.h
|
||||
src/interface/widgets/quick_settings/settings_button_widget.cpp
|
||||
src/interface/widgets/quick_settings/settings_popup_widget.cpp
|
||||
src/interface/widgets/replay/replay_manager.cpp
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck_from_website.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_remote_deck.h"
|
||||
#include "../../interface/widgets/playmat/playmat_settings_utils.h"
|
||||
#include "../../interface/widgets/tabs/tab_game.h"
|
||||
#include "deck_view.h"
|
||||
|
||||
|
|
@ -277,7 +278,11 @@ void DeckViewContainer::loadDeckFromFile(const QString &filePath)
|
|||
|
||||
void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
|
||||
{
|
||||
QString deckString = deck.writeToString_Native();
|
||||
// Bake the user-level playmat resolution (override > deck > fallback list)
|
||||
// into the uploaded deck so the server, local and remote players, and
|
||||
// replays all derive the same effective playmat.
|
||||
const DeckList effectiveDeck = applyUserPlaymatSettings(deck, SettingsCache::instance().userInterface());
|
||||
QString deckString = effectiveDeck.writeToString_Native();
|
||||
|
||||
if (deckString.length() > MAX_FILE_LENGTH) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Deck is greater than maximum file size."));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../playmat/playmat_settings_dialog.h"
|
||||
#include "../playmat/playmat_settings_utils.h"
|
||||
#include "../settings_page/user_interface_settings_page.h"
|
||||
#include "../tabs/api/commander_spellbook/commander_bracket_widget.h"
|
||||
#include "deck_list_style_proxy.h"
|
||||
|
|
@ -17,6 +18,7 @@
|
|||
#include <QTextEdit>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/settings/deck_editor_settings.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/macros.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
|
||||
|
|
@ -468,6 +470,14 @@ void DeckEditorDeckDockWidget::openPlaymatSettings()
|
|||
deckStateManager->setPlaymatCard(newCard);
|
||||
deckStateManager->setPlaymatParams(newParams);
|
||||
}
|
||||
|
||||
// "Also set as my default playmat": make this the fixed user-level
|
||||
// fallback, so decks without a playmat of their own use it.
|
||||
if (dialog.useAsDefault() && !newCard.isEmpty()) {
|
||||
auto &interfaceSettings = SettingsCache::instance().userInterface();
|
||||
interfaceSettings.setPlaymatFallbackList({storedFromResolution({newCard, newParams})});
|
||||
interfaceSettings.setPlaymatFallbackMode(0); // PlaymatFallbackMode::Fixed
|
||||
}
|
||||
updatePlaymatLabel();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
#include "playmat_collection_dialog.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "playmat_settings_dialog.h"
|
||||
#include "playmat_settings_utils.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
|
||||
PlaymatCollectionDialog::PlaymatCollectionDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setMinimumWidth(420);
|
||||
setupUi();
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::accept()
|
||||
{
|
||||
auto &interfaceSettings = SettingsCache::instance().userInterface();
|
||||
interfaceSettings.setPlaymatFallbackList(playmats);
|
||||
interfaceSettings.setPlaymatFallbackMode(modeCombo->currentData().toInt());
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
int PlaymatCollectionDialog::currentRow() const
|
||||
{
|
||||
return playmatList->currentRow();
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::setupUi()
|
||||
{
|
||||
auto &interfaceSettings = SettingsCache::instance().userInterface();
|
||||
playmats = interfaceSettings.getPlaymatFallbackList();
|
||||
|
||||
playmatList = new QListWidget;
|
||||
for (const StoredPlaymat &entry : playmats) {
|
||||
playmatList->addItem(entry.name);
|
||||
}
|
||||
connect(playmatList, &QListWidget::itemSelectionChanged, this, &PlaymatCollectionDialog::selectionChanged);
|
||||
connect(playmatList, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem *) { editPlaymat(); });
|
||||
|
||||
addButton = new QPushButton;
|
||||
editButton = new QPushButton;
|
||||
removeButton = new QPushButton;
|
||||
moveUpButton = new QPushButton;
|
||||
moveDownButton = new QPushButton;
|
||||
|
||||
connect(addButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::addPlaymat);
|
||||
connect(editButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::editPlaymat);
|
||||
connect(removeButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::removePlaymat);
|
||||
connect(moveUpButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::movePlaymatUp);
|
||||
connect(moveDownButton, &QPushButton::clicked, this, &PlaymatCollectionDialog::movePlaymatDown);
|
||||
|
||||
auto *listButtons = new QVBoxLayout;
|
||||
listButtons->addWidget(addButton);
|
||||
listButtons->addWidget(editButton);
|
||||
listButtons->addWidget(removeButton);
|
||||
listButtons->addWidget(moveUpButton);
|
||||
listButtons->addWidget(moveDownButton);
|
||||
listButtons->addStretch();
|
||||
|
||||
auto *listRow = new QHBoxLayout;
|
||||
listRow->addWidget(playmatList, 1);
|
||||
listRow->addLayout(listButtons);
|
||||
|
||||
modeCombo = new QComboBox;
|
||||
modeCombo->addItem(QString(), 0);
|
||||
modeCombo->addItem(QString(), 1);
|
||||
modeCombo->addItem(QString(), 2);
|
||||
const int modeIndex = modeCombo->findData(interfaceSettings.getPlaymatFallbackMode());
|
||||
if (modeIndex >= 0) {
|
||||
modeCombo->setCurrentIndex(modeIndex);
|
||||
}
|
||||
|
||||
auto *modeRow = new QHBoxLayout;
|
||||
modeLabel = new QLabel;
|
||||
modeLabel->setBuddy(modeCombo);
|
||||
modeRow->addWidget(modeLabel);
|
||||
modeRow->addWidget(modeCombo, 1);
|
||||
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &PlaymatCollectionDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
auto *root = new QVBoxLayout;
|
||||
root->addLayout(listRow);
|
||||
root->addLayout(modeRow);
|
||||
root->addWidget(buttonBox);
|
||||
setLayout(root);
|
||||
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::selectionChanged()
|
||||
{
|
||||
const bool hasSelection = playmatList->currentRow() >= 0;
|
||||
editButton->setEnabled(hasSelection);
|
||||
removeButton->setEnabled(hasSelection);
|
||||
moveUpButton->setEnabled(hasSelection && playmatList->currentRow() > 0);
|
||||
moveDownButton->setEnabled(hasSelection && playmatList->currentRow() < playmatList->count() - 1);
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::appendEntry(const StoredPlaymat &entry)
|
||||
{
|
||||
playmats.append(entry);
|
||||
playmatList->addItem(entry.name);
|
||||
playmatList->setCurrentRow(playmatList->count() - 1);
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::addPlaymat()
|
||||
{
|
||||
PlaymatSettingsDialog dialog(CardRef{}, PlaymatParams{}, this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
const CardRef card = dialog.card();
|
||||
if (!card.isEmpty()) {
|
||||
appendEntry(storedFromResolution({card, dialog.params()}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::editPlaymat()
|
||||
{
|
||||
const int row = currentRow();
|
||||
if (row < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const PlaymatResolution current = resolutionFromStoredPlaymat(playmats.at(row));
|
||||
PlaymatSettingsDialog dialog(current.card, current.params, this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
const CardRef card = dialog.card();
|
||||
if (card.isEmpty()) {
|
||||
return; // Removal is handled by the Remove button
|
||||
}
|
||||
playmats[row] = storedFromResolution({card, dialog.params()});
|
||||
playmatList->item(row)->setText(card.name);
|
||||
}
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::removePlaymat()
|
||||
{
|
||||
const int row = currentRow();
|
||||
if (row < 0) {
|
||||
return;
|
||||
}
|
||||
playmats.removeAt(row);
|
||||
delete playmatList->takeItem(row);
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::movePlaymatUp()
|
||||
{
|
||||
const int row = currentRow();
|
||||
if (row <= 0) {
|
||||
return;
|
||||
}
|
||||
playmats.swapItemsAt(row, row - 1);
|
||||
playmatList->insertItem(row - 1, playmatList->takeItem(row));
|
||||
playmatList->setCurrentRow(row - 1);
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::movePlaymatDown()
|
||||
{
|
||||
const int row = currentRow();
|
||||
if (row < 0 || row >= playmats.size() - 1) {
|
||||
return;
|
||||
}
|
||||
playmats.swapItemsAt(row, row + 1);
|
||||
playmatList->insertItem(row + 1, playmatList->takeItem(row));
|
||||
playmatList->setCurrentRow(row + 1);
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
void PlaymatCollectionDialog::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Default Playmats"));
|
||||
addButton->setText(tr("Add..."));
|
||||
editButton->setText(tr("Edit..."));
|
||||
removeButton->setText(tr("Remove"));
|
||||
moveUpButton->setText(tr("Move Up"));
|
||||
moveDownButton->setText(tr("Move Down"));
|
||||
modeLabel->setText(tr("When a deck has no playmat:"));
|
||||
modeCombo->setItemText(0, tr("Always use the first playmat"));
|
||||
modeCombo->setItemText(1, tr("Cycle through playmats (one per game)"));
|
||||
modeCombo->setItemText(2, tr("Pick a random playmat per game"));
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H
|
||||
#define COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
|
||||
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QPushButton;
|
||||
|
||||
/**
|
||||
* @brief Dialog for editing the user-level playmat collection.
|
||||
*
|
||||
* The collection is the fallback used when a deck has no playmat of its own.
|
||||
* It supports multiple entries and a pick mode (always first / round-robin /
|
||||
* random). The dialog edits a working copy and writes it to the settings only
|
||||
* when accepted.
|
||||
*/
|
||||
class PlaymatCollectionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlaymatCollectionDialog(QWidget *parent = nullptr);
|
||||
|
||||
void accept() override;
|
||||
|
||||
private slots:
|
||||
void addPlaymat();
|
||||
void editPlaymat();
|
||||
void removePlaymat();
|
||||
void movePlaymatUp();
|
||||
void movePlaymatDown();
|
||||
void selectionChanged();
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void retranslateUi();
|
||||
int currentRow() const;
|
||||
void appendEntry(const StoredPlaymat &entry);
|
||||
|
||||
QList<StoredPlaymat> playmats; ///< Working copy edited by the dialog.
|
||||
QListWidget *playmatList;
|
||||
QComboBox *modeCombo;
|
||||
QLabel *modeLabel;
|
||||
QPushButton *addButton;
|
||||
QPushButton *editButton;
|
||||
QPushButton *removeButton;
|
||||
QPushButton *moveUpButton;
|
||||
QPushButton *moveDownButton;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include "card_database_model.h"
|
||||
#include "playmat_preview_widget.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCompleter>
|
||||
#include <QDialogButtonBox>
|
||||
|
|
@ -27,7 +28,6 @@ PlaymatSettingsDialog::PlaymatSettingsDialog(const CardRef &initialCard,
|
|||
QWidget *parent)
|
||||
: QDialog(parent), currentCard(initialCard), currentParams(initialParams)
|
||||
{
|
||||
setWindowTitle(tr("Playmat Settings"));
|
||||
setMinimumWidth(500);
|
||||
setupUi();
|
||||
|
||||
|
|
@ -53,6 +53,8 @@ PlaymatSettingsDialog::PlaymatSettingsDialog(const CardRef &initialCard,
|
|||
marginRSpin->setValue(initialParams.marginPctR);
|
||||
verticalOffsetSpin->setValue(initialParams.verticalOffset);
|
||||
zoomSpin->setValue(initialParams.zoom);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
CardRef PlaymatSettingsDialog::card() const
|
||||
|
|
@ -65,6 +67,11 @@ PlaymatParams PlaymatSettingsDialog::params() const
|
|||
return currentParams;
|
||||
}
|
||||
|
||||
bool PlaymatSettingsDialog::useAsDefault() const
|
||||
{
|
||||
return useAsDefaultCheckBox->isChecked();
|
||||
}
|
||||
|
||||
QDoubleSpinBox *PlaymatSettingsDialog::makeSpinBox(double min, double max, double value, double step)
|
||||
{
|
||||
auto *spin = new QDoubleSpinBox;
|
||||
|
|
@ -78,7 +85,6 @@ QDoubleSpinBox *PlaymatSettingsDialog::makeSpinBox(double min, double max, doubl
|
|||
void PlaymatSettingsDialog::initializeSearchBar()
|
||||
{
|
||||
searchBar = new QLineEdit;
|
||||
searchBar->setPlaceholderText(tr("Type a card name..."));
|
||||
|
||||
cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
||||
cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this);
|
||||
|
|
@ -121,36 +127,46 @@ void PlaymatSettingsDialog::setupUi()
|
|||
zoomSpin = makeSpinBox(0.1, 4.0, currentParams.zoom, 0.05);
|
||||
|
||||
auto *form = new QFormLayout;
|
||||
form->addRow(tr("Card name:"), searchBar);
|
||||
form->addRow(tr("Printing:"), providerComboBox);
|
||||
form->addRow(tr("Left margin (%):"), marginLSpin);
|
||||
form->addRow(tr("Right margin (%):"), marginRSpin);
|
||||
form->addRow(tr("Vertical offset:"), verticalOffsetSpin);
|
||||
form->addRow(tr("Zoom:"), zoomSpin);
|
||||
cardNameLabel = new QLabel;
|
||||
printingLabel = new QLabel;
|
||||
leftMarginLabel = new QLabel;
|
||||
rightMarginLabel = new QLabel;
|
||||
verticalOffsetLabel = new QLabel;
|
||||
zoomLabel = new QLabel;
|
||||
form->addRow(cardNameLabel, searchBar);
|
||||
form->addRow(printingLabel, providerComboBox);
|
||||
form->addRow(leftMarginLabel, marginLSpin);
|
||||
form->addRow(rightMarginLabel, marginRSpin);
|
||||
form->addRow(verticalOffsetLabel, verticalOffsetSpin);
|
||||
form->addRow(zoomLabel, zoomSpin);
|
||||
|
||||
auto *controlsGroup = new QGroupBox(tr("Parameters"));
|
||||
controlsGroup = new QGroupBox;
|
||||
controlsGroup->setLayout(form);
|
||||
|
||||
useAsDefaultCheckBox = new QCheckBox;
|
||||
useAsDefaultCheckBox->setEnabled(false);
|
||||
|
||||
preview = new PlaymatPreviewWidget;
|
||||
|
||||
auto *previewLayout = new QVBoxLayout;
|
||||
previewLayout->addWidget(preview);
|
||||
auto *previewGroup = new QGroupBox(tr("Preview"));
|
||||
previewGroup = new QGroupBox;
|
||||
previewGroup->setLayout(previewLayout);
|
||||
|
||||
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *removeBtn = new QPushButton(tr("Remove Playmat"));
|
||||
buttons->addButton(removeBtn, QDialogButtonBox::ResetRole);
|
||||
removeButton = new QPushButton;
|
||||
buttons->addButton(removeButton, QDialogButtonBox::ResetRole);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(removeBtn, &QPushButton::clicked, this, [this]() {
|
||||
connect(removeButton, &QPushButton::clicked, this, [this]() {
|
||||
currentCard = CardRef{}; // empty signals removal
|
||||
accept();
|
||||
});
|
||||
|
||||
auto *root = new QVBoxLayout;
|
||||
root->addWidget(controlsGroup);
|
||||
root->addWidget(useAsDefaultCheckBox);
|
||||
root->addWidget(previewGroup);
|
||||
root->addWidget(buttons);
|
||||
setLayout(root);
|
||||
|
|
@ -190,6 +206,8 @@ void PlaymatSettingsDialog::onCardNameChanged(const QString &name)
|
|||
if (name.isEmpty()) {
|
||||
currentPixmap = QPixmap();
|
||||
preview->setPixmap(currentPixmap);
|
||||
useAsDefaultCheckBox->setChecked(false);
|
||||
useAsDefaultCheckBox->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -198,10 +216,13 @@ void PlaymatSettingsDialog::onCardNameChanged(const QString &name)
|
|||
currentPixmap = QPixmap();
|
||||
preview->setPixmap(currentPixmap);
|
||||
providerComboBox->clear();
|
||||
useAsDefaultCheckBox->setChecked(false);
|
||||
useAsDefaultCheckBox->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
currentCard.name = name;
|
||||
useAsDefaultCheckBox->setEnabled(true);
|
||||
|
||||
populateProviderCombo(name);
|
||||
|
||||
|
|
@ -254,3 +275,19 @@ void PlaymatSettingsDialog::onParamChanged()
|
|||
currentParams.zoom = zoomSpin->value();
|
||||
preview->setParams(currentParams);
|
||||
}
|
||||
|
||||
void PlaymatSettingsDialog::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Playmat Settings"));
|
||||
searchBar->setPlaceholderText(tr("Type a card name..."));
|
||||
cardNameLabel->setText(tr("Card name:"));
|
||||
printingLabel->setText(tr("Printing:"));
|
||||
leftMarginLabel->setText(tr("Left margin (%):"));
|
||||
rightMarginLabel->setText(tr("Right margin (%):"));
|
||||
verticalOffsetLabel->setText(tr("Vertical offset:"));
|
||||
zoomLabel->setText(tr("Zoom:"));
|
||||
controlsGroup->setTitle(tr("Parameters"));
|
||||
useAsDefaultCheckBox->setText(tr("Also set as my default playmat (for decks without one)"));
|
||||
previewGroup->setTitle(tr("Preview"));
|
||||
removeButton->setText(tr("Remove Playmat"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@
|
|||
#include <QPixmap>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QCompleter;
|
||||
class QLineEdit;
|
||||
class QDoubleSpinBox;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class CardDatabaseModel;
|
||||
class CardDatabaseDisplayModel;
|
||||
class CardSearchModel;
|
||||
|
|
@ -35,6 +39,12 @@ public:
|
|||
CardRef card() const;
|
||||
PlaymatParams params() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the user also wants the configured playmat saved as the
|
||||
* user-level default (fallback for decks without one).
|
||||
*/
|
||||
bool useAsDefault() const;
|
||||
|
||||
private slots:
|
||||
void onCardNameChanged(const QString &name);
|
||||
void reloadPreview();
|
||||
|
|
@ -44,6 +54,7 @@ private:
|
|||
void setupUi();
|
||||
void populateProviderCombo(const QString &cardName);
|
||||
void initializeSearchBar();
|
||||
void retranslateUi();
|
||||
QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step);
|
||||
|
||||
QLineEdit *searchBar;
|
||||
|
|
@ -57,6 +68,18 @@ private:
|
|||
|
||||
QMetaObject::Connection pixmapUpdatedConnection;
|
||||
|
||||
QCheckBox *useAsDefaultCheckBox;
|
||||
|
||||
QLabel *cardNameLabel;
|
||||
QLabel *printingLabel;
|
||||
QLabel *leftMarginLabel;
|
||||
QLabel *rightMarginLabel;
|
||||
QLabel *verticalOffsetLabel;
|
||||
QLabel *zoomLabel;
|
||||
QGroupBox *controlsGroup;
|
||||
QGroupBox *previewGroup;
|
||||
QPushButton *removeButton;
|
||||
|
||||
QDoubleSpinBox *marginLSpin;
|
||||
QDoubleSpinBox *marginRSpin;
|
||||
QDoubleSpinBox *verticalOffsetSpin;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
#include "playmat_settings_utils.h"
|
||||
|
||||
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
|
||||
PlaymatResolution resolutionFromStoredPlaymat(const StoredPlaymat &stored)
|
||||
{
|
||||
PlaymatResolution resolution;
|
||||
resolution.card = {stored.name, stored.providerId};
|
||||
resolution.params = {qBound(0.0, stored.marginPctL, 0.95), qBound(0.0, stored.marginPctR, 0.95),
|
||||
qBound(0.0, stored.verticalOffset, 1.0), qBound(0.1, stored.zoom, 4.0)};
|
||||
return resolution;
|
||||
}
|
||||
|
||||
StoredPlaymat storedFromResolution(const PlaymatResolution &resolution)
|
||||
{
|
||||
StoredPlaymat stored;
|
||||
stored.name = resolution.card.name;
|
||||
stored.providerId = resolution.card.providerId;
|
||||
stored.marginPctL = resolution.params.marginPctL;
|
||||
stored.marginPctR = resolution.params.marginPctR;
|
||||
stored.verticalOffset = resolution.params.verticalOffset;
|
||||
stored.zoom = resolution.params.zoom;
|
||||
return stored;
|
||||
}
|
||||
|
||||
DeckList applyUserPlaymatSettings(const DeckList &deck, const InterfaceSettings &settings)
|
||||
{
|
||||
// Per-session cursor for the round-robin mode; advancing per resolution
|
||||
// (per deck upload, i.e. roughly per game) cycles deterministically.
|
||||
static int rotationIndex = 0;
|
||||
|
||||
QList<PlaymatResolution> fallbackList;
|
||||
const QList<StoredPlaymat> storedList = settings.getPlaymatFallbackList();
|
||||
fallbackList.reserve(storedList.size());
|
||||
for (const StoredPlaymat &stored : storedList) {
|
||||
fallbackList.append(resolutionFromStoredPlaymat(stored));
|
||||
}
|
||||
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, resolutionFromStoredPlaymat(settings.getPlaymatOverride()), fallbackList,
|
||||
static_cast<PlaymatFallbackMode>(settings.getPlaymatFallbackMode()), rotationIndex);
|
||||
|
||||
DeckList effectiveDeck = deck;
|
||||
effectiveDeck.setPlaymatCard(resolved.card);
|
||||
effectiveDeck.setPlaymatParams(resolved.params);
|
||||
return effectiveDeck;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef COCKATRICE_PLAYMAT_SETTINGS_UTILS_H
|
||||
#define COCKATRICE_PLAYMAT_SETTINGS_UTILS_H
|
||||
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
|
||||
|
||||
class InterfaceSettings;
|
||||
|
||||
/**
|
||||
* @brief Converts a stored (settings-level) playmat entry into resolver types,
|
||||
* clamping the positioning parameters to their valid ranges.
|
||||
*/
|
||||
PlaymatResolution resolutionFromStoredPlaymat(const StoredPlaymat &stored);
|
||||
|
||||
/**
|
||||
* @brief Converts a resolved playmat into a stored (settings-level) entry.
|
||||
*/
|
||||
StoredPlaymat storedFromResolution(const PlaymatResolution &resolution);
|
||||
|
||||
/**
|
||||
* @brief Applies the user-level playmat settings (override, fallback list and
|
||||
* mode) to a copy of the deck, returning the deck with the effective
|
||||
* playmat baked into its metadata.
|
||||
*
|
||||
* The resolution chain is: override > deck-configured playmat > fallback list.
|
||||
* The deck passed in is never modified; the returned copy is intended for
|
||||
* upload to the server so local and remote players see the same effective mat.
|
||||
*/
|
||||
DeckList applyUserPlaymatSettings(const DeckList &deck, const InterfaceSettings &settings);
|
||||
|
||||
#endif // COCKATRICE_PLAYMAT_SETTINGS_UTILS_H
|
||||
|
|
@ -7,11 +7,15 @@
|
|||
#include "../dialogs/override_printing_warning.h"
|
||||
#include "../interface/theme_manager.h"
|
||||
#include "../interface/widgets/general/background_sources.h"
|
||||
#include "../playmat/playmat_collection_dialog.h"
|
||||
#include "../playmat/playmat_settings_dialog.h"
|
||||
#include "../playmat/playmat_settings_utils.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QColorDialog>
|
||||
#include <QDesktopServices>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QStyleFactory>
|
||||
#include <QTimer>
|
||||
|
|
@ -329,12 +333,27 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
|||
});
|
||||
playmatVisibilityLabel.setBuddy(&playmatVisibilityCombo);
|
||||
|
||||
// User-level playmat settings: override (force all decks) and fallback collection.
|
||||
connect(&playmatOverrideEditButton, &QPushButton::clicked, this,
|
||||
&AppearanceSettingsPage::openOverridePlaymatSettings);
|
||||
connect(&playmatOverrideClearButton, &QPushButton::clicked, this, &AppearanceSettingsPage::clearOverridePlaymat);
|
||||
connect(&playmatDefaultEditButton, &QPushButton::clicked, this,
|
||||
&AppearanceSettingsPage::openPlaymatCollectionDialog);
|
||||
|
||||
auto *overrideButtons = new QHBoxLayout;
|
||||
overrideButtons->addWidget(&playmatOverrideEditButton);
|
||||
overrideButtons->addWidget(&playmatOverrideClearButton);
|
||||
|
||||
auto *tableGrid = new QGridLayout;
|
||||
tableGrid->addWidget(&invertVerticalCoordinateCheckBox, 0, 0, 1, 2);
|
||||
tableGrid->addWidget(&minPlayersForMultiColumnLayoutLabel, 1, 0, 1, 1);
|
||||
tableGrid->addWidget(&minPlayersForMultiColumnLayoutEdit, 1, 1, 1, 1);
|
||||
tableGrid->addWidget(&playmatVisibilityLabel, 2, 0, 1, 1);
|
||||
tableGrid->addWidget(&playmatVisibilityCombo, 2, 1, 1, 1);
|
||||
tableGrid->addWidget(&playmatOverrideLabel, 3, 0, 1, 1);
|
||||
tableGrid->addLayout(overrideButtons, 3, 1, 1, 1);
|
||||
tableGrid->addWidget(&playmatDefaultLabel, 4, 0, 1, 1);
|
||||
tableGrid->addWidget(&playmatDefaultEditButton, 4, 1, 1, 1);
|
||||
|
||||
tableGroupBox = new QGroupBox;
|
||||
tableGroupBox->setLayout(tableGrid);
|
||||
|
|
@ -445,6 +464,42 @@ void AppearanceSettingsPage::cardViewExpandedRowsMaxChanged(int value)
|
|||
}
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::openOverridePlaymatSettings()
|
||||
{
|
||||
auto &interfaceSettings = SettingsCache::instance().userInterface();
|
||||
const PlaymatResolution current = resolutionFromStoredPlaymat(interfaceSettings.getPlaymatOverride());
|
||||
PlaymatSettingsDialog dialog(current.card, current.params, this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
const CardRef card = dialog.card();
|
||||
if (card.isEmpty()) {
|
||||
interfaceSettings.setPlaymatOverride(StoredPlaymat{});
|
||||
} else {
|
||||
interfaceSettings.setPlaymatOverride(storedFromResolution({card, dialog.params()}));
|
||||
}
|
||||
updatePlaymatSummary();
|
||||
}
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::clearOverridePlaymat()
|
||||
{
|
||||
SettingsCache::instance().userInterface().setPlaymatOverride(StoredPlaymat{});
|
||||
updatePlaymatSummary();
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::openPlaymatCollectionDialog()
|
||||
{
|
||||
PlaymatCollectionDialog dialog(this);
|
||||
dialog.exec();
|
||||
updatePlaymatSummary();
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::updatePlaymatSummary()
|
||||
{
|
||||
const StoredPlaymat overridePlaymat = SettingsCache::instance().userInterface().getPlaymatOverride();
|
||||
playmatOverrideEditButton.setText(overridePlaymat.isEmpty() ? tr("Set...")
|
||||
: tr("Change (%1)").arg(overridePlaymat.name));
|
||||
}
|
||||
|
||||
void AppearanceSettingsPage::retranslateUi()
|
||||
{
|
||||
themeGroupBox->setTitle(tr("Theme settings"));
|
||||
|
|
@ -504,4 +559,9 @@ void AppearanceSettingsPage::retranslateUi()
|
|||
invertVerticalCoordinateCheckBox.setText(tr("Invert vertical coordinate"));
|
||||
minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:"));
|
||||
playmatVisibilityLabel.setText(tr("Playmat visibility:"));
|
||||
}
|
||||
playmatOverrideLabel.setText(tr("Force playmat for all decks:"));
|
||||
playmatOverrideClearButton.setText(tr("Clear"));
|
||||
playmatDefaultLabel.setText(tr("Default playmats (when a deck has none):"));
|
||||
playmatDefaultEditButton.setText(tr("Edit..."));
|
||||
updatePlaymatSummary();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ private slots:
|
|||
|
||||
void cardViewInitialRowsMaxChanged(int value);
|
||||
void cardViewExpandedRowsMaxChanged(int value);
|
||||
void openOverridePlaymatSettings();
|
||||
void clearOverridePlaymat();
|
||||
void openPlaymatCollectionDialog();
|
||||
void updatePlaymatSummary();
|
||||
|
||||
private:
|
||||
QLabel themeLabel;
|
||||
|
|
@ -61,6 +65,11 @@ private:
|
|||
QCheckBox invertVerticalCoordinateCheckBox;
|
||||
QLabel playmatVisibilityLabel;
|
||||
QComboBox playmatVisibilityCombo;
|
||||
QLabel playmatOverrideLabel;
|
||||
QPushButton playmatOverrideEditButton;
|
||||
QPushButton playmatOverrideClearButton;
|
||||
QLabel playmatDefaultLabel;
|
||||
QPushButton playmatDefaultEditButton;
|
||||
QGroupBox *themeGroupBox;
|
||||
QGroupBox *homeTabGroupBox;
|
||||
QGroupBox *stylingGroupBox;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ set(HEADERS
|
|||
libcockatrice/deck_list/deck_list_history_manager.h
|
||||
libcockatrice/deck_list/deck_list_node_tree.h
|
||||
libcockatrice/deck_list/deck_list_memento.h
|
||||
libcockatrice/deck_list/playmat_resolver.h
|
||||
libcockatrice/deck_list/sideboard_plan.h
|
||||
)
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ add_library(
|
|||
libcockatrice/deck_list/deck_list.cpp
|
||||
libcockatrice/deck_list/deck_list_history_manager.cpp
|
||||
libcockatrice/deck_list/deck_list_node_tree.cpp
|
||||
libcockatrice/deck_list/playmat_resolver.cpp
|
||||
libcockatrice/deck_list/sideboard_plan.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
#include "playmat_resolver.h"
|
||||
|
||||
#include <QRandomGenerator>
|
||||
|
||||
PlaymatResolution resolveEffectivePlaymat(const DeckList &deck,
|
||||
const PlaymatResolution &force,
|
||||
const QList<PlaymatResolution> &fallbackList,
|
||||
PlaymatFallbackMode fallbackMode,
|
||||
int &rotationIndex)
|
||||
{
|
||||
if (!force.card.isEmpty()) {
|
||||
return force;
|
||||
}
|
||||
|
||||
if (!deck.getPlaymatCard().isEmpty()) {
|
||||
return {deck.getPlaymatCard(), deck.getPlaymatParams()};
|
||||
}
|
||||
|
||||
if (fallbackList.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (fallbackMode) {
|
||||
case PlaymatFallbackMode::Fixed:
|
||||
return fallbackList.first();
|
||||
case PlaymatFallbackMode::RoundRobin:
|
||||
return fallbackList.at(rotationIndex++ % fallbackList.size());
|
||||
case PlaymatFallbackMode::Random:
|
||||
return fallbackList.at(QRandomGenerator::global()->bounded(fallbackList.size()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef COCKATRICE_PLAYMAT_RESOLVER_H
|
||||
#define COCKATRICE_PLAYMAT_RESOLVER_H
|
||||
|
||||
#include "deck_list.h"
|
||||
|
||||
/**
|
||||
* @brief How the user-level fallback playmat list is consulted when a deck has
|
||||
* no playmat configured.
|
||||
*/
|
||||
enum class PlaymatFallbackMode
|
||||
{
|
||||
Fixed = 0, ///< Always use the first entry of the fallback list.
|
||||
RoundRobin = 1, ///< Cycle through the list, advancing one entry per resolution.
|
||||
Random = 2 ///< Pick a random entry per resolution.
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct PlaymatResolution
|
||||
* @ingroup Decks
|
||||
* @brief A resolved playmat (card + positioning parameters).
|
||||
*/
|
||||
struct PlaymatResolution
|
||||
{
|
||||
CardRef card; ///< The card whose art is used as playmat.
|
||||
PlaymatParams params; ///< Positioning parameters for the playmat card image.
|
||||
|
||||
bool operator==(const PlaymatResolution &other) const
|
||||
{
|
||||
return card == other.card && params == other.params;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Resolves the effective playmat for a deck per the resolution chain:
|
||||
* force override > deck-configured playmat > fallback list > none.
|
||||
*
|
||||
* @param deck The deck to resolve a playmat for.
|
||||
* @param force An optional user-level override; wins over everything. Pass an
|
||||
* empty @ref PlaymatResolution::card to skip it.
|
||||
* @param fallbackList User-level fallback playmats, consulted only when the
|
||||
* deck has no configured playmat.
|
||||
* @param fallbackMode How @p fallbackList is consulted (ignored when empty).
|
||||
* @param rotationIndex In/out cursor for @c PlaymatFallbackMode::RoundRobin;
|
||||
* advanced once per call. Unused for the other modes.
|
||||
* @return The effective playmat; an empty @ref PlaymatResolution::card when
|
||||
* nothing in the chain resolves.
|
||||
*/
|
||||
PlaymatResolution resolveEffectivePlaymat(const DeckList &deck,
|
||||
const PlaymatResolution &force,
|
||||
const QList<PlaymatResolution> &fallbackList,
|
||||
PlaymatFallbackMode fallbackMode,
|
||||
int &rotationIndex);
|
||||
|
||||
#endif // COCKATRICE_PLAYMAT_RESOLVER_H
|
||||
|
|
@ -1,9 +1,30 @@
|
|||
#ifndef COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H
|
||||
#define COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
* @struct StoredPlaymat
|
||||
* @brief A user-level playmat entry as stored in the settings, independent of
|
||||
* any deck. Mirrors a deck-configured playmat (card + parameters).
|
||||
*/
|
||||
struct StoredPlaymat
|
||||
{
|
||||
QString name; ///< Card name whose art is used as playmat.
|
||||
QString providerId; ///< Printing provider id (uuid); may be empty.
|
||||
double marginPctL = 0.07; ///< Left margin as fraction of card width (0.0-0.95).
|
||||
double marginPctR = 0.07; ///< Right margin as fraction of card width (0.0-0.95).
|
||||
double verticalOffset = 0.33; ///< Vertical position within card (0.0=top, 1.0=bottom).
|
||||
double zoom = 1.0; ///< Scale factor (0.1-4.0).
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return name.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
class IInterfaceSettingsProvider
|
||||
{
|
||||
public:
|
||||
|
|
@ -44,6 +65,15 @@ public:
|
|||
[[nodiscard]] virtual bool getBattlefieldFlashEnabled() const = 0;
|
||||
[[nodiscard]] virtual QStringList getUserListExpandedSections() const = 0;
|
||||
[[nodiscard]] virtual int getPlaymatVisibility() const = 0;
|
||||
|
||||
/** @brief User-level fallback playmats, used when a deck has none configured. */
|
||||
[[nodiscard]] virtual QList<StoredPlaymat> getPlaymatFallbackList() const = 0;
|
||||
|
||||
/** @brief User-level override playmat applied to all decks, or empty to disable. */
|
||||
[[nodiscard]] virtual StoredPlaymat getPlaymatOverride() const = 0;
|
||||
|
||||
/** @brief How the fallback list is consulted (0 Fixed, 1 Round-robin, 2 Random). */
|
||||
[[nodiscard]] virtual int getPlaymatFallbackMode() const = 0;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H
|
||||
|
|
|
|||
|
|
@ -1,5 +1,34 @@
|
|||
#include "interface_settings.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const QChar PLAYMAT_FIELD_SEP = QChar(0x1F); ///< Separator between StoredPlaymat fields.
|
||||
|
||||
QString encodeStoredPlaymat(const StoredPlaymat &stored)
|
||||
{
|
||||
return stored.name + PLAYMAT_FIELD_SEP + stored.providerId + PLAYMAT_FIELD_SEP +
|
||||
QString::number(stored.marginPctL, 'f', 4) + PLAYMAT_FIELD_SEP + QString::number(stored.marginPctR, 'f', 4) +
|
||||
PLAYMAT_FIELD_SEP + QString::number(stored.verticalOffset, 'f', 4) + PLAYMAT_FIELD_SEP +
|
||||
QString::number(stored.zoom, 'f', 4);
|
||||
}
|
||||
|
||||
StoredPlaymat decodeStoredPlaymat(const QString &encoded)
|
||||
{
|
||||
const QStringList fields = encoded.split(PLAYMAT_FIELD_SEP);
|
||||
if (fields.size() != 6) {
|
||||
return {};
|
||||
}
|
||||
StoredPlaymat stored;
|
||||
stored.name = fields.at(0);
|
||||
stored.providerId = fields.at(1);
|
||||
stored.marginPctL = fields.at(2).toDouble();
|
||||
stored.marginPctR = fields.at(3).toDouble();
|
||||
stored.verticalOffset = fields.at(4).toDouble();
|
||||
stored.zoom = fields.at(5).toDouble();
|
||||
return stored;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
InterfaceSettings::InterfaceSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "interface.ini", "interface", QString(), parent)
|
||||
{
|
||||
|
|
@ -165,6 +194,30 @@ int InterfaceSettings::getPlaymatVisibility() const
|
|||
return getValue("playmatvisibility", QString(), QString(), 2).toInt();
|
||||
}
|
||||
|
||||
QList<StoredPlaymat> InterfaceSettings::getPlaymatFallbackList() const
|
||||
{
|
||||
const QStringList entries = getValue("playmatFallbackList", QString(), QString(), QStringList()).toStringList();
|
||||
QList<StoredPlaymat> result;
|
||||
result.reserve(entries.size());
|
||||
for (const QString &entry : entries) {
|
||||
const StoredPlaymat stored = decodeStoredPlaymat(entry);
|
||||
if (!stored.isEmpty()) {
|
||||
result.append(stored);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StoredPlaymat InterfaceSettings::getPlaymatOverride() const
|
||||
{
|
||||
return decodeStoredPlaymat(getValue("playmatOverride", QString(), QString(), QString()).toString());
|
||||
}
|
||||
|
||||
int InterfaceSettings::getPlaymatFallbackMode() const
|
||||
{
|
||||
return qBound(0, getValue("playmatFallbackMode", QString(), QString(), 1).toInt(), 2);
|
||||
}
|
||||
|
||||
bool InterfaceSettings::getLifeCounterAnimationsEnabled() const
|
||||
{
|
||||
return getValue("lifeCounterAnimationsEnabled", QString(), QString(), true).toBool();
|
||||
|
|
@ -357,6 +410,33 @@ void InterfaceSettings::setPlaymatVisibility(int _visibility)
|
|||
emit playmatVisibilityChanged(_visibility);
|
||||
}
|
||||
|
||||
void InterfaceSettings::setPlaymatFallbackList(const QList<StoredPlaymat> &_fallbackList)
|
||||
{
|
||||
QStringList entries;
|
||||
entries.reserve(_fallbackList.size());
|
||||
for (const StoredPlaymat &stored : _fallbackList) {
|
||||
entries.append(encodeStoredPlaymat(stored));
|
||||
}
|
||||
setValue(entries, "playmatFallbackList");
|
||||
emit playmatSettingsChanged();
|
||||
}
|
||||
|
||||
void InterfaceSettings::setPlaymatOverride(const StoredPlaymat &_override)
|
||||
{
|
||||
setValue(_override.isEmpty() ? QString() : encodeStoredPlaymat(_override), "playmatOverride");
|
||||
emit playmatSettingsChanged();
|
||||
}
|
||||
|
||||
void InterfaceSettings::setPlaymatFallbackMode(int _fallbackMode)
|
||||
{
|
||||
const int mode = qBound(0, _fallbackMode, 2);
|
||||
if (getPlaymatFallbackMode() == mode) {
|
||||
return;
|
||||
}
|
||||
setValue(mode, "playmatFallbackMode");
|
||||
emit playmatSettingsChanged();
|
||||
}
|
||||
|
||||
void InterfaceSettings::setLifeCounterAnimationsEnabled(bool _lifeCounterAnimationsEnabled)
|
||||
{
|
||||
setValue(_lifeCounterAnimationsEnabled, "lifeCounterAnimationsEnabled");
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ public:
|
|||
[[nodiscard]] bool getShowShortcuts() const override;
|
||||
[[nodiscard]] bool getShowGameSelectorFilterToolbar() const override;
|
||||
[[nodiscard]] int getPlaymatVisibility() const override;
|
||||
[[nodiscard]] QList<StoredPlaymat> getPlaymatFallbackList() const override;
|
||||
[[nodiscard]] StoredPlaymat getPlaymatOverride() const override;
|
||||
[[nodiscard]] int getPlaymatFallbackMode() const override;
|
||||
[[nodiscard]] bool getLifeCounterAnimationsEnabled() const override;
|
||||
[[nodiscard]] bool getBattlefieldFlashEnabled() const override;
|
||||
[[nodiscard]] QStringList getUserListExpandedSections() const override;
|
||||
|
|
@ -79,6 +82,9 @@ public:
|
|||
void setShowShortcuts(bool _showShortcuts);
|
||||
void setShowGameSelectorFilterToolbar(bool _showGameSelectorFilterToolbar);
|
||||
void setPlaymatVisibility(int _visibility);
|
||||
void setPlaymatFallbackList(const QList<StoredPlaymat> &_fallbackList);
|
||||
void setPlaymatOverride(const StoredPlaymat &_override);
|
||||
void setPlaymatFallbackMode(int _fallbackMode);
|
||||
void setLifeCounterAnimationsEnabled(bool _lifeCounterAnimationsEnabled);
|
||||
void setBattlefieldFlashEnabled(bool _battlefieldFlashEnabled);
|
||||
void setUserListExpandedSections(const QStringList §ions);
|
||||
|
|
@ -94,6 +100,7 @@ signals:
|
|||
void showStatusBarChanged(bool state);
|
||||
void showGameSelectorFilterToolbarChanged(bool state);
|
||||
void playmatVisibilityChanged(int visibility);
|
||||
void playmatSettingsChanged();
|
||||
void lifeCounterAnimationsEnabledChanged(bool state);
|
||||
void battlefieldFlashEnabledChanged(bool state);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ add_test(NAME expression_test COMMAND expression_test)
|
|||
add_test(NAME clamped_arithmetic_test COMMAND clamped_arithmetic_test)
|
||||
add_test(NAME test_age_formatting COMMAND test_age_formatting)
|
||||
add_test(NAME password_hash_test COMMAND password_hash_test)
|
||||
add_test(NAME playmat_resolver_test COMMAND playmat_resolver_test)
|
||||
add_test(NAME server_card_counter_test COMMAND server_card_counter_test)
|
||||
add_test(NAME server_counter_test COMMAND server_counter_test)
|
||||
add_test(NAME server_rate_limiter_test COMMAND server_rate_limiter_test)
|
||||
|
|
@ -21,6 +22,7 @@ add_executable(expression_test expression_test.cpp)
|
|||
add_executable(clamped_arithmetic_test clamped_arithmetic_test.cpp)
|
||||
add_executable(test_age_formatting test_age_formatting.cpp)
|
||||
add_executable(password_hash_test password_hash_test.cpp)
|
||||
add_executable(playmat_resolver_test playmat_resolver_test.cpp)
|
||||
add_executable(deck_hash_performance_test deck_hash_performance_test.cpp)
|
||||
add_executable(server_card_counter_test server_card_counter_test.cpp)
|
||||
add_executable(server_counter_test server_counter_test.cpp)
|
||||
|
|
@ -56,6 +58,7 @@ if(NOT GTEST_FOUND)
|
|||
add_dependencies(clamped_arithmetic_test gtest)
|
||||
add_dependencies(test_age_formatting gtest)
|
||||
add_dependencies(password_hash_test gtest)
|
||||
add_dependencies(playmat_resolver_test gtest)
|
||||
add_dependencies(deck_hash_performance_test gtest)
|
||||
add_dependencies(server_card_counter_test gtest)
|
||||
add_dependencies(server_counter_test gtest)
|
||||
|
|
@ -74,6 +77,10 @@ target_link_libraries(
|
|||
target_link_libraries(
|
||||
password_hash_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}
|
||||
)
|
||||
target_link_libraries(
|
||||
playmat_resolver_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES}
|
||||
${TEST_QT_MODULES}
|
||||
)
|
||||
target_link_libraries(
|
||||
deck_hash_performance_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES}
|
||||
${TEST_QT_MODULES}
|
||||
|
|
|
|||
140
tests/playmat_resolver_test.cpp
Normal file
140
tests/playmat_resolver_test.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
PlaymatResolution makeResolution(const QString &name, const QString &providerId = QString())
|
||||
{
|
||||
PlaymatResolution resolution;
|
||||
resolution.card = {name, providerId};
|
||||
return resolution;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(PlaymatResolverTest, EmptyChainReturnsEmpty)
|
||||
{
|
||||
DeckList deck;
|
||||
int rotationIndex = 0;
|
||||
const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, {}, PlaymatFallbackMode::Fixed, rotationIndex);
|
||||
EXPECT_TRUE(resolved.card.isEmpty());
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, OverrideWinsOverDeckAndFallback)
|
||||
{
|
||||
DeckList deck;
|
||||
deck.setPlaymatCard({QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")});
|
||||
|
||||
const PlaymatResolution force = makeResolution(QStringLiteral("Force Mat"), QStringLiteral("force-provider"));
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("Fallback Mat"))};
|
||||
|
||||
int rotationIndex = 0;
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, force, fallback, PlaymatFallbackMode::Fixed, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, QStringLiteral("Force Mat"));
|
||||
EXPECT_EQ(resolved.card.providerId, QStringLiteral("force-provider"));
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, DeckWinsOverFallback)
|
||||
{
|
||||
DeckList deck;
|
||||
deck.setPlaymatCard({QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")});
|
||||
deck.setPlaymatParams({0.1, 0.2, 0.3, 1.5});
|
||||
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("Fallback Mat"))};
|
||||
|
||||
int rotationIndex = 0;
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, QStringLiteral("Deck Mat"));
|
||||
EXPECT_EQ(resolved.card.providerId, QStringLiteral("deck-provider"));
|
||||
EXPECT_DOUBLE_EQ(resolved.params.marginPctL, 0.1);
|
||||
EXPECT_DOUBLE_EQ(resolved.params.verticalOffset, 0.3);
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, FallbackUsedWhenDeckHasNone)
|
||||
{
|
||||
DeckList deck;
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("Fallback Mat"))};
|
||||
|
||||
int rotationIndex = 0;
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, QStringLiteral("Fallback Mat"));
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, FixedAlwaysUsesFirst)
|
||||
{
|
||||
DeckList deck;
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("First")),
|
||||
makeResolution(QStringLiteral("Second"))};
|
||||
|
||||
int rotationIndex = 3; // ignored in Fixed mode
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, QStringLiteral("First"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, RoundRobinCyclesAndWraps)
|
||||
{
|
||||
DeckList deck;
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("First")),
|
||||
makeResolution(QStringLiteral("Second")),
|
||||
makeResolution(QStringLiteral("Third"))};
|
||||
|
||||
int rotationIndex = 0;
|
||||
const QStringList expected = {QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third"),
|
||||
QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third")};
|
||||
for (const QString &name : expected) {
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::RoundRobin, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, name);
|
||||
}
|
||||
EXPECT_EQ(rotationIndex, expected.size());
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, RoundRobinRespectsExistingCursor)
|
||||
{
|
||||
DeckList deck;
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("First")),
|
||||
makeResolution(QStringLiteral("Second"))};
|
||||
|
||||
int rotationIndex = 5;
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::RoundRobin, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, QStringLiteral("Second")); // 5 % 2 == 1
|
||||
EXPECT_EQ(rotationIndex, 6);
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, RandomStaysWithinList)
|
||||
{
|
||||
DeckList deck;
|
||||
const QList<PlaymatResolution> fallback = {makeResolution(QStringLiteral("First")),
|
||||
makeResolution(QStringLiteral("Second")),
|
||||
makeResolution(QStringLiteral("Third"))};
|
||||
|
||||
int rotationIndex = 0;
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Random, rotationIndex);
|
||||
ASSERT_FALSE(resolved.card.name.isEmpty());
|
||||
EXPECT_TRUE(fallback.contains(resolved));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlaymatResolverTest, ForceWithEmptyCardIgnoresFallbackParamsButNotFallback)
|
||||
{
|
||||
DeckList deck;
|
||||
deck.setPlaymatCard({QStringLiteral("Deck Mat")});
|
||||
|
||||
// An empty force entry must not mask the deck-configured playmat.
|
||||
const PlaymatResolution emptyForce;
|
||||
int rotationIndex = 0;
|
||||
const PlaymatResolution resolved =
|
||||
resolveEffectivePlaymat(deck, emptyForce, {}, PlaymatFallbackMode::Fixed, rotationIndex);
|
||||
EXPECT_EQ(resolved.card.name, QStringLiteral("Deck Mat"));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue