mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-11 20:43:25 -08:00
Replace the hand-built zero-margin layouts with QtUtils::makeLayout, which is the helper the printing selector already uses.
374 lines
14 KiB
C++
374 lines
14 KiB
C++
#include "dlg_create_token.h"
|
|
|
|
#include "../../client/settings/cache_settings.h"
|
|
#include "../../interface/widgets/printing_selector/printing_selector.h"
|
|
#include "libcockatrice/card/card_info.h"
|
|
#include "libcockatrice/card/database/card_database_querier.h"
|
|
#include "libcockatrice/card/printing/exact_card.h"
|
|
#include "token_preview_widget.h"
|
|
|
|
#include <QByteArray>
|
|
#include <QChar>
|
|
#include <QCheckBox>
|
|
#include <QCloseEvent>
|
|
#include <QComboBox>
|
|
#include <QDialogButtonBox>
|
|
#include <QGridLayout>
|
|
#include <QGroupBox>
|
|
#include <QHeaderView>
|
|
#include <QItemSelectionModel>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QModelIndex>
|
|
#include <QRadioButton>
|
|
#include <QSet>
|
|
#include <QSignalBlocker>
|
|
#include <QTabWidget>
|
|
#include <QTimer>
|
|
#include <QTreeView>
|
|
#include <QVBoxLayout>
|
|
#include <QVariant>
|
|
#include <QWidget>
|
|
#include <libcockatrice/card/card_localization.h>
|
|
#include <libcockatrice/card/database/card_database_manager.h>
|
|
#include <libcockatrice/models/database/card_database_model.h>
|
|
#include <libcockatrice/models/database/token/token_display_model.h>
|
|
#include <libcockatrice/settings/card_override_settings.h>
|
|
#include <libcockatrice/settings/cards_display_settings.h>
|
|
#include <libcockatrice/settings/interface_settings.h>
|
|
#include <libcockatrice/settings/layouts_settings.h>
|
|
#include <libcockatrice/utility/card_ref.h>
|
|
#include <libcockatrice/utility/qt_utils.h>
|
|
#include <libcockatrice/utility/string_limits.h>
|
|
#include <qnamespace.h>
|
|
|
|
class QModelIndex;
|
|
class QWidget;
|
|
|
|
DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent)
|
|
: QDialog(parent), predefinedTokens(_predefinedTokens)
|
|
{
|
|
pic = new TokenPreviewWidget();
|
|
pic->setObjectName("pic");
|
|
|
|
nameLabel = new QLabel(tr("&Name:"));
|
|
nameEdit = new QLineEdit(tr("Token"));
|
|
nameEdit->setMaxLength(MAX_NAME_LENGTH);
|
|
QTimer::singleShot(100, this, [=, this]() {
|
|
nameEdit->selectAll();
|
|
nameEdit->setFocus();
|
|
});
|
|
|
|
connect(nameEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updateSearch);
|
|
nameLabel->setBuddy(nameEdit);
|
|
|
|
colorLabel = new QLabel(tr("C&olor:"));
|
|
colorEdit = new QComboBox;
|
|
colorEdit->addItem(tr("white"), QChar('w'));
|
|
colorEdit->addItem(tr("blue"), QChar('u'));
|
|
colorEdit->addItem(tr("black"), QChar('b'));
|
|
colorEdit->addItem(tr("red"), QChar('r'));
|
|
colorEdit->addItem(tr("green"), QChar('g'));
|
|
colorEdit->addItem(tr("multicolor"), QChar('m'));
|
|
colorEdit->addItem(tr("colorless"), QChar());
|
|
colorLabel->setBuddy(colorEdit);
|
|
|
|
ptLabel = new QLabel(tr("&P/T:"));
|
|
ptEdit = new QLineEdit;
|
|
ptEdit->setMaxLength(MAX_NAME_LENGTH);
|
|
ptLabel->setBuddy(ptEdit);
|
|
|
|
annotationLabel = new QLabel(tr("&Annotation:"));
|
|
annotationEdit = new QLineEdit;
|
|
annotationEdit->setMaxLength(MAX_NAME_LENGTH);
|
|
annotationLabel->setBuddy(annotationEdit);
|
|
|
|
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
|
|
destroyCheckBox->setChecked(true);
|
|
|
|
faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
|
|
connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
|
|
|
|
auto *grid = new QGridLayout;
|
|
grid->addWidget(nameLabel, 0, 0);
|
|
grid->addWidget(nameEdit, 0, 1);
|
|
grid->addWidget(colorLabel, 1, 0);
|
|
grid->addWidget(colorEdit, 1, 1);
|
|
grid->addWidget(ptLabel, 2, 0);
|
|
grid->addWidget(ptEdit, 2, 1);
|
|
grid->addWidget(annotationLabel, 3, 0);
|
|
grid->addWidget(annotationEdit, 3, 1);
|
|
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
|
|
grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
|
|
|
|
auto *tokenDataGroupBox = new QGroupBox(tr("Token data"));
|
|
tokenDataGroupBox->setLayout(grid);
|
|
|
|
cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
|
cardDatabaseDisplayModel = new TokenDisplayModel(this);
|
|
cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel);
|
|
|
|
const auto applyCardSearchLanguage = [this]() {
|
|
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
|
|
cardDatabaseDisplayModel->setSearchLanguage(CardSearchLanguage{
|
|
cardsDisplay.getCardLang(), static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
|
};
|
|
applyCardSearchLanguage();
|
|
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
|
applyCardSearchLanguage);
|
|
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
|
|
applyCardSearchLanguage);
|
|
|
|
chooseTokenFromAllRadioButton = new QRadioButton(tr("Show &all tokens"));
|
|
connect(chooseTokenFromAllRadioButton, &QRadioButton::toggled, this, &DlgCreateToken::actChooseTokenFromAll);
|
|
chooseTokenFromDeckRadioButton = new QRadioButton(tr("Show tokens from this &deck"));
|
|
connect(chooseTokenFromDeckRadioButton, &QRadioButton::toggled, this, &DlgCreateToken::actChooseTokenFromDeck);
|
|
|
|
QByteArray deckHeaderState = SettingsCache::instance().layouts().getDeckEditorDbHeaderState();
|
|
chooseTokenView = new QTreeView;
|
|
chooseTokenView->setModel(cardDatabaseDisplayModel);
|
|
chooseTokenView->setUniformRowHeights(true);
|
|
chooseTokenView->setRootIsDecorated(false);
|
|
chooseTokenView->setAlternatingRowColors(true);
|
|
chooseTokenView->setSortingEnabled(true);
|
|
chooseTokenView->sortByColumn(0, Qt::AscendingOrder);
|
|
chooseTokenView->resizeColumnToContents(0);
|
|
chooseTokenView->setWordWrap(true);
|
|
|
|
if (!deckHeaderState.isNull()) {
|
|
chooseTokenView->header()->restoreState(deckHeaderState);
|
|
}
|
|
|
|
chooseTokenView->header()->setStretchLastSection(false);
|
|
chooseTokenView->header()->hideSection(1); // Sets
|
|
chooseTokenView->header()->hideSection(2); // Mana Cost
|
|
chooseTokenView->header()->setSectionResizeMode(5, QHeaderView::ResizeToContents); // Color(s)
|
|
connect(chooseTokenView->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
|
|
&DlgCreateToken::tokenSelectionChanged);
|
|
connect(chooseTokenView, &QTreeView::doubleClicked, this, &DlgCreateToken::actOk);
|
|
|
|
if (predefinedTokens.isEmpty()) {
|
|
chooseTokenFromAllRadioButton->setChecked(true);
|
|
chooseTokenFromDeckRadioButton->setDisabled(true); // No tokens in deck = no need for option
|
|
} else {
|
|
chooseTokenFromDeckRadioButton->setChecked(true);
|
|
cardDatabaseDisplayModel->setCardNameSet(QSet<QString>(predefinedTokens.begin(), predefinedTokens.end()));
|
|
}
|
|
|
|
auto *tokenChoosePage = new QWidget;
|
|
tokenChoosePage->setObjectName("tokenChoosePage");
|
|
auto *tokenChooseLayout = QtUtils::makeLayout<QVBoxLayout>(tokenChoosePage);
|
|
tokenChooseLayout->addWidget(chooseTokenFromAllRadioButton);
|
|
tokenChooseLayout->addWidget(chooseTokenFromDeckRadioButton);
|
|
tokenChooseLayout->addWidget(chooseTokenView);
|
|
|
|
auto *printingsPage = new QWidget;
|
|
printingsPage->setObjectName("tokenPrintingsPage");
|
|
auto *printingsLayout = QtUtils::makeLayout<QVBoxLayout>(printingsPage);
|
|
printingSelector = new PrintingSelector(printingsPage, &printingSelectorHost);
|
|
printingSelector->setObjectName("tokenPrintingSelector");
|
|
printingsLayout->addWidget(printingSelector);
|
|
|
|
connect(printingSelector, &PrintingSelector::printingSelected, this, [this](const ExactCard &card) {
|
|
explicitProviderId = card.getPrinting().getUuid();
|
|
explicitProviderName = card.getName();
|
|
updatePreview();
|
|
});
|
|
|
|
tokenSourceTabs = new QTabWidget;
|
|
tokenSourceTabs->setObjectName("tokenSourceTabs");
|
|
tokenSourceTabs->addTab(tokenChoosePage, tr("&Tokens"));
|
|
tokenSourceTabs->addTab(printingsPage, tr("&Printings"));
|
|
|
|
auto *hbox = new QGridLayout;
|
|
hbox->addWidget(pic, 0, 0, 1, 1);
|
|
hbox->addWidget(tokenDataGroupBox, 1, 0, 1, 1);
|
|
hbox->addWidget(tokenSourceTabs, 0, 1, 2, 1);
|
|
hbox->setColumnStretch(1, 1);
|
|
|
|
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgCreateToken::actOk);
|
|
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgCreateToken::actReject);
|
|
|
|
auto *mainLayout = new QVBoxLayout;
|
|
mainLayout->addLayout(hbox);
|
|
mainLayout->addWidget(buttonBox);
|
|
setLayout(mainLayout);
|
|
|
|
setWindowTitle(tr("Create token"));
|
|
|
|
connect(nameEdit, &QLineEdit::textChanged, this, &DlgCreateToken::syncPrintingsToToken);
|
|
connect(nameEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updatePreview);
|
|
connect(colorEdit, qOverload<int>(&QComboBox::currentIndexChanged), this, &DlgCreateToken::updatePreview);
|
|
connect(ptEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updatePreview);
|
|
connect(annotationEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updatePreview);
|
|
syncPrintingsToToken();
|
|
updatePreview();
|
|
|
|
resize(900, 650);
|
|
restoreGeometry(SettingsCache::instance().layouts().getTokenDialogGeometry());
|
|
}
|
|
|
|
void DlgCreateToken::closeEvent(QCloseEvent *event)
|
|
{
|
|
event->accept();
|
|
SettingsCache::instance().layouts().setTokenDialogGeometry(saveGeometry());
|
|
}
|
|
|
|
void DlgCreateToken::faceDownCheckBoxToggled(bool checked)
|
|
{
|
|
// Keep the face-up draft visible while choosing what to create. Only the
|
|
// outgoing command applies face-down color/P/T restrictions.
|
|
colorEdit->setEnabled(!checked);
|
|
ptEdit->setEnabled(!checked);
|
|
if (checked) {
|
|
ptEdit->clearFocus();
|
|
}
|
|
updatePreview();
|
|
}
|
|
|
|
void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
|
{
|
|
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
|
|
|
|
CardInfoPtr cardInfo;
|
|
|
|
if (realIndex.isValid()) {
|
|
cardInfo = cardDatabaseModel->getCard(realIndex.row());
|
|
}
|
|
|
|
if (cardInfo) {
|
|
const QSignalBlocker colorBlocker(colorEdit);
|
|
const QSignalBlocker ptBlocker(ptEdit);
|
|
const QSignalBlocker annotationBlocker(annotationEdit);
|
|
updateSearchFieldWithoutUpdatingFilter(cardInfo->getName());
|
|
const QChar cardColor = cardInfo->getColorChar();
|
|
colorEdit->setCurrentIndex(colorEdit->findData(cardColor, Qt::UserRole, Qt::MatchFixedString));
|
|
ptEdit->setText(cardInfo->getPowTough());
|
|
if (SettingsCache::instance().userInterface().getAnnotateTokens()) {
|
|
annotationEdit->setText(cardInfo->getText());
|
|
}
|
|
}
|
|
|
|
// Filtering, changing the deck/all scope, or reloading the database can
|
|
// invalidate the current row. None of these actions discard the token draft.
|
|
syncPrintingsToToken();
|
|
updatePreview();
|
|
}
|
|
|
|
void DlgCreateToken::syncPrintingsToToken()
|
|
{
|
|
const CardInfoPtr cardInfo = CardDatabaseManager::query()->lookupCardByName(nameEdit->text());
|
|
|
|
if (!cardInfo) {
|
|
explicitProviderId.clear();
|
|
explicitProviderName.clear();
|
|
} else if (!explicitProviderId.isEmpty() && explicitProviderName != cardInfo->getName()) {
|
|
// A pick belongs to the token name it was made for.
|
|
explicitProviderId.clear();
|
|
explicitProviderName.clear();
|
|
}
|
|
|
|
if (!cardInfo || cardInfo->getSets().empty()) {
|
|
printingSelector->setCard(nullptr);
|
|
printingSelector->setSelectedProviderId(QString());
|
|
printingSelector->setPlaceholderText(tr("This token has no available printings."));
|
|
return;
|
|
}
|
|
|
|
printingSelector->setPlaceholderText(QString());
|
|
printingSelector->setCard(cardInfo);
|
|
|
|
const QString providerId =
|
|
explicitProviderId.isEmpty()
|
|
? SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardInfo->getName())
|
|
: explicitProviderId;
|
|
printingSelector->setSelectedProviderId(providerId);
|
|
}
|
|
|
|
void DlgCreateToken::updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const
|
|
{
|
|
const QSignalBlocker blocker(nameEdit);
|
|
nameEdit->setText(newValue);
|
|
}
|
|
|
|
void DlgCreateToken::updatePreview()
|
|
{
|
|
pic->setToken(tokenDraft());
|
|
}
|
|
|
|
void DlgCreateToken::updateSearch(const QString &search)
|
|
{
|
|
cardDatabaseDisplayModel->setCardName(search);
|
|
}
|
|
|
|
void DlgCreateToken::actChooseTokenFromAll(bool checked)
|
|
{
|
|
if (checked) {
|
|
cardDatabaseDisplayModel->setCardNameSet(QSet<QString>());
|
|
}
|
|
}
|
|
|
|
void DlgCreateToken::actChooseTokenFromDeck(bool checked)
|
|
{
|
|
if (checked) {
|
|
cardDatabaseDisplayModel->setCardNameSet(QSet<QString>(predefinedTokens.begin(), predefinedTokens.end()));
|
|
}
|
|
}
|
|
|
|
void DlgCreateToken::actOk()
|
|
{
|
|
SettingsCache::instance().layouts().setTokenDialogGeometry(saveGeometry());
|
|
accept();
|
|
}
|
|
|
|
void DlgCreateToken::actReject()
|
|
{
|
|
SettingsCache::instance().layouts().setTokenDialogGeometry(saveGeometry());
|
|
reject();
|
|
}
|
|
|
|
TokenInfo DlgCreateToken::tokenDraft() const
|
|
{
|
|
// Keep an explicit pick only while it still belongs to the current token.
|
|
QString providerId = explicitProviderId;
|
|
if (!providerId.isEmpty()) {
|
|
const CardInfoPtr pickedCard = CardDatabaseManager::query()->lookupCardByName(nameEdit->text());
|
|
if (!pickedCard || pickedCard->getName() != explicitProviderName) {
|
|
providerId.clear();
|
|
}
|
|
}
|
|
if (providerId.isEmpty()) {
|
|
providerId = SettingsCache::instance().cardOverrides().getCardPreferenceOverride(nameEdit->text());
|
|
}
|
|
|
|
TokenInfo token = {.name = nameEdit->text(),
|
|
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
|
|
.pt = ptEdit->text(),
|
|
.annotation = annotationEdit->text(),
|
|
.destroy = destroyCheckBox->isChecked(),
|
|
.faceDown = faceDownCheckBox->isChecked(),
|
|
.providerId = providerId};
|
|
|
|
// Match the normalization applied when the player creates the token, so
|
|
// manually typed aliases and the default P/T preview the actual result.
|
|
const ExactCard correctedCard =
|
|
CardDatabaseManager::query()->guessCard(CardRef{.name = token.name, .providerId = token.providerId});
|
|
if (correctedCard) {
|
|
token.name = correctedCard.getName();
|
|
if (token.pt.isEmpty()) {
|
|
token.pt = correctedCard.getInfo().getPowTough();
|
|
}
|
|
}
|
|
return token;
|
|
}
|
|
|
|
TokenInfo DlgCreateToken::getTokenInfo() const
|
|
{
|
|
TokenInfo token = tokenDraft();
|
|
if (token.faceDown) {
|
|
token.color = QString(QChar());
|
|
token.pt.clear();
|
|
}
|
|
return token;
|
|
}
|