Merge branch 'master' into card-rounding-option

This commit is contained in:
Basile Clement 2025-03-21 09:14:49 +01:00 committed by GitHub
commit 8389884aae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1608 additions and 1142 deletions

File diff suppressed because it is too large Load diff

View file

@ -47,12 +47,10 @@
# card_info = false # card_info = false
# card_list = false # card_list = false
# stack_zone = false
flow_layout.debug = false flow_layout.debug = false
flow_widget.debug = false flow_widget.debug = false
flow_widget.size.debug = false flow_widget.size.debug = false
# pixel_map_generator = false # pixel_map_generator = false
# filter_string = false # filter_string = false

View file

@ -55,6 +55,9 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
aExportDeckDecklist = new QAction(QString(), this); aExportDeckDecklist = new QAction(QString(), this);
connect(aExportDeckDecklist, SIGNAL(triggered()), deckEditor, SLOT(actExportDeckDecklist())); connect(aExportDeckDecklist, SIGNAL(triggered()), deckEditor, SLOT(actExportDeckDecklist()));
aExportDeckDecklistXyz = new QAction(QString(), this);
connect(aExportDeckDecklistXyz, SIGNAL(triggered()), deckEditor, SLOT(actExportDeckDecklistXyz()));
aAnalyzeDeckDeckstats = new QAction(QString(), this); aAnalyzeDeckDeckstats = new QAction(QString(), this);
connect(aAnalyzeDeckDeckstats, SIGNAL(triggered()), deckEditor, SLOT(actAnalyzeDeckDeckstats())); connect(aAnalyzeDeckDeckstats, SIGNAL(triggered()), deckEditor, SLOT(actAnalyzeDeckDeckstats()));
@ -63,6 +66,8 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
analyzeDeckMenu = new QMenu(this); analyzeDeckMenu = new QMenu(this);
analyzeDeckMenu->addAction(aExportDeckDecklist); analyzeDeckMenu->addAction(aExportDeckDecklist);
analyzeDeckMenu->addAction(aExportDeckDecklistXyz);
analyzeDeckMenu->addSeparator();
analyzeDeckMenu->addAction(aAnalyzeDeckDeckstats); analyzeDeckMenu->addAction(aAnalyzeDeckDeckstats);
analyzeDeckMenu->addAction(aAnalyzeDeckTappedout); analyzeDeckMenu->addAction(aAnalyzeDeckTappedout);
@ -160,6 +165,7 @@ void DeckEditorMenu::retranslateUi()
analyzeDeckMenu->setTitle(tr("&Send deck to online service")); analyzeDeckMenu->setTitle(tr("&Send deck to online service"));
aExportDeckDecklist->setText(tr("Create decklist (decklist.org)")); aExportDeckDecklist->setText(tr("Create decklist (decklist.org)"));
aExportDeckDecklistXyz->setText(tr("Create decklist (decklist.xyz)"));
aAnalyzeDeckDeckstats->setText(tr("Analyze deck (deckstats.net)")); aAnalyzeDeckDeckstats->setText(tr("Analyze deck (deckstats.net)"));
aAnalyzeDeckTappedout->setText(tr("Analyze deck (tappedout.net)")); aAnalyzeDeckTappedout->setText(tr("Analyze deck (tappedout.net)"));
@ -172,13 +178,17 @@ void DeckEditorMenu::refreshShortcuts()
aNewDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aNewDeck")); aNewDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aNewDeck"));
aLoadDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeck")); aLoadDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeck"));
aSaveDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeck")); aSaveDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeck"));
aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist"));
aSaveDeckAs->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckAs")); aSaveDeckAs->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckAs"));
aLoadDeckFromClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromClipboard")); aLoadDeckFromClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromClipboard"));
aEditDeckInClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboard")); aEditDeckInClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboard"));
aEditDeckInClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboardRaw")); aEditDeckInClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboardRaw"));
aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck")); aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck"));
aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist"));
aExportDeckDecklistXyz->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklistXyz"));
aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck")); aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck"));
aAnalyzeDeckTappedout->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeckTappedout"));
aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose")); aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose"));
aSaveDeckToClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckToClipboard")); aSaveDeckToClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckToClipboard"));

View file

