mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-12 13:00:01 -08:00
[Client] Preview token edits live and preserve custom drafts (#7737)
* Preview token edits live and preserve custom drafts * Restore the upstream reveal-random-hand-cards action The rebase onto upstream/master took the PR side of player_actions.cpp and dropped PlayerActions::actRevealRandomHandCards, which upstream added after the PR merge base. Re-apply the face-down P/T fix on the current file so the branch keeps both changes. * Draw the token preview P/T as the hover-panel pill The preview painted the name and P/T unconditionally: the name duplicated the artwork, and the raw P/T text was drawn even when it matched the printed value, which looked wrong at non-default dialog sizes. - Suppress the card name over a token picture via a small AbstractCardItem hook; cards without art still show it. - Draw only a P/T that differs from the printed getPowTough() as the #7700 hover-panel pill: dark rounded scrim, bold white text, anchored on the printed P/T box and clamped inside the card. - Use designated CardRef initializers in the new code. The face-up draft still previews while face-down creation is selected. --------- Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
This commit is contained in:
parent
3f3aff9408
commit
3d8c729517
9 changed files with 323 additions and 31 deletions
|
|
@ -86,6 +86,7 @@ set(cockatrice_SOURCES
|
||||||
src/game_graphics/deckview/tabbed_deck_view_container.cpp
|
src/game_graphics/deckview/tabbed_deck_view_container.cpp
|
||||||
src/game_graphics/dialogs/dlg_annotation.cpp
|
src/game_graphics/dialogs/dlg_annotation.cpp
|
||||||
src/game_graphics/dialogs/dlg_create_token.cpp
|
src/game_graphics/dialogs/dlg_create_token.cpp
|
||||||
|
src/game_graphics/dialogs/token_preview_widget.cpp
|
||||||
src/game_graphics/dialogs/dlg_move_top_cards_until.cpp
|
src/game_graphics/dialogs/dlg_move_top_cards_until.cpp
|
||||||
src/game_graphics/dialogs/dlg_roll_dice.cpp
|
src/game_graphics/dialogs/dlg_roll_dice.cpp
|
||||||
src/game_graphics/game_scene.cpp
|
src/game_graphics/game_scene.cpp
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,11 @@ QSizeF AbstractCardItem::getTranslatedSize(QPainter *painter) const
|
||||||
painter->combinedTransform().map(QLineF(0, 0, 0, boundingRect().height())).length());
|
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)
|
void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle)
|
||||||
{
|
{
|
||||||
const int MAX_FONT_SIZE = SettingsCache::instance().appearance().getMaxFontSize();
|
const int MAX_FONT_SIZE = SettingsCache::instance().appearance().getMaxFontSize();
|
||||||
|
|
@ -233,8 +238,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
||||||
painter->drawPath(shape());
|
painter->drawPath(shape());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() ||
|
if (translatedPixmap.isNull() || state->getFaceDown() || shouldPaintNameOverPicture()) {
|
||||||
state->getFaceDown()) {
|
|
||||||
painter->save();
|
painter->save();
|
||||||
transformPainter(painter, translatedSize, angle);
|
transformPainter(painter, translatedSize, angle);
|
||||||
painter->setPen(Qt::white);
|
painter->setPen(Qt::white);
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,13 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle);
|
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 mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#include "dlg_create_token.h"
|
#include "dlg_create_token.h"
|
||||||
|
|
||||||
#include "../../client/settings/cache_settings.h"
|
#include "../../client/settings/cache_settings.h"
|
||||||
#include "../../interface/widgets/cards/card_info_picture_widget.h"
|
|
||||||
#include "libcockatrice/card/card_info.h"
|
#include "libcockatrice/card/card_info.h"
|
||||||
#include "libcockatrice/card/database/card_database_querier.h"
|
#include "libcockatrice/card/database/card_database_querier.h"
|
||||||
|
#include "token_preview_widget.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QChar>
|
#include <QChar>
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include <QModelIndex>
|
#include <QModelIndex>
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
#include <QSignalBlocker>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
#include <libcockatrice/settings/cards_display_settings.h>
|
#include <libcockatrice/settings/cards_display_settings.h>
|
||||||
#include <libcockatrice/settings/interface_settings.h>
|
#include <libcockatrice/settings/interface_settings.h>
|
||||||
#include <libcockatrice/settings/layouts_settings.h>
|
#include <libcockatrice/settings/layouts_settings.h>
|
||||||
|
#include <libcockatrice/utility/card_ref.h>
|
||||||
#include <libcockatrice/utility/string_limits.h>
|
#include <libcockatrice/utility/string_limits.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
|
|
||||||
|
|
@ -41,7 +43,7 @@ class QWidget;
|
||||||
DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent)
|
DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent)
|
||||||
: QDialog(parent), predefinedTokens(_predefinedTokens)
|
: QDialog(parent), predefinedTokens(_predefinedTokens)
|
||||||
{
|
{
|
||||||
pic = new CardInfoPictureWidget();
|
pic = new TokenPreviewWidget();
|
||||||
pic->setObjectName("pic");
|
pic->setObjectName("pic");
|
||||||
|
|
||||||
nameLabel = new QLabel(tr("&Name:"));
|
nameLabel = new QLabel(tr("&Name:"));
|
||||||
|
|
@ -173,6 +175,12 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
||||||
|
|
||||||
setWindowTitle(tr("Create token"));
|
setWindowTitle(tr("Create token"));
|
||||||
|
|
||||||
|
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);
|
||||||
|
updatePreview();
|
||||||
|
|
||||||
resize(600, 500);
|
resize(600, 500);
|
||||||
restoreGeometry(SettingsCache::instance().layouts().getTokenDialogGeometry());
|
restoreGeometry(SettingsCache::instance().layouts().getTokenDialogGeometry());
|
||||||
}
|
}
|
||||||
|
|
@ -185,17 +193,14 @@ void DlgCreateToken::closeEvent(QCloseEvent *event)
|
||||||
|
|
||||||
void DlgCreateToken::faceDownCheckBoxToggled(bool checked)
|
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) {
|
if (checked) {
|
||||||
colorEdit->setCurrentIndex(6);
|
|
||||||
colorEdit->setEnabled(false);
|
|
||||||
ptEdit->clear();
|
|
||||||
ptEdit->clearFocus();
|
ptEdit->clearFocus();
|
||||||
ptEdit->setEnabled(false);
|
|
||||||
} else {
|
|
||||||
colorEdit->setEnabled(true);
|
|
||||||
ptEdit->setEnabled(true);
|
|
||||||
annotationEdit->setEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
updatePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||||
|
|
@ -204,11 +209,14 @@ void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QMo
|
||||||
|
|
||||||
CardInfoPtr cardInfo;
|
CardInfoPtr cardInfo;
|
||||||
|
|
||||||
if (current.row() >= 0) {
|
if (realIndex.isValid()) {
|
||||||
cardInfo = cardDatabaseModel->getCard(realIndex.row());
|
cardInfo = cardDatabaseModel->getCard(realIndex.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardInfo) {
|
if (cardInfo) {
|
||||||
|
const QSignalBlocker colorBlocker(colorEdit);
|
||||||
|
const QSignalBlocker ptBlocker(ptEdit);
|
||||||
|
const QSignalBlocker annotationBlocker(annotationEdit);
|
||||||
updateSearchFieldWithoutUpdatingFilter(cardInfo->getName());
|
updateSearchFieldWithoutUpdatingFilter(cardInfo->getName());
|
||||||
const QChar cardColor = cardInfo->getColorChar();
|
const QChar cardColor = cardInfo->getColorChar();
|
||||||
colorEdit->setCurrentIndex(colorEdit->findData(cardColor, Qt::UserRole, Qt::MatchFixedString));
|
colorEdit->setCurrentIndex(colorEdit->findData(cardColor, Qt::UserRole, Qt::MatchFixedString));
|
||||||
|
|
@ -216,21 +224,22 @@ void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QMo
|
||||||
if (SettingsCache::instance().userInterface().getAnnotateTokens()) {
|
if (SettingsCache::instance().userInterface().getAnnotateTokens()) {
|
||||||
annotationEdit->setText(cardInfo->getText());
|
annotationEdit->setText(cardInfo->getText());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
nameEdit->setText("");
|
|
||||||
colorEdit->setCurrentIndex(colorEdit->findData(QString(), Qt::UserRole, Qt::MatchFixedString));
|
|
||||||
ptEdit->setText("");
|
|
||||||
annotationEdit->setText("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pic->setCard(CardDatabaseManager::query()->getPreferredCard(cardInfo));
|
// Filtering, changing the deck/all scope, or reloading the database can
|
||||||
|
// invalidate the current row. None of these actions discard the token draft.
|
||||||
|
updatePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DlgCreateToken::updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const
|
void DlgCreateToken::updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const
|
||||||
{
|
{
|
||||||
nameEdit->blockSignals(true);
|
const QSignalBlocker blocker(nameEdit);
|
||||||
nameEdit->setText(newValue);
|
nameEdit->setText(newValue);
|
||||||
nameEdit->blockSignals(false);
|
}
|
||||||
|
|
||||||
|
void DlgCreateToken::updatePreview()
|
||||||
|
{
|
||||||
|
pic->setToken(tokenDraft());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DlgCreateToken::updateSearch(const QString &search)
|
void DlgCreateToken::updateSearch(const QString &search)
|
||||||
|
|
@ -264,13 +273,36 @@ void DlgCreateToken::actReject()
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenInfo DlgCreateToken::tokenDraft() const
|
||||||
|
{
|
||||||
|
TokenInfo token = {.name = nameEdit->text(),
|
||||||
|
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
|
||||||
|
.pt = ptEdit->text(),
|
||||||
|
.annotation = annotationEdit->text(),
|
||||||
|
.destroy = destroyCheckBox->isChecked(),
|
||||||
|
.faceDown = faceDownCheckBox->isChecked(),
|
||||||
|
.providerId =
|
||||||
|
SettingsCache::instance().cardOverrides().getCardPreferenceOverride(nameEdit->text())};
|
||||||
|
|
||||||
|
// 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 DlgCreateToken::getTokenInfo() const
|
||||||
{
|
{
|
||||||
return {.name = nameEdit->text(),
|
TokenInfo token = tokenDraft();
|
||||||
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
|
if (token.faceDown) {
|
||||||
.pt = ptEdit->text(),
|
token.color = QString(QChar());
|
||||||
.annotation = annotationEdit->text(),
|
token.pt.clear();
|
||||||
.destroy = destroyCheckBox->isChecked(),
|
}
|
||||||
.faceDown = faceDownCheckBox->isChecked(),
|
return token;
|
||||||
.providerId = SettingsCache::instance().cardOverrides().getCardPreferenceOverride(nameEdit->text())};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class QRadioButton;
|
||||||
class QTreeView;
|
class QTreeView;
|
||||||
class CardDatabaseModel;
|
class CardDatabaseModel;
|
||||||
class TokenDisplayModel;
|
class TokenDisplayModel;
|
||||||
class CardInfoPictureWidget;
|
class TokenPreviewWidget;
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
|
|
||||||
|
|
@ -39,6 +39,7 @@ private slots:
|
||||||
void faceDownCheckBoxToggled(bool checked);
|
void faceDownCheckBoxToggled(bool checked);
|
||||||
void tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
void tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
void updateSearch(const QString &search);
|
void updateSearch(const QString &search);
|
||||||
|
void updatePreview();
|
||||||
void actChooseTokenFromAll(bool checked);
|
void actChooseTokenFromAll(bool checked);
|
||||||
void actChooseTokenFromDeck(bool checked);
|
void actChooseTokenFromDeck(bool checked);
|
||||||
void actOk();
|
void actOk();
|
||||||
|
|
@ -54,9 +55,10 @@ private:
|
||||||
QCheckBox *destroyCheckBox;
|
QCheckBox *destroyCheckBox;
|
||||||
QCheckBox *faceDownCheckBox;
|
QCheckBox *faceDownCheckBox;
|
||||||
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
|
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
|
||||||
CardInfoPictureWidget *pic;
|
TokenPreviewWidget *pic;
|
||||||
QTreeView *chooseTokenView;
|
QTreeView *chooseTokenView;
|
||||||
|
|
||||||
|
[[nodiscard]] TokenInfo tokenDraft() const;
|
||||||
void updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const;
|
void updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
159
cockatrice/src/game_graphics/dialogs/token_preview_widget.cpp
Normal file
159
cockatrice/src/game_graphics/dialogs/token_preview_widget.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
#include "token_preview_widget.h"
|
||||||
|
|
||||||
|
#include "../../interface/theme_manager.h"
|
||||||
|
#include "../board/abstract_card_item.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QFontMetricsF>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QString>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <libcockatrice/card/card_info.h>
|
||||||
|
#include <libcockatrice/utility/card_ref.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TokenPreviewCardItem()
|
||||||
|
{
|
||||||
|
setFlag(ItemIsSelectable, false);
|
||||||
|
setAcceptedMouseButtons(Qt::NoButton);
|
||||||
|
setAcceptHoverEvents(false);
|
||||||
|
setCursor(Qt::ArrowCursor);
|
||||||
|
connect(state, &CardState::stateChanged, this, [this] { update(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
|
||||||
|
{
|
||||||
|
// Show what the user is creating even when it will enter face down.
|
||||||
|
// Reuse the board's artwork, custom-token background and name rendering.
|
||||||
|
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);
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenPreviewWidget::TokenPreviewWidget(QWidget *parent) : QGraphicsView(parent), card(new TokenPreviewCardItem)
|
||||||
|
{
|
||||||
|
auto *previewScene = new QGraphicsScene(this);
|
||||||
|
previewScene->addItem(card);
|
||||||
|
previewScene->setSceneRect(card->boundingRect());
|
||||||
|
setScene(previewScene);
|
||||||
|
setInteractive(false);
|
||||||
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
setFrameShape(QFrame::NoFrame);
|
||||||
|
viewport()->setBackgroundRole(QPalette::Window);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setMinimumSize(150, 200);
|
||||||
|
setAccessibleName(tr("Token preview"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TokenPreviewWidget::setToken(const TokenInfo &token)
|
||||||
|
{
|
||||||
|
card->setCardRef(CardRef{.name = token.name, .providerId = token.providerId});
|
||||||
|
card->setColor(token.color);
|
||||||
|
card->setFaceDown(false);
|
||||||
|
card->getState()->setPT(token.pt);
|
||||||
|
card->getState()->setAnnotation(token.annotation);
|
||||||
|
setToolTip(token.annotation);
|
||||||
|
QString description = token.name + " " + token.pt + " " + token.annotation;
|
||||||
|
if (token.faceDown) {
|
||||||
|
description += " " + tr("Will be created face down.");
|
||||||
|
}
|
||||||
|
setAccessibleDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize TokenPreviewWidget::sizeHint() const
|
||||||
|
{
|
||||||
|
return {200, 280};
|
||||||
|
}
|
||||||
|
|
||||||
|
void TokenPreviewWidget::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsView::resizeEvent(event);
|
||||||
|
fitInView(sceneRect(), Qt::KeepAspectRatio);
|
||||||
|
}
|
||||||
25
cockatrice/src/game_graphics/dialogs/token_preview_widget.h
Normal file
25
cockatrice/src/game_graphics/dialogs/token_preview_widget.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef COCKATRICE_TOKEN_PREVIEW_WIDGET_H
|
||||||
|
#define COCKATRICE_TOKEN_PREVIEW_WIDGET_H
|
||||||
|
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <libcockatrice/game/player/token_info.h>
|
||||||
|
|
||||||
|
class TokenPreviewCardItem;
|
||||||
|
|
||||||
|
/** A noninteractive preview of the token that the dialog will create. */
|
||||||
|
class TokenPreviewWidget : public QGraphicsView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TokenPreviewWidget(QWidget *parent = nullptr);
|
||||||
|
void setToken(const TokenInfo &token);
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TokenPreviewCardItem *card;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -951,7 +951,7 @@ void PlayerActions::actCreateToken(TokenInfo tokenToCreate)
|
||||||
lastTokenInfo.name = correctedCard.getName();
|
lastTokenInfo.name = correctedCard.getName();
|
||||||
int tableRow = lastTokenInfo.faceDown ? 2 : correctedCard.getInfo().getUiAttributes().tableRow;
|
int tableRow = lastTokenInfo.faceDown ? 2 : correctedCard.getInfo().getUiAttributes().tableRow;
|
||||||
lastTokenTableRow = TableZoneLogic::tableRowToGridY(tableRow);
|
lastTokenTableRow = TableZoneLogic::tableRowToGridY(tableRow);
|
||||||
if (lastTokenInfo.pt.isEmpty()) {
|
if (!lastTokenInfo.faceDown && lastTokenInfo.pt.isEmpty()) {
|
||||||
lastTokenInfo.pt = correctedCard.getInfo().getPowTough();
|
lastTokenInfo.pt = correctedCard.getInfo().getPowTough();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <libcockatrice/game/player/player_actions.h>
|
#include <libcockatrice/game/player/player_actions.h>
|
||||||
#include <libcockatrice/game/zones/table_zone_logic.h>
|
#include <libcockatrice/game/zones/table_zone_logic.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -117,3 +118,64 @@ TEST(PlayerActionsTest, PlayCiptCreatureSendsTapped)
|
||||||
EXPECT_TRUE(card.has_tapped());
|
EXPECT_TRUE(card.has_tapped());
|
||||||
EXPECT_TRUE(card.tapped());
|
EXPECT_TRUE(card.tapped());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These three #7737 command cases use the current shared game harness.
|
||||||
|
TEST(TokenCommandTest, FaceDownKnownTokenDoesNotRestoreDatabasePowerToughness)
|
||||||
|
{
|
||||||
|
GameHarness harness;
|
||||||
|
harness.client()->sent.clear();
|
||||||
|
TokenInfo token;
|
||||||
|
token.name = QStringLiteral("Kitten");
|
||||||
|
token.color = QString(QChar());
|
||||||
|
token.faceDown = true;
|
||||||
|
harness.player()->getPlayerActions()->actCreateToken(token);
|
||||||
|
ASSERT_EQ(harness.client()->sent.size(), 1);
|
||||||
|
ASSERT_EQ(harness.lastCommand().game_command_size(), 1);
|
||||||
|
const auto &gameCommand = harness.lastCommand().game_command(0);
|
||||||
|
ASSERT_TRUE(gameCommand.HasExtension(Command_CreateToken::ext));
|
||||||
|
const auto &command = gameCommand.GetExtension(Command_CreateToken::ext);
|
||||||
|
EXPECT_EQ(command.card_name(), "Kitten");
|
||||||
|
EXPECT_TRUE(command.face_down());
|
||||||
|
EXPECT_TRUE(command.pt().empty());
|
||||||
|
harness.player()->getPlayerActions()->actCreateAnotherToken();
|
||||||
|
ASSERT_EQ(harness.client()->sent.size(), 2);
|
||||||
|
const auto &repeat = harness.lastCommand().game_command(0).GetExtension(Command_CreateToken::ext);
|
||||||
|
EXPECT_TRUE(repeat.face_down());
|
||||||
|
EXPECT_TRUE(repeat.pt().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TokenCommandTest, FaceUpKnownAliasKeepsCanonicalNameAndDefaultPowerToughness)
|
||||||
|
{
|
||||||
|
GameHarness harness;
|
||||||
|
harness.client()->sent.clear();
|
||||||
|
TokenInfo token;
|
||||||
|
token.name = QStringLiteral("kitten");
|
||||||
|
harness.player()->getPlayerActions()->actCreateToken(token);
|
||||||
|
ASSERT_EQ(harness.client()->sent.size(), 1);
|
||||||
|
ASSERT_EQ(harness.lastCommand().game_command_size(), 1);
|
||||||
|
const auto &gameCommand = harness.lastCommand().game_command(0);
|
||||||
|
ASSERT_TRUE(gameCommand.HasExtension(Command_CreateToken::ext));
|
||||||
|
const auto &command = gameCommand.GetExtension(Command_CreateToken::ext);
|
||||||
|
EXPECT_EQ(command.card_name(), "Kitten");
|
||||||
|
EXPECT_FALSE(command.face_down());
|
||||||
|
EXPECT_EQ(command.pt(), "1/1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TokenCommandTest, FaceUpKnownTokenPreservesExplicitPowerToughness)
|
||||||
|
{
|
||||||
|
GameHarness harness;
|
||||||
|
harness.client()->sent.clear();
|
||||||
|
TokenInfo token;
|
||||||
|
token.name = QStringLiteral("Kitten");
|
||||||
|
token.pt = QStringLiteral("7/9");
|
||||||
|
token.annotation = QStringLiteral("Flying");
|
||||||
|
harness.player()->getPlayerActions()->actCreateToken(token);
|
||||||
|
ASSERT_EQ(harness.client()->sent.size(), 1);
|
||||||
|
ASSERT_EQ(harness.lastCommand().game_command_size(), 1);
|
||||||
|
const auto &gameCommand = harness.lastCommand().game_command(0);
|
||||||
|
ASSERT_TRUE(gameCommand.HasExtension(Command_CreateToken::ext));
|
||||||
|
const auto &command = gameCommand.GetExtension(Command_CreateToken::ext);
|
||||||
|
EXPECT_FALSE(command.face_down());
|
||||||
|
EXPECT_EQ(command.pt(), "7/9");
|
||||||
|
EXPECT_EQ(command.annotation(), "Flying");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue