mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 09:05:10 -07:00
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
Use the landscape orientation flag to rotate sideways-layout card art upright before cropping, so planes/sieges show their horizontal art as the server profile banner card instead of a rotated full card. - Curve cropCardArt around the card's landscapeOrientation flag with landscape-specific art margins (mirrors CardInfoPictureWidget) - Add shared CardArtUtils::rotateSidewaysLayoutArt helper and apply it to the playmat (game render and settings preview) and card info widget, replacing three duplicate 90-degree rotation blocks Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
325 lines
11 KiB
C++
325 lines
11 KiB
C++
#include "playmat_settings_dialog.h"
|
|
|
|
#include "../../card_picture_loader/card_picture_loader.h"
|
|
#include "../cards/art_crop_attribution.h"
|
|
#include "../cards/card_art_utils.h"
|
|
#include "../utility/completer_utils.h"
|
|
#include "card_database_display_model.h"
|
|
#include "card_database_model.h"
|
|
#include "playmat_preview_widget.h"
|
|
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QCompleter>
|
|
#include <QDialogButtonBox>
|
|
#include <QDoubleSpinBox>
|
|
#include <QFormLayout>
|
|
#include <QGroupBox>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QPainter>
|
|
#include <QPainterPath>
|
|
#include <QPushButton>
|
|
#include <QVBoxLayout>
|
|
#include <libcockatrice/card/database/card_database_manager.h>
|
|
|
|
PlaymatSettingsDialog::PlaymatSettingsDialog(const CardRef &initialCard,
|
|
const PlaymatParams &initialParams,
|
|
QWidget *parent)
|
|
: QDialog(parent), currentCard(initialCard), currentParams(initialParams)
|
|
{
|
|
setMinimumWidth(500);
|
|
setupUi();
|
|
|
|
// Seed UI from initial values
|
|
if (!initialCard.name.isEmpty()) {
|
|
searchBar->setText(initialCard.name);
|
|
onCardNameChanged(initialCard.name);
|
|
|
|
// onCardNameChanged leaves the printing combo on the first printing in
|
|
// the database, which would silently change the deck's stored playmat
|
|
// card on accept. Restore the stored printing when it resolves locally.
|
|
const int storedPrintingIndex = providerComboBox->findData(initialCard.providerId);
|
|
if (storedPrintingIndex != -1) {
|
|
providerComboBox->setCurrentIndex(storedPrintingIndex);
|
|
} else {
|
|
// Stored printing not in the local database: keep it rather than
|
|
// silently substituting the first printing.
|
|
currentCard.providerId = initialCard.providerId;
|
|
reloadPreview();
|
|
}
|
|
}
|
|
|
|
retranslateUi();
|
|
}
|
|
|
|
CardRef PlaymatSettingsDialog::card() const
|
|
{
|
|
return currentCard;
|
|
}
|
|
|
|
PlaymatParams PlaymatSettingsDialog::params() const
|
|
{
|
|
return currentParams;
|
|
}
|
|
|
|
QDoubleSpinBox *PlaymatSettingsDialog::makeSpinBox(double min, double max, double value, double step)
|
|
{
|
|
auto *spin = new QDoubleSpinBox;
|
|
spin->setRange(min, max);
|
|
spin->setSingleStep(step);
|
|
spin->setDecimals(3);
|
|
spin->setValue(value);
|
|
return spin;
|
|
}
|
|
|
|
void PlaymatSettingsDialog::initializeSearchBar()
|
|
{
|
|
searchBar = new QLineEdit;
|
|
|
|
cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
|
cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this);
|
|
cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel);
|
|
|
|
const CardCompleterSetup cardSetup = createCardCompleter(cardDatabaseDisplayModel, this, 15);
|
|
searchModel = cardSetup.searchModel;
|
|
proxyModel = cardSetup.proxyModel;
|
|
completer = cardSetup.completer;
|
|
searchBar->setCompleter(completer);
|
|
|
|
connectCardCompleterSearch(searchBar, cardSetup);
|
|
|
|
connect(completer, static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated), this,
|
|
[this](const QString &completion) {
|
|
if (searchBar->text() != completion) {
|
|
searchBar->setText(completion);
|
|
searchBar->setCursorPosition(searchBar->text().length());
|
|
}
|
|
onCardNameChanged(completion);
|
|
});
|
|
|
|
connect(searchBar, &QLineEdit::returnPressed, this, [this]() { onCardNameChanged(searchBar->text()); });
|
|
}
|
|
|
|
void PlaymatSettingsDialog::setupUi()
|
|
{
|
|
initializeSearchBar();
|
|
|
|
providerComboBox = new QComboBox;
|
|
connect(providerComboBox, &QComboBox::currentIndexChanged, this, [this]() {
|
|
currentCard.providerId = providerComboBox->currentData().toString();
|
|
reloadPreview();
|
|
});
|
|
|
|
auto *form = new QFormLayout;
|
|
controlsForm = form;
|
|
cardNameLabel = new QLabel;
|
|
printingLabel = new QLabel;
|
|
form->addRow(cardNameLabel, searchBar);
|
|
form->addRow(printingLabel, providerComboBox);
|
|
|
|
// Numerical editors expose the raw PlaymatParams for precise input. They
|
|
// share the same form as the rows above so every field lines up on one
|
|
// label column. They stay hidden until requested since the crop surface
|
|
// is the primary control.
|
|
marginLSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctL, 0.01);
|
|
marginRSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctR, 0.01);
|
|
verticalOffsetSpin = makeSpinBox(0.0, 1.0, currentParams.verticalOffset, 0.01);
|
|
zoomSpin = makeSpinBox(0.1, 4.0, currentParams.zoom, 0.05);
|
|
|
|
leftMarginLabel = new QLabel;
|
|
rightMarginLabel = new QLabel;
|
|
verticalOffsetLabel = new QLabel;
|
|
zoomLabel = new QLabel;
|
|
|
|
showNumericEditorsCheck = new QCheckBox;
|
|
|
|
form->addRow(showNumericEditorsCheck);
|
|
form->addRow(leftMarginLabel, marginLSpin);
|
|
form->addRow(rightMarginLabel, marginRSpin);
|
|
form->addRow(verticalOffsetLabel, verticalOffsetSpin);
|
|
form->addRow(zoomLabel, zoomSpin);
|
|
|
|
controlsGroup = new QGroupBox;
|
|
controlsGroup->setLayout(form);
|
|
|
|
preview = new PlaymatPreviewWidget;
|
|
preview->setParams(currentParams);
|
|
|
|
auto *previewLayout = new QVBoxLayout;
|
|
previewLayout->addWidget(preview);
|
|
previewCaptionLabel = new QLabel;
|
|
previewCaptionLabel->setAlignment(Qt::AlignCenter);
|
|
previewCaptionLabel->setWordWrap(true);
|
|
previewLayout->addWidget(previewCaptionLabel);
|
|
previewGroup = new QGroupBox;
|
|
previewGroup->setLayout(previewLayout);
|
|
|
|
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
removeButton = new QPushButton;
|
|
buttons->addButton(removeButton, QDialogButtonBox::ResetRole);
|
|
|
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
connect(removeButton, &QPushButton::clicked, this, [this]() {
|
|
currentCard = CardRef{}; // empty signals removal
|
|
accept();
|
|
});
|
|
|
|
// The crop surface is the primary control: dragging pans, wheel/keys zoom,
|
|
// editing exactly the same stored parameters the numeric fields do.
|
|
connect(preview, &PlaymatPreviewWidget::paramsEdited, this, [this](const PlaymatParams &edited) {
|
|
currentParams = edited;
|
|
|
|
QSignalBlocker blockMarginL(marginLSpin);
|
|
QSignalBlocker blockMarginR(marginRSpin);
|
|
QSignalBlocker blockOffset(verticalOffsetSpin);
|
|
QSignalBlocker blockZoom(zoomSpin);
|
|
marginLSpin->setValue(edited.marginPctL);
|
|
marginRSpin->setValue(edited.marginPctR);
|
|
verticalOffsetSpin->setValue(edited.verticalOffset);
|
|
zoomSpin->setValue(edited.zoom);
|
|
});
|
|
|
|
connect(showNumericEditorsCheck, &QCheckBox::toggled, this, &PlaymatSettingsDialog::setNumericEditorsVisible);
|
|
setNumericEditorsVisible(false);
|
|
|
|
connect(marginLSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged);
|
|
connect(marginRSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged);
|
|
connect(verticalOffsetSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged);
|
|
connect(zoomSpin, &QDoubleSpinBox::valueChanged, this, &PlaymatSettingsDialog::onParamChanged);
|
|
|
|
// The crop surface leads visually, card selection supports it below.
|
|
auto *root = new QVBoxLayout;
|
|
root->addWidget(previewGroup);
|
|
root->addWidget(controlsGroup);
|
|
root->addWidget(buttons);
|
|
setLayout(root);
|
|
}
|
|
|
|
void PlaymatSettingsDialog::populateProviderCombo(const QString &cardName)
|
|
{
|
|
providerComboBox->clear();
|
|
|
|
auto card = CardDatabaseManager::query()->getCard({cardName});
|
|
|
|
const auto &sets = card.getInfo().getSets();
|
|
|
|
for (const auto &printings : sets) {
|
|
for (const auto &p : printings) {
|
|
QString setName = p.getSet()->getLongName();
|
|
QString collector = p.getProperty("num");
|
|
QString uuid = p.getUuid();
|
|
|
|
QString label = setName;
|
|
if (!collector.isEmpty()) {
|
|
label += " #" + collector;
|
|
}
|
|
|
|
providerComboBox->addItem(label, uuid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlaymatSettingsDialog::onCardNameChanged(const QString &name)
|
|
{
|
|
if (name.isEmpty()) {
|
|
currentPixmap = QPixmap();
|
|
preview->setPixmap(currentPixmap);
|
|
return;
|
|
}
|
|
|
|
const ExactCard card = CardDatabaseManager::query()->getCard({name});
|
|
if (!card) {
|
|
currentPixmap = QPixmap();
|
|
preview->setPixmap(currentPixmap);
|
|
providerComboBox->clear();
|
|
return;
|
|
}
|
|
|
|
currentCard.name = name;
|
|
|
|
populateProviderCombo(name);
|
|
|
|
if (providerComboBox->count() == 0) {
|
|
currentPixmap = QPixmap();
|
|
preview->setPixmap(currentPixmap);
|
|
currentCard.providerId.clear();
|
|
return;
|
|
}
|
|
|
|
currentCard.providerId = providerComboBox->currentData().toString();
|
|
reloadPreview();
|
|
}
|
|
|
|
void PlaymatSettingsDialog::reloadPreview()
|
|
{
|
|
if (currentCard.name.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
ExactCard card = CardDatabaseManager::query()->getCard({currentCard.name, currentCard.providerId});
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
disconnect(pixmapUpdatedConnection);
|
|
|
|
QPixmap fullRes;
|
|
CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040));
|
|
|
|
if (fullRes.isNull()) {
|
|
CardInfo *cardInfo = card.getCardPtr().data();
|
|
if (cardInfo) {
|
|
pixmapUpdatedConnection = connect(cardInfo, &CardInfo::pixmapUpdated, this, [this]() { reloadPreview(); });
|
|
}
|
|
return;
|
|
}
|
|
|
|
currentPixmap = CardArtUtils::rotateSidewaysLayoutArt(fullRes, card);
|
|
preview->setPixmap(currentPixmap);
|
|
preview->setParams(currentParams);
|
|
preview->setAttribution(buildArtAttribution(card));
|
|
}
|
|
|
|
void PlaymatSettingsDialog::onParamChanged()
|
|
{
|
|
currentParams.marginPctL = marginLSpin->value();
|
|
currentParams.marginPctR = marginRSpin->value();
|
|
currentParams.verticalOffset = verticalOffsetSpin->value();
|
|
currentParams.zoom = zoomSpin->value();
|
|
preview->setParams(currentParams);
|
|
}
|
|
|
|
void PlaymatSettingsDialog::setNumericEditorsVisible(bool visible)
|
|
{
|
|
controlsForm->setRowVisible(leftMarginLabel, visible);
|
|
controlsForm->setRowVisible(rightMarginLabel, visible);
|
|
controlsForm->setRowVisible(verticalOffsetLabel, visible);
|
|
controlsForm->setRowVisible(zoomLabel, visible);
|
|
|
|
// A QDialog never resizes itself when its content requirements change,
|
|
// so revealing the editors would squeeze the crop group until the info
|
|
// caption ran into the preview. Re-fit the dialog to the new size hint.
|
|
adjustSize();
|
|
}
|
|
|
|
void PlaymatSettingsDialog::retranslateUi()
|
|
{
|
|
setWindowTitle(tr("Playmat Settings"));
|
|
searchBar->setPlaceholderText(tr("Type a card name..."));
|
|
cardNameLabel->setText(tr("Card name:"));
|
|
printingLabel->setText(tr("Printing:"));
|
|
showNumericEditorsCheck->setText(tr("Show numerical editors"));
|
|
leftMarginLabel->setText(tr("Left margin (%):"));
|
|
rightMarginLabel->setText(tr("Right margin (%):"));
|
|
verticalOffsetLabel->setText(tr("Vertical offset:"));
|
|
zoomLabel->setText(tr("Zoom:"));
|
|
controlsGroup->setTitle(tr("Card"));
|
|
previewGroup->setTitle(tr("Crop"));
|
|
previewCaptionLabel->setText(
|
|
tr("Drag to pan, scroll to zoom, arrow keys nudge, plus and minus zoom, Backspace or Esc restores. "
|
|
"Dimmed strips mark where a wider table crops further."));
|
|
removeButton->setText(tr("Remove Playmat"));
|
|
}
|