@ -17,7 +17,7 @@ public:
QAction *aNewDeck, *aLoadDeck, *aClearRecents, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, QAction *aNewDeck, *aLoadDeck, *aClearRecents, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard,
*aEditDeckInClipboard, *aEditDeckInClipboardRaw, *aSaveDeckToClipboard, *aSaveDeckToClipboardNoSetInfo, *aEditDeckInClipboard, *aEditDeckInClipboardRaw, *aSaveDeckToClipboard, *aSaveDeckToClipboardNoSetInfo,
*aSaveDeckToClipboardRaw, *aSaveDeckToClipboardRawNoSetInfo, *aPrintDeck, *aExportDeckDecklist, *aSaveDeckToClipboardRaw, *aSaveDeckToClipboardRawNoSetInfo, *aPrintDeck, *aExportDeckDecklist,
*aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout, *aClose; *aExportDeckDecklistXyz, *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout, *aClose;
QMenu *loadRecentDeckMenu, *analyzeDeckMenu, *editDeckInClipboardMenu, *saveDeckToClipboardMenu; QMenu *loadRecentDeckMenu, *analyzeDeckMenu, *editDeckInClipboardMenu, *saveDeckToClipboardMenu;
void setSaveStatus(bool newStatus); void setSaveStatus(bool newStatus);

View file

@ -455,17 +455,12 @@ void AbstractTabDeckEditor::actPrintDeck()
dlg->exec(); dlg->exec();
} }
// Action called when export deck to decklist menu item is pressed. void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website)
void AbstractTabDeckEditor::actExportDeckDecklist()
{ {
// Get the decklist class for the deck.
DeckLoader *const deck = getDeckList();
// create a string to load the decklist url into.
QString decklistUrlString;
// check if deck is not null // check if deck is not null
if (deck) { if (DeckLoader *const deck = getDeckList()) {
// Get the decklist url string from the deck loader class. // Get the decklist url string from the deck loader class.
decklistUrlString = deck->exportDeckToDecklist(); QString decklistUrlString = deck->exportDeckToDecklist(website);
// Check to make sure the string isn't empty. // Check to make sure the string isn't empty.
if (QString::compare(decklistUrlString, "", Qt::CaseInsensitive) == 0) { if (QString::compare(decklistUrlString, "", Qt::CaseInsensitive) == 0) {
// Show an error if the deck is empty, and return. // Show an error if the deck is empty, and return.
@ -481,10 +476,26 @@ void AbstractTabDeckEditor::actExportDeckDecklist()
QDesktopServices::openUrl(decklistUrlString); QDesktopServices::openUrl(decklistUrlString);
} else { } else {
// if there's no deck loader object, return an error // if there's no deck loader object, return an error
QMessageBox::critical(this, tr("Error"), tr("No deck was selected to be saved.")); QMessageBox::critical(this, tr("Error"), tr("No deck was selected to be exported."));
} }
} }
/**
* Exports the deck to www.decklist.org (the old website)
*/
void AbstractTabDeckEditor::actExportDeckDecklist()
{
exportToDecklistWebsite(DeckLoader::DecklistOrg);
}
/**
* Exports the deck to www.decklist.xyz (the new website)
*/
void AbstractTabDeckEditor::actExportDeckDecklistXyz()
{
exportToDecklistWebsite(DeckLoader::DecklistXyz);
}
void AbstractTabDeckEditor::actAnalyzeDeckDeckstats() void AbstractTabDeckEditor::actAnalyzeDeckDeckstats()
{ {
auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(),

View file

@ -100,6 +100,7 @@ protected slots:
void actSaveDeckToClipboardRawNoSetInfo(); void actSaveDeckToClipboardRawNoSetInfo();
void actPrintDeck(); void actPrintDeck();
void actExportDeckDecklist(); void actExportDeckDecklist();
void actExportDeckDecklistXyz();
void actAnalyzeDeckDeckstats(); void actAnalyzeDeckDeckstats();
void actAnalyzeDeckTappedout(); void actAnalyzeDeckTappedout();
@ -119,6 +120,7 @@ protected slots:
private: private:
virtual void setDeck(DeckLoader *_deck); virtual void setDeck(DeckLoader *_deck);
void editDeckInClipboard(bool annotated); void editDeckInClipboard(bool annotated);
void exportToDecklistWebsite(DeckLoader::DecklistWebsite website);
protected: protected:
/** /**

View file

@ -14,7 +14,7 @@
#include <QLabel> #include <QLabel>
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList) DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList)
: QWidget(_parent), deckList(_deckList) : QWidget(_parent), deckList(nullptr)
{ {
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
@ -27,8 +27,8 @@ DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_par
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
if (deckList) { if (_deckList) {
connectDeckList(deckList); connectDeckList(_deckList);
} }
layout->addWidget(flowWidget); layout->addWidget(flowWidget);
@ -36,10 +36,20 @@ DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_par
void DeckPreviewDeckTagsDisplayWidget::connectDeckList(DeckList *_deckList) void DeckPreviewDeckTagsDisplayWidget::connectDeckList(DeckList *_deckList)
{ {
flowWidget->clearLayout(); if (deckList) {
disconnect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
}
deckList = _deckList; deckList = _deckList;
connect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags); connect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
refreshTags();
}
void DeckPreviewDeckTagsDisplayWidget::refreshTags()
{
flowWidget->clearLayout();
for (const QString &tag : deckList->getTags()) { for (const QString &tag : deckList->getTags()) {
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag)); flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
} }
@ -50,16 +60,6 @@ void DeckPreviewDeckTagsDisplayWidget::connectDeckList(DeckList *_deckList)
flowWidget->addWidget(tagAdditionWidget); flowWidget->addWidget(tagAdditionWidget);
} }
void DeckPreviewDeckTagsDisplayWidget::refreshTags()
{
flowWidget->clearLayout();
QStringList tags = deckList->getTags();
for (const QString &tag : tags) {
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
}
flowWidget->addWidget(new DeckPreviewTagAdditionWidget(this, tr("Edit tags ...")));
}
/** /**
* Gets the filepath of all files (no directories) in target directory and all subdirectories * Gets the filepath of all files (no directories) in target directory and all subdirectories
*/ */
@ -160,4 +160,4 @@ void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
} }
} }
} }
} }

