diff --git a/cockatrice/src/game_graphics/board/abstract_card_item.cpp b/cockatrice/src/game_graphics/board/abstract_card_item.cpp index f2c0966e4..39a83be0a 100644 --- a/cockatrice/src/game_graphics/board/abstract_card_item.cpp +++ b/cockatrice/src/game_graphics/board/abstract_card_item.cpp @@ -181,6 +181,11 @@ QSizeF AbstractCardItem::getTranslatedSize(QPainter *painter) const painter->combinedTransform().map(QLineF(0, 0, 0, boundingRect().height())).length()); } +bool AbstractCardItem::shouldPaintNameOverPicture() const +{ + return SettingsCache::instance().cardsDisplay().getDisplayCardNames(); +} + void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle) { const int MAX_FONT_SIZE = SettingsCache::instance().appearance().getMaxFontSize(); @@ -233,8 +238,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS painter->drawPath(shape()); } - if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || - state->getFaceDown()) { + if (translatedPixmap.isNull() || state->getFaceDown() || shouldPaintNameOverPicture()) { painter->save(); transformPainter(painter, translatedSize, angle); painter->setPen(Qt::white); diff --git a/cockatrice/src/game_graphics/board/abstract_card_item.h b/cockatrice/src/game_graphics/board/abstract_card_item.h index 82058f08a..9ace939a0 100644 --- a/cockatrice/src/game_graphics/board/abstract_card_item.h +++ b/cockatrice/src/game_graphics/board/abstract_card_item.h @@ -190,6 +190,13 @@ public: protected: void transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle); + /** + * @brief Whether the card name is painted over its artwork. + * + * Follows the "Display card names on cards having a picture" setting. Views + * that already show the name elsewhere, such as the token preview, turn it off. + */ + [[nodiscard]] virtual bool shouldPaintNameOverPicture() const; void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override; diff --git a/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp b/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp index e9ed36d19..1516dca83 100644 --- a/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp +++ b/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -285,7 +286,8 @@ TokenInfo DlgCreateToken::tokenDraft() const // 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({token.name, token.providerId}); + const ExactCard correctedCard = + CardDatabaseManager::query()->guessCard(CardRef{.name = token.name, .providerId = token.providerId}); if (correctedCard) { token.name = correctedCard.getName(); if (token.pt.isEmpty()) { diff --git a/cockatrice/src/game_graphics/dialogs/token_preview_widget.cpp b/cockatrice/src/game_graphics/dialogs/token_preview_widget.cpp index 6979be724..a4d869ca2 100644 --- a/cockatrice/src/game_graphics/dialogs/token_preview_widget.cpp +++ b/cockatrice/src/game_graphics/dialogs/token_preview_widget.cpp @@ -1,11 +1,34 @@ #include "token_preview_widget.h" +#include "../../interface/theme_manager.h" #include "../board/abstract_card_item.h" +#include +#include +#include #include #include +#include #include +#include +#include #include +#include + +namespace +{ +// P/T pill, matching the #7700 hover panel. Its sizes are given for a 270 px picture +// (CardHoverWidget::CARD_IMAGE_WIDTH) and scale with the preview. +constexpr qreal PILL_REFERENCE_WIDTH = 270.0; +constexpr qreal PILL_MIN_WIDTH = 48.0; +constexpr qreal PILL_MIN_HEIGHT = 22.0; +constexpr qreal PILL_TEXT_PADDING = 4.0; +constexpr qreal PILL_CORNER_RADIUS = 4.0; +constexpr qreal PILL_FONT_PIXELS = 12.0; +constexpr qreal PILL_ANCHOR_X = 0.92; +constexpr qreal PILL_ANCHOR_Y = 0.94; +constexpr int PILL_SCRIM_ALPHA = 180; +} // namespace class TokenPreviewCardItem : public AbstractCardItem { @@ -26,19 +49,69 @@ public: AbstractCardItem::paint(painter, option, widget); const QSizeF translatedSize = getTranslatedSize(painter); + if (translatedSize.isEmpty()) { + return; + } const qreal scale = translatedSize.width() / boundingRect().width(); + painter->save(); transformPainter(painter, translatedSize, 0); - painter->setPen(Qt::white); - painter->setBackground(Qt::black); - painter->setBackgroundMode(Qt::OpaqueMode); - const QRectF textRect(4 * scale, 4 * scale, translatedSize.width() - 8 * scale, - translatedSize.height() - 8 * scale); - painter->drawText(textRect, Qt::AlignCenter | Qt::TextWrapAnywhere, state->getAnnotation()); - if (state->getPT() != getCardInfo().getPowTough()) { - painter->setPen(QColor(255, 150, 0)); + + if (!state->getAnnotation().isEmpty()) { + painter->setPen(Qt::white); + painter->setBackground(Qt::black); + painter->setBackgroundMode(Qt::OpaqueMode); + const QRectF textRect(4 * scale, 4 * scale, translatedSize.width() - 8 * scale, + translatedSize.height() - 8 * scale); + painter->drawText(textRect, Qt::AlignCenter | Qt::TextWrapAnywhere, state->getAnnotation()); } - painter->drawText(textRect, Qt::AlignRight | Qt::AlignBottom, state->getPT()); + + // The card picture already shows the printed P/T, so only a different value + // needs the pill. A custom token with no picture also has no printed value. + const QString pt = state->getPT(); + if (!pt.isEmpty() && pt != getCardInfo().getPowTough()) { + paintPtPill(painter, translatedSize, pt); + } + + painter->restore(); + } + +protected: + // The artwork carries the name; the dialog's own fields already show it. + [[nodiscard]] bool shouldPaintNameOverPicture() const override + { + return false; + } + +private: + void paintPtPill(QPainter *painter, const QSizeF &translatedSize, const QString &pt) const + { + const qreal ratio = translatedSize.width() / PILL_REFERENCE_WIDTH; + + QFont font = themeManager->appFont(AppFont::CardText); + font.setBold(true); + font.setPixelSize(std::max(1, qRound(PILL_FONT_PIXELS * ratio))); + + const QFontMetricsF metrics(font); + const QSizeF pillSize( + qMax(PILL_MIN_WIDTH * ratio, metrics.horizontalAdvance(pt) + 2 * PILL_TEXT_PADDING * ratio), + qMax(PILL_MIN_HEIGHT * ratio, metrics.height())); + const QPointF anchor(translatedSize.width() * PILL_ANCHOR_X, translatedSize.height() * PILL_ANCHOR_Y); + // Centred on the card's printed P/T box, then kept inside the card. + const QRectF pill(qBound(0.0, anchor.x() - pillSize.width() / 2, translatedSize.width() - pillSize.width()), + qBound(0.0, anchor.y() - pillSize.height() / 2, translatedSize.height() - pillSize.height()), + pillSize.width(), pillSize.height()); + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setBackgroundMode(Qt::TransparentMode); + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(0, 0, 0, PILL_SCRIM_ALPHA)); + const qreal radius = PILL_CORNER_RADIUS * ratio; + painter->drawRoundedRect(pill, radius, radius); + painter->setFont(font); + painter->setPen(Qt::white); + painter->drawText(pill, Qt::AlignCenter, pt); painter->restore(); } }; @@ -61,7 +134,7 @@ TokenPreviewWidget::TokenPreviewWidget(QWidget *parent) : QGraphicsView(parent), void TokenPreviewWidget::setToken(const TokenInfo &token) { - card->setCardRef({token.name, token.providerId}); + card->setCardRef(CardRef{.name = token.name, .providerId = token.providerId}); card->setColor(token.color); card->setFaceDown(false); card->getState()->setPT(token.pt);