diff --git a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp b/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp index 52f21f714..130daadf4 100644 --- a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp +++ b/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.cpp @@ -3,25 +3,63 @@ #include "../cards/art_crop_attribution.h" #include "playmat_utils.h" +#include #include +#include #include #include +#include +#include + +namespace +{ +// Mirrors the dialog/proto clamps so gestures can never produce an +// out of range parameter. The zoom FLOOR is dynamic: see +// playmatClampedZoom(), zooming out stops where the sampling window would +// exceed the card itself, so there is no dead range at the bottom end. +constexpr qreal MAX_MARGIN = 0.95; +// Range of in game stack+table aspect ratios worth designing for, derived +// from PlayerGraphicsItem::paint()'s combinedArea = stack ∪ table: +// height = 10 + 30 + 3*102 + 2*30 = 406 (TableZone rows) +// width = 1.5*72 + (20 + 5*72 + 15) = 503 (StackZone + TableZone +// at MIN_WIDTH) +// The area's shape depends on GAME CONTENT (played card columns widen the +// table by ~107 px each), not on the window size. Fresh board ≈ 503/406, +// a table grown to roughly double its minimum width ≈ 2.2. +constexpr qreal MIN_TABLE_ASPECT = 503.0 / 406.0; // fresh board: most generous framing +constexpr qreal MAX_TABLE_ASPECT = 2.2; // well developed, wide table +// Keyboard nudge steps (viewport convention: Down looks further down). +constexpr qreal KEY_PAN_MARGIN_STEP = 0.005; +constexpr qreal KEY_PAN_OFFSET_STEP = 0.01; +constexpr qreal KEY_ZOOM_STEP = 1.05; +constexpr qreal WHEEL_ZOOM_BASE = 1.15; // zoom factor per wheel notch +} // namespace PlaymatPreviewWidget::PlaymatPreviewWidget(QWidget *parent) : QWidget(parent) { - setMinimumSize(400, 120); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + // The crop is square and drawn contain fit, so the height decides its + // on screen size, keep it generous but let the dialog compress on small + // or high DPI screens + setMinimumSize(400, 180); + QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Expanding); + setSizePolicy(sp); + setFocusPolicy(Qt::StrongFocus); + setCursor(Qt::OpenHandCursor); + setAccessibleName(tr("Playmat crop")); + setAccessibleDescription(tr("Zoom %1×").arg(QString::number(params.zoom, 'f', 2))); } void PlaymatPreviewWidget::setPixmap(const QPixmap &pixmap) { sourcePixmap = pixmap; + setCursor(sourcePixmap.isNull() ? Qt::ArrowCursor : Qt::OpenHandCursor); update(); } void PlaymatPreviewWidget::setParams(const PlaymatParams &p) { params = p; + setAccessibleDescription(tr("Zoom %1×").arg(QString::number(params.zoom, 'f', 2))); update(); } @@ -31,6 +69,94 @@ void PlaymatPreviewWidget::setAttribution(const QString &attribution) update(); } +QRectF PlaymatPreviewWidget::activePlayArea() const +{ + // The viewport is a frame shaped like a fresh board's stack+table area + // (kMinTableAspect): the most generous framing the game will produce. + // Dimmed strips mark where a wider, developed table crops further. + const QRectF cardRect = QRectF(rect()).adjusted(3, 2, -3, -2); + return PlaymatUtils::aspectFitRect(cardRect.adjusted(6, 4, -4, -4), MIN_TABLE_ASPECT); +} + +qreal PlaymatPreviewWidget::samplingWindowSide() const +{ + if (sourcePixmap.isNull()) { + return 0.0; + } + // Same clamped window the render path uses, gestures and painting must + // never disagree about geometry. + return PlaymatUtils::playmatWindowSide(sourcePixmap.size(), params); +} + +qreal PlaymatPreviewWidget::widgetToSourceScale() const +{ + const qreal cropSide = samplingWindowSide(); + const QRectF area = activePlayArea(); + if (cropSide <= 0.0 || area.isEmpty()) { + return 0.0; + } + // Mirror coverFitRect(): the square crop into the (wider) viewport fills + // its width. + return area.width() / cropSide; +} + +void PlaymatPreviewWidget::applyCropDelta(qreal dMarginL, qreal dMarginR, qreal dOffset, qreal zoomFactor) +{ + PlaymatParams next = params; + if (dMarginL + dMarginR == 0.0) { + // Pure horizontal pan rebalances the margins along their + // sum constant segment. Individual bounds must not break that + // invariant, otherwise repeated corner drags let one margin grow + // without end, collapsing the viewing window and desynchronizing the + // visual zoom from the readout. + const qreal sum = params.marginPctL + params.marginPctR; + const qreal lo = qMax(0.0, sum - MAX_MARGIN); + const qreal hi = qMin(sum, MAX_MARGIN); + next.marginPctL = qBound(lo, params.marginPctL + dMarginL, hi); + next.marginPctR = sum - next.marginPctL; + } else { + next.marginPctL = qBound(0.0, params.marginPctL + dMarginL, MAX_MARGIN); + next.marginPctR = qBound(0.0, params.marginPctR + dMarginR, MAX_MARGIN); + } + next.verticalOffset = qBound(0.0, params.verticalOffset + dOffset, 1.0); + // Clamp through the shared helper so the floor tracks the new margins: + // zooming out stops exactly where the window reaches the card bounds. + next.zoom = params.zoom * zoomFactor; + if (!sourcePixmap.isNull()) { + next.zoom = PlaymatUtils::playmatClampedZoom(sourcePixmap.size(), next); + } + + if (sameCrop(next, params)) { + return; + } + + params = next; + setAccessibleDescription(tr("Zoom %1×").arg(QString::number(params.zoom, 'f', 2))); + update(); + emit paramsEdited(params); +} + +bool PlaymatPreviewWidget::sameCrop(const PlaymatParams &a, const PlaymatParams &b) const +{ + // Exact comparison on purpose: clamped assignments yield identical bits, + // while qFuzzyCompare based equality misbehaves around zero + return a.marginPctL == b.marginPctL && a.marginPctR == b.marginPctR && a.verticalOffset == b.verticalOffset && + a.zoom == b.zoom; +} + +void PlaymatPreviewWidget::restoreSnapshot() +{ + // The snapshot only ever holds values that passed the gesture clamps, + // so it is safe to restore verbatim + if (sameCrop(paramsAtFocusIn, params)) { + return; + } + params = paramsAtFocusIn; + setAccessibleDescription(tr("Zoom %1×").arg(QString::number(params.zoom, 'f', 2))); + update(); + emit paramsEdited(params); +} + void PlaymatPreviewWidget::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -56,23 +182,40 @@ void PlaymatPreviewWidget::paintEvent(QPaintEvent *) return; } - // Draw the playmat art using the same logic as PlayerGraphicsItem - // The preview area represents the combined stack+table play area - // Stack is ~20% width on the left, table is ~80% on the right - const QRectF playArea = cardRect.adjusted(6, 4, -4, -4); + const QRectF playArea = activePlayArea(); + // Exactly the game's pipeline (player_graphics_item): cover fit the crop + // into the table shaped viewport, centered, so the frame shows precisely + // what a minimum aspect window shows, and dragging moves the art behind + // the fixed frame. const QRectF srcRect = PlaymatUtils::computeArtSourceRect(sourcePixmap.size(), params); const QRectF dstRect = PlaymatUtils::coverFitRect(playArea, srcRect.size()); painter.setClipRect(playArea.toRect()); painter.drawPixmap(dstRect, sourcePixmap, srcRect); - painter.setClipping(false); + + // Wider (developed) tables crop further: mark where a kMaxTableAspect + // board stops. Palette driven so theme authors can recolor the markers. + const qreal wideBandHeight = playArea.height() * (MIN_TABLE_ASPECT / MAX_TABLE_ASPECT); + const qreal stripHeight = (playArea.height() - wideBandHeight) / 2.0; + QColor stripColor = palette().color(QPalette::Window); + stripColor.setAlpha(150); + painter.fillRect(QRectF(playArea.left(), playArea.top(), playArea.width(), stripHeight), stripColor); + painter.fillRect(QRectF(playArea.left(), playArea.bottom() - stripHeight, playArea.width(), stripHeight), + stripColor); + QColor hairlineColor = palette().color(QPalette::Highlight); + hairlineColor.setAlpha(110); + painter.setPen(QPen(hairlineColor, 1)); + painter.drawLine(QPointF(playArea.left(), playArea.top() + stripHeight), + QPointF(playArea.right(), playArea.top() + stripHeight)); + painter.drawLine(QPointF(playArea.left(), playArea.bottom() - stripHeight), + QPointF(playArea.right(), playArea.bottom() - stripHeight)); // Draw zone divider: stack is roughly the left portion const double stackWidthRatio = 0.18; // Stack is about 18% of total play area const double stackDividerX = playArea.left() + playArea.width() * stackWidthRatio; - // Subtle semi-transparent overlays to distinguish zones + // Subtle semi transparent overlays to distinguish zones // Stack zone overlay (slightly darker) QRectF stackOverlay(playArea.left(), playArea.top(), playArea.width() * stackWidthRatio, playArea.height()); painter.fillRect(stackOverlay, QColor(0, 0, 0, 40)); @@ -89,11 +232,150 @@ void PlaymatPreviewWidget::paintEvent(QPaintEvent *) const double landDividerY = playArea.top() + playArea.height() * 0.65; painter.setPen(QPen(QColor(255, 255, 255, 30), 1)); painter.drawLine(QPointF(stackDividerX, landDividerY), QPointF(playArea.right(), landDividerY)); + painter.setClipping(false); - // Border around entire play area + // Border around the viewport = boundary of every plausible framing. painter.setPen(QPen(QColor(70, 80, 95, 120), 1)); painter.setBrush(Qt::NoBrush); painter.drawRoundedRect(playArea.adjusted(0, 0, -1, -1), 3, 3); + // Visible keyboard focus per the focus cursor contract, Tab must show + // where the keys land + if (hasFocus()) { + QPen focusPen(palette().color(QPalette::Highlight), 2); + painter.setPen(focusPen); + painter.drawRoundedRect(playArea.adjusted(-1, -1, 1, 1), 3, 3); + } + paintArtAttribution(painter, playArea, attributionText, Qt::AlignRight | Qt::AlignBottom, 0.8); + + // Zoom readout so the gesture has a visible, stable counterpart. + QColor ink = palette().color(QPalette::WindowText); + ink.setAlpha(160); + painter.setPen(ink); + painter.drawText(QPointF(playArea.left() + 8, playArea.bottom() - 8), + tr("Zoom %1×").arg(QString::number(params.zoom, 'f', 2))); +} + +void PlaymatPreviewWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || sourcePixmap.isNull() || samplingWindowSide() <= 0.0) { + QWidget::mousePressEvent(event); + return; + } + lastDragPos = event->pos(); + setCursor(Qt::ClosedHandCursor); + event->accept(); +} + +void PlaymatPreviewWidget::mouseMoveEvent(QMouseEvent *event) +{ + if (!(event->buttons() & Qt::LeftButton) || sourcePixmap.isNull()) { + QWidget::mouseMoveEvent(event); + return; + } + + const QPointF delta = QPointF(event->pos() - lastDragPos); + lastDragPos = event->pos(); + + const qreal scale = widgetToSourceScale(); + // Vertical travel of the SAMPLING window: verticalOffset moves its top + // edge by exactly this much per unit, identical to the render path. + // Windows taller than the art (square/landscape sources zoomed out) + // leave no travel, vertical drags are then boundary no ops. + const qreal travel = static_cast(sourcePixmap.height()) - samplingWindowSide(); + if (scale <= 0.0) { + event->accept(); + return; + } + + // Dragging moves the ART with the cursor, so the viewing window slides the + // other way. Horizontal panning rebalances the margins (their sum, hence + // the window width, stays constant), vertical panning moves the window's + // top edge within its available travel. + const qreal sourceW = sourcePixmap.width(); + const qreal dMargin = -(delta.x() / scale) / sourceW; + const qreal dOffset = travel > 0.5 ? -(delta.y() / scale) / travel : 0.0; + + applyCropDelta(dMargin, -dMargin, dOffset, 1.0); + event->accept(); +} + +void PlaymatPreviewWidget::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + setCursor(sourcePixmap.isNull() ? Qt::ArrowCursor : Qt::OpenHandCursor); + event->accept(); + return; + } + QWidget::mouseReleaseEvent(event); +} + +void PlaymatPreviewWidget::wheelEvent(QWheelEvent *event) +{ + if (sourcePixmap.isNull() || samplingWindowSide() <= 0.0) { + QWidget::wheelEvent(event); + return; + } + const qreal notches = static_cast(event->angleDelta().y()) / 120.0; + if (notches == 0.0) { + event->accept(); + return; + } + applyCropDelta(0.0, 0.0, 0.0, std::pow(WHEEL_ZOOM_BASE, notches)); + event->accept(); +} + +void PlaymatPreviewWidget::keyPressEvent(QKeyEvent *event) +{ + if (sourcePixmap.isNull()) { + QWidget::keyPressEvent(event); + return; + } + + switch (event->key()) { + case Qt::Key_Escape: + if (sameCrop(params, paramsAtFocusIn)) { + // Nothing to undo on this surface, let the event reach the + // dialog so Esc keeps its close meaning there + QWidget::keyPressEvent(event); + return; + } + restoreSnapshot(); + break; + case Qt::Key_Backspace: + restoreSnapshot(); + break; + case Qt::Key_Left: + applyCropDelta(-KEY_PAN_MARGIN_STEP, KEY_PAN_MARGIN_STEP, 0.0, 1.0); + break; + case Qt::Key_Right: + applyCropDelta(KEY_PAN_MARGIN_STEP, -KEY_PAN_MARGIN_STEP, 0.0, 1.0); + break; + case Qt::Key_Up: + applyCropDelta(0.0, 0.0, -KEY_PAN_OFFSET_STEP, 1.0); + break; + case Qt::Key_Down: + applyCropDelta(0.0, 0.0, KEY_PAN_OFFSET_STEP, 1.0); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: + applyCropDelta(0.0, 0.0, 0.0, KEY_ZOOM_STEP); + break; + case Qt::Key_Minus: + applyCropDelta(0.0, 0.0, 0.0, 1.0 / KEY_ZOOM_STEP); + break; + default: + QWidget::keyPressEvent(event); + return; + } + event->accept(); +} + +void PlaymatPreviewWidget::focusInEvent(QFocusEvent *event) +{ + // Snapshot for the Esc or Backspace reset, restoring whatever the user + // had when the surface took focus + paramsAtFocusIn = params; + QWidget::focusInEvent(event); } diff --git a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h b/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h index 55771192f..88b9ec41d 100644 --- a/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h +++ b/cockatrice/src/interface/widgets/playmat/playmat_preview_widget.h @@ -1,16 +1,23 @@ #ifndef COCKATRICE_PLAYMAT_PREVIEW_WIDGET_H #define COCKATRICE_PLAYMAT_PREVIEW_WIDGET_H +#include #include #include #include /** - * @brief Preview widget that shows how a playmat card art will appear + * @brief Interactive crop surface showing how a playmat card art will appear * across the combined table + stack play area. * - * Renders a miniature mockup with the card art applied using the - * given PlaymatParams, including faint zone divider lines. + * Renders a fixed frame shaped like a fresh board's stack+table area (the + * most generous framing the game produces): it shows the tallest slice of + * the square crop in normal play, with dimmed strips marking where a wider, + * developed table crops further, exactly the game's own render pipeline. + * The widget doubles as the editor's primary crop control: dragging pans the + * art behind the frame, the wheel zooms, and arrow keys nudge, mirroring + * the stored parameters (margins pan horizontally, verticalOffset + * vertically, zoom scales) so no separate numeric controls are needed. */ class PlaymatPreviewWidget : public QWidget { @@ -23,13 +30,32 @@ public: void setParams(const PlaymatParams ¶ms); void setAttribution(const QString &attribution); +signals: + /** @brief Emitted whenever direct manipulation (drag, wheel, keys) changes the crop parameters. */ + void paramsEdited(const PlaymatParams ¶ms); + protected: void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void focusInEvent(QFocusEvent *event) override; private: + QRectF activePlayArea() const; ///< destination rect used for rendering AND gesture math + qreal samplingWindowSide() const; ///< clamped square window side, shared with the render path + qreal widgetToSourceScale() const; + void applyCropDelta(qreal dMarginL, qreal dMarginR, qreal dOffset, qreal zoomFactor); + bool sameCrop(const PlaymatParams &a, const PlaymatParams &b) const; + void restoreSnapshot(); + QPixmap sourcePixmap; PlaymatParams params; + PlaymatParams paramsAtFocusIn; ///< crop as of the latest focus gain, restored by Esc or Backspace QString attributionText; + QPoint lastDragPos; ///< widget space position of the previous mouse move while panning }; #endif // COCKATRICE_PLAYMAT_PREVIEW_WIDGET_H diff --git a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp b/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp index 72c715e13..57706cf93 100644 --- a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp +++ b/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.cpp @@ -7,6 +7,7 @@ #include "card_database_model.h" #include "playmat_preview_widget.h" +#include #include #include #include @@ -48,10 +49,6 @@ PlaymatSettingsDialog::PlaymatSettingsDialog(const CardRef &initialCard, reloadPreview(); } } - marginLSpin->setValue(initialParams.marginPctL); - marginRSpin->setValue(initialParams.marginPctR); - verticalOffsetSpin->setValue(initialParams.verticalOffset); - zoomSpin->setValue(initialParams.zoom); retranslateUi(); } @@ -112,23 +109,32 @@ void PlaymatSettingsDialog::setupUi() connect(providerComboBox, &QComboBox::currentIndexChanged, this, [this]() { currentCard.providerId = providerComboBox->currentData().toString(); reloadPreview(); - onParamChanged(); }); + 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); - auto *form = new QFormLayout; - 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); + + showNumericEditorsCheck = new QCheckBox; + + form->addRow(showNumericEditorsCheck); form->addRow(leftMarginLabel, marginLSpin); form->addRow(rightMarginLabel, marginRSpin); form->addRow(verticalOffsetLabel, verticalOffsetSpin); @@ -138,9 +144,14 @@ void PlaymatSettingsDialog::setupUi() 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); @@ -155,16 +166,35 @@ void PlaymatSettingsDialog::setupUi() accept(); }); - auto *root = new QVBoxLayout; - root->addWidget(controlsGroup); - root->addWidget(previewGroup); - root->addWidget(buttons); - setLayout(root); + // 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) @@ -261,17 +291,34 @@ void PlaymatSettingsDialog::onParamChanged() 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("Parameters")); - previewGroup->setTitle(tr("Preview")); + 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")); } diff --git a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h b/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h index 7ccd1569d..9ef306a5f 100644 --- a/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h +++ b/cockatrice/src/interface/widgets/playmat/playmat_settings_dialog.h @@ -5,13 +5,16 @@ #include #include +class QCheckBox; class QComboBox; class QCompleter; class QDoubleSpinBox; +class QFormLayout; class QGroupBox; class QLabel; class QLineEdit; class QPushButton; +class QWidget; class CardDatabaseModel; class CardDatabaseDisplayModel; class CardSearchModel; @@ -21,10 +24,11 @@ class PlaymatPreviewWidget; /** * @brief Dialog for configuring the playmat card art for a deck. * - * Allows the user to select a card from the database and adjust - * positioning parameters (margins, zoom, vertical offset) for how - * the card art appears as a playmat background across the - * combined table + stack play area. + * The crop surface is the primary control: drag to pan the visible art, + * scroll (or +/- keys) to zoom, arrow keys to nudge. Card name and printing + * are selected below. A checkbox reveals optional numerical editors for the + * raw PlaymatParams. These controls edit the same stored PlaymatParams that + * ship in deck files and player properties. */ class PlaymatSettingsDialog : public QDialog { @@ -40,14 +44,15 @@ public: private slots: void onCardNameChanged(const QString &name); - void reloadPreview(); void onParamChanged(); + void reloadPreview(); private: void setupUi(); void populateProviderCombo(const QString &cardName); void initializeSearchBar(); void retranslateUi(); + void setNumericEditorsVisible(bool visible); QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step); QLineEdit *searchBar; @@ -63,6 +68,9 @@ private: QLabel *cardNameLabel; QLabel *printingLabel; + QLabel *previewCaptionLabel; + QCheckBox *showNumericEditorsCheck; + QFormLayout *controlsForm; QLabel *leftMarginLabel; QLabel *rightMarginLabel; QLabel *verticalOffsetLabel; @@ -75,6 +83,7 @@ private: QDoubleSpinBox *marginRSpin; QDoubleSpinBox *verticalOffsetSpin; QDoubleSpinBox *zoomSpin; + PlaymatPreviewWidget *preview; QPixmap currentPixmap;