View file

@ -59,7 +59,7 @@ void VisualDeckStorageFolderDisplayWidget::refreshUi()
} }
/** /**
* Gets all files in the directory that have a .txt or .cod extension * Gets all files in the directory that have an accepted decklist file extension
* *
* @param filePath The directory to search through * @param filePath The directory to search through
* @param recursive Whether to search through subdirectories * @param recursive Whether to search through subdirectories

View file

@ -226,6 +226,19 @@ bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, FileFormat f
return result; return result;
} }
static QString getDomainForWebsite(DeckLoader::DecklistWebsite website)
{
switch (website) {
case DeckLoader::DecklistOrg:
return "www.decklist.org";
case DeckLoader::DecklistXyz:
return "www.decklist.xyz";
default:
qCWarning(DeckLoaderLog) << "Invalid decklist website enum:" << website;
return "";
}
}
// This struct is here to support the forEachCard function call, defined in decklist. It // This struct is here to support the forEachCard function call, defined in decklist. It
// requires a function to be called for each card, and passes an inner node and a card for // requires a function to be called for each card, and passes an inner node and a card for
// each card in the decklist. // each card in the decklist.
@ -275,11 +288,14 @@ struct FormatDeckListForExport
} }
}; };
// Export deck to decklist function, called to format the deck in a way to be sent to a server /**
QString DeckLoader::exportDeckToDecklist() * Export deck to decklist function, called to format the deck in a way to be sent to a server
* @param website The website we're sending the deck to
*/
QString DeckLoader::exportDeckToDecklist(DecklistWebsite website)
{ {
// Add the base url // Add the base url
QString deckString = "https://www.decklist.org/?"; QString deckString = "https://" + getDomainForWebsite(website) + "/?";
// Create two strings to pass to function // Create two strings to pass to function
QString mainBoardCards, sideBoardCards; QString mainBoardCards, sideBoardCards;
// Set up the struct to call. // Set up the struct to call.

View file

@ -31,6 +31,12 @@ public:
*/ */
static const QStringList FILE_NAME_FILTERS; static const QStringList FILE_NAME_FILTERS;
enum DecklistWebsite
{
DecklistOrg,
DecklistXyz
};
private: private:
QString lastFileName; QString lastFileName;
FileFormat lastFileFormat; FileFormat lastFileFormat;
@ -71,7 +77,7 @@ public:
bool loadFromRemote(const QString &nativeString, int remoteDeckId); bool loadFromRemote(const QString &nativeString, int remoteDeckId);
bool saveToFile(const QString &fileName, FileFormat fmt); bool saveToFile(const QString &fileName, FileFormat fmt);
bool updateLastLoadedTimestamp(const QString &fileName, FileFormat fmt); bool updateLastLoadedTimestamp(const QString &fileName, FileFormat fmt);
QString exportDeckToDecklist(); QString exportDeckToDecklist(DecklistWebsite website);
void resolveSetNameAndNumberToProviderID(); void resolveSetNameAndNumberToProviderID();

View file

@ -3,7 +3,6 @@
#include "../game/cards/card_database.h" #include "../game/cards/card_database.h"
#include "../game/cards/card_database_manager.h" #include "../game/cards/card_database_manager.h"
#include "../game/filters/filter_string.h" #include "../game/filters/filter_string.h"
#include "trice_limits.h"
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
@ -14,15 +13,17 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, QString _expr, uint _numberOfHits, bool autoPlay) DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, QStringList exprs, uint _numberOfHits, bool autoPlay)
: QDialog(parent) : QDialog(parent)
{ {
exprLabel = new QLabel(tr("Card name (or search expressions):")); exprLabel = new QLabel(tr("Card name (or search expressions):"));
exprEdit = new QLineEdit(this); exprComboBox = new QComboBox(this);
exprEdit->setFocus(); exprComboBox->setFocus();
exprEdit->setText(_expr); exprComboBox->setEditable(true);
exprLabel->setBuddy(exprEdit); exprComboBox->setInsertPolicy(QComboBox::InsertAtTop);
exprComboBox->insertItems(0, exprs);
exprLabel->setBuddy(exprComboBox);
numberOfHitsLabel = new QLabel(tr("Number of hits:")); numberOfHitsLabel = new QLabel(tr("Number of hits:"));
numberOfHitsEdit = new QSpinBox(this); numberOfHitsEdit = new QSpinBox(this);
@ -43,7 +44,7 @@ DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, QString _expr, uint
auto *mainLayout = new QVBoxLayout; auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(exprLabel); mainLayout->addWidget(exprLabel);
mainLayout->addWidget(exprEdit); mainLayout->addWidget(exprComboBox);
mainLayout->addItem(grid); mainLayout->addItem(grid);
mainLayout->addWidget(autoPlayCheckBox); mainLayout->addWidget(autoPlayCheckBox);
mainLayout->addWidget(buttonBox); mainLayout->addWidget(buttonBox);
@ -92,7 +93,7 @@ bool DlgMoveTopCardsUntil::validateMatchExists(const FilterString &filterString)
void DlgMoveTopCardsUntil::validateAndAccept() void DlgMoveTopCardsUntil::validateAndAccept()
{ {
auto movingCardsUntilFilter = FilterString(exprEdit->text()); auto movingCardsUntilFilter = FilterString(exprComboBox->currentText());
if (!movingCardsUntilFilter.valid()) { if (!movingCardsUntilFilter.valid()) {
QMessageBox::warning(this, tr("Invalid filter"), movingCardsUntilFilter.error(), QMessageBox::Ok); QMessageBox::warning(this, tr("Invalid filter"), movingCardsUntilFilter.error(), QMessageBox::Ok);
return; return;
@ -102,12 +103,29 @@ void DlgMoveTopCardsUntil::validateAndAccept()
return; return;
} }
// move currently selected text to top of history list
if (exprComboBox->currentIndex() != 0) {
QString currentExpr = exprComboBox->currentText();
exprComboBox->removeItem(exprComboBox->currentIndex());
exprComboBox->insertItem(0, currentExpr);
exprComboBox->setCurrentIndex(0);
}
accept(); accept();
} }
QString DlgMoveTopCardsUntil::getExpr() const QString DlgMoveTopCardsUntil::getExpr() const
{ {
return exprEdit->text(); return exprComboBox->currentText();
}
QStringList DlgMoveTopCardsUntil::getExprs() const
{
QStringList exprs;
for (int i = 0; i < exprComboBox->count(); ++i) {
exprs.append(exprComboBox->itemText(i));
}
return exprs;
} }
uint DlgMoveTopCardsUntil::getNumberOfHits() const uint DlgMoveTopCardsUntil::getNumberOfHits() const

View file

@ -2,10 +2,10 @@
#define DLG_MOVE_TOP_CARDS_UNTIL_H #define DLG_MOVE_TOP_CARDS_UNTIL_H
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox>
#include <QDialog> #include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
#include <QLineEdit>
#include <QSpinBox> #include <QSpinBox>
class FilterString; class FilterString;
@ -15,7 +15,7 @@ class DlgMoveTopCardsUntil : public QDialog
Q_OBJECT Q_OBJECT
QLabel *exprLabel, *numberOfHitsLabel; QLabel *exprLabel, *numberOfHitsLabel;
QLineEdit *exprEdit; QComboBox *exprComboBox;
QSpinBox *numberOfHitsEdit; QSpinBox *numberOfHitsEdit;
QDialogButtonBox *buttonBox; QDialogButtonBox *buttonBox;
QCheckBox *autoPlayCheckBox; QCheckBox *autoPlayCheckBox;
@ -25,10 +25,11 @@ class DlgMoveTopCardsUntil : public QDialog
public: public:
explicit DlgMoveTopCardsUntil(QWidget *parent = nullptr, explicit DlgMoveTopCardsUntil(QWidget *parent = nullptr,
QString expr = QString(), QStringList exprs = QStringList(),
uint numberOfHits = 1, uint numberOfHits = 1,
bool autoPlay = false); bool autoPlay = false);
QString getExpr() const; QString getExpr() const;
QStringList getExprs() const;
uint getNumberOfHits() const; uint getNumberOfHits() const;
bool isAutoPlay() const; bool isAutoPlay() const;
}; };

View file

@ -10,7 +10,6 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QPainter> #include <QPainter>
#include <QStyleOption>
#include <algorithm> #include <algorithm>
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, AbstractCardItem::AbstractCardItem(QGraphicsItem *parent,
@ -19,7 +18,7 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent,
Player *_owner, Player *_owner,
int _id) int _id)
: ArrowTarget(_owner, parent), id(_id), name(_name), providerId(_providerId), tapped(false), facedown(false), : ArrowTarget(_owner, parent), id(_id), name(_name), providerId(_providerId), tapped(false), facedown(false),
tapAngle(0), bgColor(Qt::transparent), realZValue(0) tapAngle(0), bgColor(Qt::transparent), isHovered(false), realZValue(0)
{ {
setCursor(Qt::OpenHandCursor); setCursor(Qt::OpenHandCursor);
setFlag(ItemIsSelectable); setFlag(ItemIsSelectable);
@ -28,8 +27,6 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent,
connect(&SettingsCache::instance(), &SettingsCache::displayCardNamesChanged, this, [this] { update(); }); connect(&SettingsCache::instance(), &SettingsCache::displayCardNamesChanged, this, [this] { update(); });
refreshCardInfo(); refreshCardInfo();
setAcceptHoverEvents(true);
connect(&SettingsCache::instance(), &SettingsCache::roundCardCornersChanged, this, [this](bool _roundCardCorners) { connect(&SettingsCache::instance(), &SettingsCache::roundCardCornersChanged, this, [this](bool _roundCardCorners) {
Q_UNUSED(_roundCardCorners); Q_UNUSED(_roundCardCorners);
@ -168,7 +165,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
painter->restore(); painter->restore();
} }
void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * /*widget*/) void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
{ {
painter->save(); painter->save();
@ -177,7 +174,6 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
painter->setRenderHint(QPainter::Antialiasing, false); painter->setRenderHint(QPainter::Antialiasing, false);
bool isHovered = option->state.testFlag(QStyle::State_MouseOver);
if (isSelected() || isHovered) { if (isSelected() || isHovered) {
QPen pen; QPen pen;
if (isHovered) if (isHovered)
@ -220,24 +216,17 @@ void AbstractCardItem::setProviderId(const QString &_providerId)
refreshCardInfo(); refreshCardInfo();
} }
void AbstractCardItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) void AbstractCardItem::setHovered(bool _hovered)
{ {
Q_UNUSED(event); if (isHovered == _hovered)
return;
emit hovered(this); if (_hovered)
setZValue(2000000004); processHoverEvent();
setScale(SettingsCache::instance().getScaleCards() ? 1.1 : 1); isHovered = _hovered;
setTransformOriginPoint(CARD_WIDTH / 2, CARD_HEIGHT / 2); setZValue(_hovered ? 2000000004 : realZValue);
update(); setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1);
} setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0);
void AbstractCardItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
setZValue(realZValue);
setScale(1);
setTransformOriginPoint(0, 0);
update(); update();
} }
@ -333,6 +322,11 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
event->accept(); event->accept();
} }
void AbstractCardItem::processHoverEvent()
{
emit hovered(this);
}
QVariant AbstractCardItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) QVariant AbstractCardItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{ {
if (change == ItemSelectedHasChanged) { if (change == ItemSelectedHasChanged) {

View file

@ -24,6 +24,7 @@ protected:
QColor bgColor; QColor bgColor;
private: private:
bool isHovered;
qreal realZValue; qreal realZValue;
private slots: private slots:
void pixmapUpdated(); void pixmapUpdated();
@ -85,6 +86,7 @@ public:
return realZValue; return realZValue;
} }
void setRealZValue(qreal _zValue); void setRealZValue(qreal _zValue);
void setHovered(bool _hovered);
QString getColor() const QString getColor() const
{ {
return color; return color;
@ -100,6 +102,7 @@ public:
return facedown; return facedown;
} }
void setFaceDown(bool _facedown); void setFaceDown(bool _facedown);
void processHoverEvent();
void deleteCardInfoPopup() void deleteCardInfoPopup()
{ {
emit deleteCardInfoPopup(name); emit deleteCardInfoPopup(name);
@ -111,9 +114,6 @@ protected:
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;
void cacheBgColor(); void cacheBgColor();
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
}; };
#endif #endif

View file

@ -469,6 +469,7 @@ bool CardItem::animationEvent()
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) .translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF)
.rotate(tapAngle) .rotate(tapAngle)
.translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); .translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
setHovered(false);
update(); update();
return animationIncomplete; return animationIncomplete;

View file

@ -164,6 +164,12 @@ void DeckView::mouseDoubleClickEvent(QMouseEvent *event)
} }
} }
void DeckViewCard::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
event->accept();
processHoverEvent();
}
DeckViewCardContainer::DeckViewCardContainer(const QString &_name) : QGraphicsItem(), name(_name), width(0), height(0) DeckViewCardContainer::DeckViewCardContainer(const QString &_name) : QGraphicsItem(), name(_name), width(0), height(0)
{ {
setCacheMode(DeviceCoordinateCache); setCacheMode(DeviceCoordinateCache);

View file

@ -37,6 +37,7 @@ public:
protected: protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
}; };
class DeckViewCardDragItem : public AbstractCardDragItem class DeckViewCardDragItem : public AbstractCardDragItem

View file

@ -253,6 +253,51 @@ void GameScene::processViewSizeChange(const QSize &newSize)
} }
} }
void GameScene::updateHover(const QPointF &scenePos)
{
QList<QGraphicsItem *> itemList =
items(scenePos, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder, getViewTransform());
// Search for the topmost zone and ignore all cards not belonging to that zone.
CardZone *zone = 0;
for (int i = 0; i < itemList.size(); ++i)
if ((zone = qgraphicsitem_cast<CardZone *>(itemList[i])))
break;
CardItem *maxZCard = 0;
if (zone) {
qreal maxZ = -1;
for (int i = 0; i < itemList.size(); ++i) {
CardItem *card = qgraphicsitem_cast<CardItem *>(itemList[i]);
if (!card)
continue;
if (card->getAttachedTo()) {
if (card->getAttachedTo()->getZone() != zone)
continue;
} else if (card->getZone() != zone)
continue;
if (card->getRealZValue() > maxZ) {
maxZ = card->getRealZValue();
maxZCard = card;
}
}
}
if (hoveredCard && (maxZCard != hoveredCard))
hoveredCard->setHovered(false);
if (maxZCard && (maxZCard != hoveredCard))
maxZCard->setHovered(true);
hoveredCard = maxZCard;
}
bool GameScene::event(QEvent *event)
{
if (event->type() == QEvent::GraphicsSceneMouseMove)
updateHover(static_cast<QGraphicsSceneMouseEvent *>(event)->scenePos());
return QGraphicsScene::event(event);
}
void GameScene::timerEvent(QTimerEvent * /*event*/) void GameScene::timerEvent(QTimerEvent * /*event*/)
{ {
QMutableSetIterator<CardItem *> i(cardsToAnimate); QMutableSetIterator<CardItem *> i(cardsToAnimate);

View file

@ -30,9 +30,11 @@ private:
QList<QList<Player *>> playersByColumn; QList<QList<Player *>> playersByColumn;
QList<ZoneViewWidget *> zoneViews; QList<ZoneViewWidget *> zoneViews;
QSize viewSize; QSize viewSize;
QPointer<CardItem> hoveredCard;
QBasicTimer *animationTimer; QBasicTimer *animationTimer;
QSet<CardItem *> cardsToAnimate; QSet<CardItem *> cardsToAnimate;
int playerRotation; int playerRotation;
void updateHover(const QPointF &scenePos);
public: public:
explicit GameScene(PhasesToolbar *_phasesToolbar, QObject *parent = nullptr); explicit GameScene(PhasesToolbar *_phasesToolbar, QObject *parent = nullptr);
@ -63,6 +65,7 @@ public slots:
void rearrange(); void rearrange();
protected: protected:
bool event(QEvent *event) override;
void timerEvent(QTimerEvent *event) override; void timerEvent(QTimerEvent *event) override;
signals: signals:
void sigStartRubberBand(const QPointF &selectionOrigin); void sigStartRubberBand(const QPointF &selectionOrigin);

View file

@ -1443,19 +1443,20 @@ void Player::actMoveTopCardsUntil()
{ {
stopMoveTopCardsUntil(); stopMoveTopCardsUntil();
DlgMoveTopCardsUntil dlg(game, movingCardsUntilExpr, movingCardsUntilNumberOfHits, movingCardsUntilAutoPlay); DlgMoveTopCardsUntil dlg(game, movingCardsUntilExprs, movingCardsUntilNumberOfHits, movingCardsUntilAutoPlay);
if (!dlg.exec()) { if (!dlg.exec()) {
return; return;
} }
movingCardsUntilExpr = dlg.getExpr(); auto expr = dlg.getExpr();
movingCardsUntilExprs = dlg.getExprs();
movingCardsUntilNumberOfHits = dlg.getNumberOfHits(); movingCardsUntilNumberOfHits = dlg.getNumberOfHits();
movingCardsUntilAutoPlay = dlg.isAutoPlay(); movingCardsUntilAutoPlay = dlg.isAutoPlay();
if (zones.value("deck")->getCards().empty()) { if (zones.value("deck")->getCards().empty()) {
stopMoveTopCardsUntil(); stopMoveTopCardsUntil();
} else { } else {
movingCardsUntilFilter = FilterString(movingCardsUntilExpr); movingCardsUntilFilter = FilterString(expr);
movingCardsUntilCounter = movingCardsUntilNumberOfHits; movingCardsUntilCounter = movingCardsUntilNumberOfHits;
movingCardsUntil = true; movingCardsUntil = true;
actMoveTopCardToPlay(); actMoveTopCardToPlay();
@ -2386,6 +2387,7 @@ void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext &
card->setFaceDown(event.face_down()); card->setFaceDown(event.face_down());
if (startZone != targetZone) { if (startZone != targetZone) {
card->setBeingPointedAt(false); card->setBeingPointedAt(false);
card->setHovered(false);
const QList<CardItem *> &attachedCards = card->getAttachedCards(); const QList<CardItem *> &attachedCards = card->getAttachedCards();
for (auto attachedCard : attachedCards) { for (auto attachedCard : attachedCards) {

View file

@ -279,7 +279,7 @@ private:
bool movingCardsUntil; bool movingCardsUntil;
QTimer *moveTopCardTimer; QTimer *moveTopCardTimer;
QString movingCardsUntilExpr = {}; QStringList movingCardsUntilExprs = {};
int movingCardsUntilNumberOfHits = 1; int movingCardsUntilNumberOfHits = 1;
bool movingCardsUntilAutoPlay = false; bool movingCardsUntilAutoPlay = false;
FilterString movingCardsUntilFilter; FilterString movingCardsUntilFilter;

View file

@ -139,7 +139,6 @@ void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event) void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{ {
if (!cards.isEmpty()) if (!cards.isEmpty())
emit cards[0]->hovered(cards[0]); cards[0]->processHoverEvent();
QGraphicsItem::hoverEnterEvent(event); QGraphicsItem::hoverEnterEvent(event);
} }

View file

@ -66,26 +66,25 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZone
cmd.set_target_zone(getName().toStdString()); cmd.set_target_zone(getName().toStdString());
int index = 0; int index = 0;
if (cards.isEmpty()) {
index = 0; if (!cards.isEmpty()) {
} else {
const auto cardCount = static_cast<int>(cards.size()); const auto cardCount = static_cast<int>(cards.size());
const auto &card = cards.at(0); const auto &card = cards.at(0);
if (card == nullptr) {
qCWarning(StackZoneLog) << "Attempted to move card from" << startZone->getName() << ", but was null";
return;
}
index = qRound(divideCardSpaceInZone(dropPoint.y(), cardCount, boundingRect().height(), index = qRound(divideCardSpaceInZone(dropPoint.y(), cardCount, boundingRect().height(),
card->boundingRect().height(), true)); card->boundingRect().height(), true));
}
if (startZone == this) { // divideCardSpaceInZone is not guaranteed to return a valid index
const auto &dragItem = dragItems.at(0); // currently, so clamp it to avoid crashes.
const auto &card = cards.at(index); index = qBound(0, index, cardCount - 1);
if (card != nullptr && dragItem != nullptr && card->getId() == dragItem->getId()) {
return; if (startZone == this) {
const auto &dragItem = dragItems.at(0);
const auto &card = cards.at(index);
if (card->getId() == dragItem->getId()) {
return;
}
} }
} }

View file

@ -3,8 +3,6 @@
#include "select_zone.h" #include "select_zone.h"
inline Q_LOGGING_CATEGORY(StackZoneLog, "stack_zone");
class StackZone : public SelectZone class StackZone : public SelectZone
{ {
Q_OBJECT Q_OBJECT

View file

@ -173,9 +173,13 @@ private:
{"MainWindow/aWatchReplay", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Watch Replay..."), {"MainWindow/aWatchReplay", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Watch Replay..."),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Main_Window)}, ShortcutGroup::Main_Window)},
{"TabDeckEditor/aAnalyzeDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Analyze Deck"), {"TabDeckEditor/aAnalyzeDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Analyze Deck (deckstats.net)"),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aAnalyzeDeckTappedout",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Analyze Deck (tappedout.net)"),
parseSequenceString(""),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aClearFilterAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Clear All Filters"), {"TabDeckEditor/aClearFilterAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Clear All Filters"),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},
@ -193,9 +197,14 @@ private:
{"TabDeckEditor/aEditTokens", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Edit Custom Tokens..."), {"TabDeckEditor/aEditTokens", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Edit Custom Tokens..."),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aExportDeckDecklist", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Export Deck"), {"TabDeckEditor/aExportDeckDecklist",
parseSequenceString(""), ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Export Deck (decklist.org)"),
ShortcutGroup::Deck_Editor)}, parseSequenceString(""),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aExportDeckDecklistXyz",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Export Deck (decklist.xyz)"),
parseSequenceString(""),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aIncrement", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card"), {"TabDeckEditor/aIncrement", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card"),
parseSequenceString("+"), parseSequenceString("+"),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},

View file

@ -1,6 +1,29 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="en_US"> <TS version="2.1" language="en_US">
<context>
<name>BetaReleaseChannel</name>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="210"/>
<source>Beta</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="236"/>
<source>No reply received from the release update server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="245"/>
<source>Invalid reply received from the release update server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="278"/>
<source>No reply received from the file update server.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>IntroPage</name> <name>IntroPage</name>
<message> <message>
@ -63,7 +86,8 @@
</message> </message>
<message> <message>
<location filename="src/oraclewizard.cpp" line="287"/> <location filename="src/oraclewizard.cpp" line="287"/>
<source>Sets JSON file (%1)</source> <source>Sets file (%1)</source>
<oldsource>Sets JSON file (%1)</oldsource>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
@ -247,7 +271,7 @@
<context> <context>
<name>OracleImporter</name> <name>OracleImporter</name>
<message> <message>
<location filename="src/oracleimporter.cpp" line="465"/> <location filename="src/oracleimporter.cpp" line="471"/>
<source>Dummy set containing tokens</source> <source>Dummy set containing tokens</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -283,6 +307,15 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>PictureLoader</name>
<message>
<location filename="../cockatrice/src/client/ui/picture_loader/picture_to_load.cpp" line="219"/>
<source>en</source>
<comment>code for scryfall&apos;s language property, not available for all languages</comment>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>SaveSetsPage</name> <name>SaveSetsPage</name>
<message> <message>
@ -357,6 +390,21 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>ShortcutsSettings</name>
<message>
<location filename="../cockatrice/src/settings/shortcuts_settings.cpp" line="54"/>
<source>Your configuration file contained invalid shortcuts.
Please check your shortcut settings!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/settings/shortcuts_settings.cpp" line="56"/>
<source>The following shortcuts have been set to default:
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>SimpleDownloadFilePage</name> <name>SimpleDownloadFilePage</name>
<message> <message>
@ -392,6 +440,34 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>StableReleaseChannel</name>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="104"/>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="120"/>
<source>No reply received from the release update server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="128"/>
<source>Invalid reply received from the release update server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="176"/>
<source>No reply received from the tag update server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../cockatrice/src/client/network/release_channel.cpp" line="183"/>
<source>Invalid reply received from the tag update server.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>UnZip</name> <name>UnZip</name>
<message> <message>
@ -537,6 +613,7 @@
<name>i18n</name> <name>i18n</name>
<message> <message>
<location filename="src/oraclewizard.cpp" line="58"/> <location filename="src/oraclewizard.cpp" line="58"/>
<location filename="../cockatrice/src/settings/cache_settings.cpp" line="167"/>
<source>English</source> <source>English</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>