Merge remote-tracking branch 'upstream/master' into first-run-tutorial

This commit is contained in:
Lukas Brübach 2026-10-11 15:14:26 +02:00
commit f5826d9d6f
1395 changed files with 67177 additions and 10446 deletions

View file

@ -1,15 +1,24 @@
#include "color_identity_widget.h"
#include "../../../../client/settings/cache_settings.h"
#include "libcockatrice/card/card_info.h"
#include "mana_symbol_widget.h"
#include <QChar>
#include <QHBoxLayout>
#include <QLabel>
#include <QLayoutItem>
#include <QList>
#include <QObject>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QRegularExpressionMatchIterator>
#include <QResizeEvent>
#include <QSharedPointer>
#include <QSize>
#include <QtGlobal>
#include <libcockatrice/settings/visual_deck_storage_settings.h>
#include <libcockatrice/utility/qt_utils.h>
#include <qnamespace.h>
ColorIdentityWidget::ColorIdentityWidget(QWidget *parent, const QString &_colorIdentity)
: QWidget(parent), colorIdentity(_colorIdentity)
@ -41,6 +50,11 @@ void ColorIdentityWidget::populateManaSymbolWidgets()
// clear old layout
QtUtils::clearLayoutRec(layout);
// The freshly created symbols haven't been sized yet, so force the next resize pass
// to apply the symbol size again.
lastIconSize = -1;
lastWidth = -1;
// populate mana symbols
if (SettingsCache::instance().visualDeckStorage().getVisualDeckStorageDrawUnusedColorIdentities()) {
for (const QString symbol : fullColorIdentity) {
@ -73,20 +87,37 @@ void ColorIdentityWidget::toggleUnusedVisibility()
void ColorIdentityWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
QList<ManaSymbolWidget *> manaSymbols = findChildren<ManaSymbolWidget *>();
if (!manaSymbols.isEmpty()) {
int totalWidth = event->size().width();
int totalHeight = totalWidth / 6; // Set height to 1/4 of the width
setFixedHeight(totalHeight);
const int totalWidth = event->size().width();
if (totalWidth == lastWidth && lastIconSize != -1) {
return;
}
lastWidth = totalWidth;
int spacing = layout->spacing();
int count = manaSymbols.size();
int availableWidth = totalWidth - (spacing * (count - 1));
int iconSize = qMin(availableWidth / count, totalHeight); // Ensure icons fit within the new height
const int totalHeight = qMax(0, totalWidth / 6); // Set height to 1/4 of the width
setFixedHeight(totalHeight);
for (ManaSymbolWidget *manaSymbol : manaSymbols) {
manaSymbol->setFixedSize(iconSize, iconSize);
const int count = layout->count();
if (count == 0) {
return;
}
const int spacing = layout->spacing();
const int availableWidth = totalWidth - (spacing * (count - 1));
const int iconSize = qMin(availableWidth / count, totalHeight); // Ensure icons fit within the new height
if (iconSize <= 0) {
lastIconSize = iconSize;
return;
}
if (iconSize == lastIconSize) {
return;
}
lastIconSize = iconSize;
for (int i = 0; i < count; ++i) {
if (auto *w = qobject_cast<ManaSymbolWidget *>(layout->itemAt(i)->widget())) {
w->setFixedSize(iconSize, iconSize);
}
}
}

View file

@ -7,9 +7,13 @@
#ifndef COLOR_IDENTITY_WIDGET_H
#define COLOR_IDENTITY_WIDGET_H
#include <QHBoxLayout>
#include <QString>
#include <QStringList>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
#include <qtmetamacros.h>
class QHBoxLayout;
class ColorIdentityWidget : public QWidget
{
@ -30,6 +34,8 @@ public slots:
private:
QString colorIdentity;
QHBoxLayout *layout;
int lastIconSize = -1; ///< The symbol size last applied, to skip redundant resize passes.
int lastWidth = -1; ///< The width last processed, to skip redundant resize passes.
};
#endif // COLOR_IDENTITY_WIDGET_H

View file

@ -0,0 +1,42 @@
#include "deck_color_identity.h"
#include "libcockatrice/card/card_info.h"
#include <QChar>
#include <QList>
#include <QSet>
#include <QStringList>
#include <libcockatrice/card/database/card_database_querier.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
QString getDeckColorIdentity(const DeckList &deck, const CardDatabaseQuerier *db)
{
const QStringList cardList = deck.getCardList({DECK_ZONE_MAIN, DECK_ZONE_SIDE});
if (cardList.isEmpty()) {
return {};
}
QSet<QChar> colorSet; // A set to collect unique color symbols (e.g., W, U, B, R, G)
for (const QString &cardName : cardList) {
CardInfoPtr currentCard = db->getCardInfo(cardName);
if (currentCard) {
const QString colors = currentCard->getColors(); // returns something like "WUB"
for (const QChar &color : colors) {
colorSet.insert(color);
}
}
}
// Ensure the color identity is in WUBRG order
QString colorIdentity;
const QString wubrgOrder = "WUBRG";
for (const QChar &color : wubrgOrder) {
if (colorSet.contains(color)) {
colorIdentity.append(color);
}
}
return colorIdentity;
}

View file

@ -0,0 +1,20 @@
#ifndef COCKATRICE_DECK_COLOR_IDENTITY_H
#define COCKATRICE_DECK_COLOR_IDENTITY_H
#include <QString>
class CardDatabaseQuerier;
class DeckList;
/**
* @brief Computes the color identity of a deck (e.g. "WUBRG") from the color
* symbols of all cards in the main deck and sideboard, ordered WUBRG.
*
* Shared as a free function so the deck storage previews and the deck share
* dialog compute identities identically.
*
* @param db Card database used to look up card color symbols.
*/
QString getDeckColorIdentity(const DeckList &deck, const CardDatabaseQuerier *db);
#endif // COCKATRICE_DECK_COLOR_IDENTITY_H

View file

@ -1,12 +1,16 @@
#include "mana_cost_widget.h"
#include "libcockatrice/card/card_info.h"
#include "mana_symbol_widget.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QList>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QRegularExpressionMatchIterator>
#include <QResizeEvent>
#include <QSize>
#include <qregularexpression.h>
#include <QtGlobal>
ManaCostWidget::ManaCostWidget(QWidget *parent, CardInfoPtr _card) : QWidget(parent), card(_card)
{

View file

@ -7,9 +7,13 @@
#ifndef MANA_COST_WIDGET_H
#define MANA_COST_WIDGET_H
#include <QHBoxLayout>
#include <QString>
#include <QStringList>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
#include <qtmetamacros.h>
class QHBoxLayout;
class ManaCostWidget : public QWidget
{
@ -17,7 +21,7 @@ class ManaCostWidget : public QWidget
public:
explicit ManaCostWidget(QWidget *parent, CardInfoPtr card);
QStringList parseManaCost(const QString &manaString);
static QStringList parseManaCost(const QString &manaString);
public slots:
void resizeEvent(QResizeEvent *event) override;

View file

@ -1,15 +1,23 @@
#include "mana_symbol_widget.h"
#include "../../../../client/settings/cache_settings.h"
#include "../../../pixel_map_generator.h"
#include <QGraphicsOpacityEffect>
#include <QPixmap>
#include <QResizeEvent>
#include <QSharedPointer>
#include <QSize>
#include <QtGlobal>
#include <libcockatrice/settings/visual_deck_storage_settings.h>
#include <utility>
class QWidget;
ManaSymbolWidget::ManaSymbolWidget(QWidget *parent, QString _symbol, bool _isActive, bool _mayBeToggled)
: QLabel(parent), symbol(_symbol), isActive(_isActive), mayBeToggled(_mayBeToggled)
: QLabel(parent), symbol(std::move(_symbol)), isActive(_isActive), mayBeToggled(_mayBeToggled)
{
loadManaIcon();
setPixmap(manaIcon.scaled(50, 50, Qt::KeepAspectRatio, Qt::SmoothTransformation));
setPixmap(ManaSymbolPixmapGenerator::generatePixmap(symbol, QSize(50, 50)));
setMaximumWidth(50);
// Initialize opacity effect
@ -64,16 +72,13 @@ void ManaSymbolWidget::mousePressEvent(QMouseEvent *event)
void ManaSymbolWidget::resizeEvent(QResizeEvent *event)
{
QLabel::resizeEvent(event);
setPixmap(manaIcon.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
const QSize newSize = event->size();
void ManaSymbolWidget::loadManaIcon()
{
QString filename = "theme:icons/mana/";
if (symbol == "W" || symbol == "U" || symbol == "B" || symbol == "R" || symbol == "G") {
filename += symbol;
// Skip the rescale when the size didn't actually change: layout passes resize these
// widgets repeatedly with identical sizes.
if (newSize.isEmpty() || pixmap().size() == newSize) {
return;
}
manaIcon = QPixmap(filename);
setPixmap(ManaSymbolPixmapGenerator::generatePixmap(symbol, newSize));
}

View file

@ -7,8 +7,13 @@
#ifndef MANA_SYMBOL_WIDGET_H
#define MANA_SYMBOL_WIDGET_H
#include <QGraphicsOpacityEffect>
#include <QChar>
#include <QLabel>
#include <QString>
#include <qtmetamacros.h>
class QGraphicsOpacityEffect;
class QWidget;
class ManaSymbolWidget : public QLabel
{
@ -33,8 +38,6 @@ public:
return symbol[0];
}
void loadManaIcon();
public slots:
void resizeEvent(QResizeEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
@ -44,7 +47,6 @@ signals:
private:
QString symbol;
QPixmap manaIcon;
bool isActive;
bool mayBeToggled;
QGraphicsOpacityEffect *opacityEffect;

View file

@ -0,0 +1,70 @@
#include "art_crop_attribution.h"
#include "libcockatrice/card/printing/printing_info.h"
#include <QColor>
#include <QFont>
#include <QFontMetrics>
#include <QObject>
#include <QPainter>
#include <QPointF>
#include <QRectF>
#include <QSizeF>
#include <QString>
#include <QtGlobal>
#include <libcockatrice/card/printing/exact_card.h>
QString buildArtAttribution(const ExactCard &card)
{
const QString artist = card.getPrinting().getArtist();
if (artist.isEmpty()) {
return QString();
}
return QObject::tr("Art: %1").arg(artist);
}
QRectF paintArtAttribution(QPainter &painter,
const QRectF &rect,
const QString &attribution,
Qt::Alignment anchor,
qreal scale)
{
if (attribution.isEmpty()) {
return QRectF();
}
painter.save();
QFont font = painter.font();
font.setPointSizeF(qMax(6.0, font.pointSizeF() * scale));
painter.setFont(font);
const QFontMetrics fm(font);
const qreal maxTextWidth = rect.width() * 0.45;
const QString elided = fm.elidedText(attribution, Qt::ElideRight, qMax(qreal(80.0) * scale, maxTextWidth));
const qreal pad = 6.0 * scale;
QRectF captionRect(QPointF(0, 0), QSizeF(fm.horizontalAdvance(elided) + pad * 2.0, fm.height() + pad * 2.0));
const qreal margin = 4.0 * scale;
if (anchor.testFlag(Qt::AlignLeft)) {
captionRect.moveLeft(rect.left() + margin);
} else {
captionRect.moveRight(rect.right() - margin);
}
if (anchor.testFlag(Qt::AlignTop)) {
captionRect.moveTop(rect.top() + margin);
} else {
captionRect.moveBottom(rect.bottom() - margin);
}
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 120));
painter.drawRoundedRect(captionRect, 4, 4);
painter.setPen(QColor(255, 255, 255, 220));
painter.drawText(captionRect, Qt::AlignCenter, elided);
painter.restore();
return captionRect;
}

View file

@ -0,0 +1,42 @@
#ifndef COCKATRICE_ART_CROP_ATTRIBUTION_H
#define COCKATRICE_ART_CROP_ATTRIBUTION_H
#include <QtGlobal>
#include <qnamespace.h>
class ExactCard;
class QPainter;
class QRectF;
class QString;
/**
* @brief Builds an attribution caption for a cropped card art display.
*
* When a card image's art region is shown cropped (an "art crop"), the artist
* should be credited in the same interface. Returns an empty string when the
* database has no artist data for the card.
*
* @param card The card whose art is being displayed.
* @return Caption such as "Art: John Avon", or empty.
*/
QString buildArtAttribution(const ExactCard &card);
/**
* @brief Paints an attribution caption in a corner of a rect.
*
* Draws a subtle semi-transparent pill containing the caption, elided to fit.
*
* @param painter Painter to draw with.
* @param rect The area (e.g. the cropped art region) the caption belongs to.
* @param attribution Caption text (see buildArtAttribution()).
* @param anchor Corner of @p rect to pin the pill to (default bottom-right).
* @param scale Size multiplier for the pill (e.g. 0.8 for a smaller pill).
* @return The rect the pill was drawn in, or an empty rect if @p attribution is empty.
*/
QRectF paintArtAttribution(QPainter &painter,
const QRectF &rect,
const QString &attribution,
Qt::Alignment anchor = Qt::AlignRight | Qt::AlignBottom,
qreal scale = 1.0);
#endif // COCKATRICE_ART_CROP_ATTRIBUTION_H

View file

@ -0,0 +1,21 @@
#include "card_art_utils.h"
#include "libcockatrice/card/card_info.h"
#include <QTransform>
#include <libcockatrice/card/printing/exact_card.h>
#include <qnamespace.h>
namespace CardArtUtils
{
QPixmap rotateSidewaysLayoutArt(const QPixmap &art, const ExactCard &card)
{
if (!card.getInfo().getUiAttributes().landscapeOrientation) {
return art;
}
QTransform transform;
transform.rotate(90);
return art.transformed(transform, Qt::SmoothTransformation);
}
} // namespace CardArtUtils

View file

@ -0,0 +1,25 @@
#ifndef CARD_ART_UTILS_H
#define CARD_ART_UTILS_H
#include <QPixmap>
class ExactCard;
namespace CardArtUtils
{
/**
* @brief Rotates a card's art upright when its layout shows sideways.
*
* Sideways-layout cards (planes, sieges/battles, split cards) store their
* landscape artwork rotated 90° inside a portrait frame. Art-crop displays,
* playmat art, and the card-info picture must show such art upright before
* sampling or painting. Portrait cards are returned unchanged.
*
* @param art The card pixmap to orient.
* @param card The card describing the art orientation.
* @return @p art rotated 90° clockwise for sideways-layout cards, else @p art.
*/
QPixmap rotateSidewaysLayoutArt(const QPixmap &art, const ExactCard &card);
} // namespace CardArtUtils
#endif // CARD_ART_UTILS_H

View file

@ -1,11 +1,33 @@
#include "card_group_display_widget.h"
#include "../../../../client/settings/cache_settings.h"
#include "../../general/display/banner_widget.h"
#include "../card_info_picture_widget.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include "../card_size_widget.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include <QResizeEvent>
#include <QAbstractItemModel>
#include <QAbstractProxyModel>
#include <QDebug>
#include <QItemSelection>
#include <QItemSelectionModel>
#include <QItemSelectionRange>
#include <QModelIndex>
#include <QObject>
#include <QSharedPointer>
#include <QSize>
#include <QSlider>
#include <QStringLiteral>
#include <QVariant>
#include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h>
#include <utility>
CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
@ -30,6 +52,7 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
layout->addWidget(banner);
CardGroupDisplayWidget::updateCardDisplays();
updateCardCount();
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::onCardAddition);
if (selectionModel) {
@ -38,6 +61,13 @@ CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
}
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::onCardRemoval);
connect(deckListModel, &QAbstractItemModel::dataChanged, this, &CardGroupDisplayWidget::onDataChanged);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &CardGroupDisplayWidget::updateCardCount);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &CardGroupDisplayWidget::updateCardCount);
connect(deckListModel, &QAbstractItemModel::dataChanged, this, &CardGroupDisplayWidget::updateCardCount);
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::visualDeckEditorShowCardCountsChanged,
this, &CardGroupDisplayWidget::updateCardCount);
cardSizeWidget->enableCtrlScrollResize(this);
}
// Just here so it can get overwritten in subclasses.
@ -174,16 +204,18 @@ void CardGroupDisplayWidget::updateCardDisplays()
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// 4. persist the source index
QPersistentModelIndex persistent(sourceIndex);
addCardWidgets(QPersistentModelIndex(sourceIndex));
}
}
// Get the card amount
int cardAmount =
sourceIndex.sibling(sourceIndex.row(), DeckListModelColumns::CARD_AMOUNT).data(Qt::EditRole).toInt();
void CardGroupDisplayWidget::addCardWidgets(const QPersistentModelIndex &persistent)
{
// Get the card amount
int cardAmount = persistent.sibling(persistent.row(), DeckListModelColumns::CARD_AMOUNT).data(Qt::EditRole).toInt();
// Create multiple widgets for the card count
for (int copy = 0; copy < cardAmount; ++copy) {
addToLayout(constructWidgetForIndex(persistent));
}
// Create multiple widgets for the card count
for (int copy = 0; copy < cardAmount; ++copy) {
addToLayout(constructWidgetForIndex(persistent));
}
}
@ -346,4 +378,22 @@ void CardGroupDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSort
clearAllDisplayWidgets();
updateCardDisplays();
}
}
void CardGroupDisplayWidget::updateCardCount()
{
if (!banner || !deckListModel || !trackedIndex.isValid()) {
return;
}
QString text = cardGroupCategory;
if (SettingsCache::instance().cardsDisplay().getVisualDeckEditorShowCardCounts()) {
int total = 0;
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
total +=
deckListModel->index(i, DeckListModelColumns::CARD_AMOUNT, trackedIndex).data(Qt::EditRole).toInt();
}
text += QStringLiteral(" (%1)").arg(total);
}
banner->setText(text);
}

View file

@ -7,15 +7,24 @@
#ifndef CARD_GROUP_DISPLAY_WIDGET_H
#define CARD_GROUP_DISPLAY_WIDGET_H
#include "../../general/display/banner_widget.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include "../card_size_widget.h"
#include <QItemSelectionModel>
#include <QLabel>
#include <QList>
#include <QMap>
#include <QMouseEvent>
#include <QPersistentModelIndex>
#include <QString>
#include <QStringList>
#include <QVBoxLayout>
#include <QVector>
#include <QWidget>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <qtmetamacros.h>
class BannerWidget;
class CardSizeWidget;
class DeckListModel;
class ExactCard;
class QItemSelection;
class QItemSelectionModel;
class QModelIndex;
class CardGroupDisplayWidget : public QWidget
{
@ -35,6 +44,7 @@ public:
void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void refreshSelectionForIndex(const QPersistentModelIndex &persistent);
void clearAllDisplayWidgets();
void addCardWidgets(const QPersistentModelIndex &persistent);
DeckListModel *deckListModel;
QItemSelectionModel *selectionModel;
@ -54,6 +64,7 @@ public slots:
virtual void onCardRemoval(const QModelIndex &parent, int first, int last);
void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
void updateCardCount();
void resizeEvent(QResizeEvent *event) override;
signals:

View file

@ -1,10 +1,20 @@
#include "flat_card_group_display_widget.h"
#include <QResizeEvent>
#include <libcockatrice/card/database/card_database_manager.h>
#include "../../general/display/banner_widget.h"
#include "../../general/layout_containers/flow_widget.h"
#include <QAbstractItemModel>
#include <QList>
#include <QMap>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <qnamespace.h>
#include <utility>
class CardSizeWidget;
class QItemSelectionModel;
FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
QItemSelectionModel *_selectionModel,

View file

@ -10,6 +10,16 @@
#include "../../general/layout_containers/flow_widget.h"
#include "card_group_display_widget.h"
#include <QPersistentModelIndex>
#include <QString>
#include <QStringList>
#include <qtmetamacros.h>
class CardSizeWidget;
class DeckListModel;
class QItemSelectionModel;
class QWidget;
class FlatCardGroupDisplayWidget : public CardGroupDisplayWidget
{
Q_OBJECT

View file

@ -1,8 +1,20 @@
#include "overlapped_card_group_display_widget.h"
#include "../../general/display/banner_widget.h"
#include "../../general/layout_containers/overlap_widget.h"
#include "../card_size_widget.h"
#include <QAbstractItemModel>
#include <QList>
#include <QMap>
#include <QResizeEvent>
#include <libcockatrice/card/database/card_database_manager.h>
#include <QSlider>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <qnamespace.h>
class QItemSelectionModel;
OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,

View file

@ -10,6 +10,16 @@
#include "../../general/layout_containers/overlap_widget.h"
#include "card_group_display_widget.h"
#include <QPersistentModelIndex>
#include <QString>
#include <QStringList>
#include <qtmetamacros.h>
class CardSizeWidget;
class DeckListModel;
class QItemSelectionModel;
class QWidget;
class OverlappedCardGroupDisplayWidget : public CardGroupDisplayWidget
{
Q_OBJECT

View file

@ -1,14 +1,25 @@
#include "card_info_display_widget.h"
#include "../../../game_graphics/board/card_item.h"
#include "../../../game_graphics/board/abstract_card_item.h"
#include "../../../game_graphics/card_dimensions.h"
#include "card_info_picture_widget.h"
#include "card_info_text_widget.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/utility/card_ref.h"
#include <QApplication>
#include <QGuiApplication>
#include <QObject>
#include <QRect>
#include <QScreen>
#include <QSize>
#include <QString>
#include <QVBoxLayout>
#include <libcockatrice/card/database/card_database_manager.h>
class QWidget;
CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *parent, Qt::WindowFlags flags)
: QFrame(parent, flags), aspectRatio(CardDimensions::HEIGHT_F / CardDimensions::WIDTH_F)
{

View file

@ -7,15 +7,17 @@
#ifndef CARDINFOWIDGET_H
#define CARDINFOWIDGET_H
#include <QComboBox>
#include <QFrame>
#include <QStringList>
#include <QtGlobal>
#include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h>
#include <qnamespace.h>
#include <qtmetamacros.h>
class CardInfoPictureWidget;
class CardInfoTextWidget;
class AbstractCardItem;
class QWidget;
struct CardRef;
class CardInfoDisplayWidget : public QFrame
{

View file

@ -1,16 +1,27 @@
#include "card_info_frame_widget.h"
#include "../../../client/settings/cache_settings.h"
#include "../../../game_graphics/board/card_item.h"
#include "card_info_display_widget.h"
#include "../../../game_graphics/board/abstract_card_item.h"
#include "card_info_picture_widget.h"
#include "card_info_text_widget.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include <QList>
#include <QObject>
#include <QPushButton>
#include <QSplitter>
#include <QVBoxLayout>
#include <QWidget>
#include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/utility/card_ref.h>
#include <qnamespace.h>
struct CardRef;
CardInfoFrameWidget::CardInfoFrameWidget(QWidget *parent)
: QTabWidget(parent), viewTransformationButton(nullptr), cardTextOnly(false)

View file

@ -7,16 +7,19 @@
#ifndef CARDFRAME_H
#define CARDFRAME_H
#include <QPushButton>
#include <QString>
#include <QTabWidget>
#include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h>
#include <qtmetamacros.h>
class AbstractCardItem;
class CardInfoPictureWidget;
class CardInfoTextWidget;
class QVBoxLayout;
class QSplitter;
class QPushButton;
class QWidget;
struct CardRef;
class CardInfoFrameWidget : public QTabWidget
{

View file

@ -1,6 +1,12 @@
#include "card_info_picture_art_crop_widget.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "libcockatrice/card/printing/exact_card.h"
#include <QRect>
#include <QSize>
class QWidget;
CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent)
: CardInfoPictureWidget(parent, false, false)

View file

@ -9,6 +9,11 @@
#include "card_info_picture_widget.h"
#include <QPixmap>
#include <qtmetamacros.h>
class QWidget;
class CardInfoPictureArtCropWidget : public CardInfoPictureWidget
{
Q_OBJECT

View file

@ -2,10 +2,17 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/printing/exact_card.h"
#include <QPainterPath>
#include <QPoint>
#include <QRect>
#include <QSize>
#include <QStylePainter>
#include <QtGlobal>
#include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h>
/**
* @brief Constructs a CardPictureEnlargedWidget.
@ -52,12 +59,29 @@ void CardInfoPictureEnlargedWidget::loadPixmap(const QSize &size)
* @param size The desired size for the pixmap.
*
* Sets the widget's pixmap to the card image and resizes the widget to match the specified size. Triggers a repaint.
*
* When the image is not yet cached, the pixmap is cleared (instead of showing a stale previous card) and the widget
* refreshes automatically once the card image finishes loading.
*/
void CardInfoPictureEnlargedWidget::setCardPixmap(const ExactCard &_card, const QSize size)
{
if (card.getCardPtr()) {
disconnect(card.getCardPtr().data(), nullptr, this, nullptr);
}
card = _card;
// Clear any previous card's art so we never paint a stale pixmap while the new image loads
enlargedPixmap = QPixmap();
loadPixmap(size);
if (card.getCardPtr()) {
connect(card.getCardPtr().data(), &CardInfo::pixmapUpdated, this, [this]() {
loadPixmap(this->size());
update();
});
}
setFixedSize(size); // Set the widget size to the enlarged size
update(); // Trigger a repaint

View file

@ -8,8 +8,12 @@
#ifndef CARD_PICTURE_ENLARGED_WIDGET_H
#define CARD_PICTURE_ENLARGED_WIDGET_H
#include <QPixmap>
#include <QWidget>
#include <libcockatrice/card/printing/exact_card.h>
#include <qtmetamacros.h>
class QSize;
class CardInfoPictureEnlargedWidget final : public QWidget
{

View file

@ -1,20 +1,47 @@
#include "card_info_picture_widget.h"
#include "../../../client/settings/cache_settings.h"
#include "../../../game_graphics/board/card_item.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "../../../interface/widgets/tabs/tab_supervisor.h"
#include "../../window_main.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include "card_art_utils.h"
#include "card_info_picture_enlarged_widget.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/card/printing/printing_info.h"
#include "libcockatrice/deck_list/tree/inner_deck_list_node.h"
#include <QAbstractAnimation>
#include <QAction>
#include <QApplication>
#include <QByteArray>
#include <QCursor>
#include <QDebug>
#include <QEasingCurve>
#include <QGuiApplication>
#include <QList>
#include <QMenu>
#include <QMouseEvent>
#include <QObject>
#include <QPainterPath>
#include <QPropertyAnimation>
#include <QRect>
#include <QScreen>
#include <QSharedPointer>
#include <QSize>
#include <QString>
#include <QStylePainter>
#include <QTimer>
#include <QWidget>
#include <QtGlobal>
#include <algorithm>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <utility>
#include <qlogging.h>
#include <qnamespace.h>
static constexpr qreal MTG_CARD_ASPECT_RATIO = 1.396;
// static constexpr qreal YUGIOH_CARD_ASPECT_RATIO = 1.457;
@ -73,6 +100,8 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT
update();
});
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
&CardInfoPictureWidget::updatePixmap);
}
/**
@ -193,12 +222,7 @@ void CardInfoPictureWidget::paintEvent(QPaintEvent *event)
QPixmap transformedPixmap = resizedPixmap; // Default pixmap
if (SettingsCache::instance().cardsDisplay().getAutoRotateSidewaysLayoutCards()) {
if (exactCard.getInfo().getUiAttributes().landscapeOrientation) {
// Rotate pixmap 90 degrees to the left
QTransform transform;
transform.rotate(90);
transformedPixmap = resizedPixmap.transformed(transform, Qt::SmoothTransformation);
}
transformedPixmap = CardArtUtils::rotateSidewaysLayoutArt(resizedPixmap, exactCard);
}
// Handle DPI scaling

View file

@ -7,15 +7,20 @@
#ifndef CARD_INFO_PICTURE_H
#define CARD_INFO_PICTURE_H
#include "card_info_picture_enlarged_widget.h"
#include <QPropertyAnimation>
#include <QLoggingCategory>
#include <QMouseEvent>
#include <QPixmap>
#include <QPoint>
#include <QWidget>
#include <libcockatrice/card/printing/exact_card.h>
#include <qtmetamacros.h>
class CardInfoPictureEnlargedWidget;
class QPropertyAnimation;
class QTimer;
inline Q_LOGGING_CATEGORY(CardInfoPictureWidgetLog, "card_info_picture_widget");
class AbstractCardItem;
class QMenu;
class CardInfoPictureWidget : public QWidget

View file

@ -1,8 +1,24 @@
#include "card_info_picture_with_text_overlay_widget.h"
#include <QBrush>
#include <QChar>
#include <QFont>
#include <QFontMetrics>
#include <QList>
#include <QPainter>
#include <QPainterPath>
#include <QPalette>
#include <QPen>
#include <QPixmap>
#include <QPoint>
#include <QRect>
#include <QSize>
#include <QSizePolicy>
#include <QStringList>
#include <QStylePainter>
#include <algorithm>
class QWidget;
/**
* @brief Constructs a CardPictureWithTextOverlay widget.
@ -133,12 +149,14 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
path.addRoundedRect(glowRect, radius, radius);
// Soft outer glow
QColor glowColor(0, 150, 255, 80); // subtle blu
QColor glowColor = palette().color(QPalette::Highlight);
glowColor.setAlpha(80);
painter.setPen(QPen(glowColor, 6));
painter.drawPath(path);
// Thin inner border for crispness
QColor borderColor(0, 150, 255, 200);
QColor borderColor = palette().color(QPalette::Highlight);
borderColor.setAlpha(200);
painter.setPen(QPen(borderColor, 2));
painter.drawRoundedRect(pixmapRect, radius, radius);

View file

@ -11,8 +11,14 @@
#include "card_info_picture_widget.h"
#include <QColor>
#include <QSize>
#include <QString>
#include <QTextOption>
#include <qnamespace.h>
#include <qtmetamacros.h>
class QPainter;
class QRect;
class QWidget;
class CardInfoPictureWithTextOverlayWidget : public CardInfoPictureWidget
{

View file

@ -1,16 +1,28 @@
#include "card_info_text_widget.h"
#include "../../../game_graphics/board/card_item.h"
#include "../../../client/settings/cache_settings.h"
#include "../../card_localization.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/printing/printing_info.h"
#include "libcockatrice/card/set/card_set.h"
#include "libcockatrice/settings/cards_display_settings.h"
#include <QGridLayout>
#include <QLabel>
#include <QList>
#include <QScrollArea>
#include <QScrollBar>
#include <QSharedPointer>
#include <QSize>
#include <QStringList>
#include <QTextEdit>
#include <QWidget>
#include <libcockatrice/card/game_specific_terms.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <qnamespace.h>
#include <qobjectdefs.h>
CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent), info(nullptr)
CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent)
{
propsLabel = new QLabel;
propsLabel->setOpenExternalLinks(false);
@ -39,6 +51,12 @@ CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent), info(n
grid->setRowStretch(1, 1);
retranslateUi();
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this, [this] {
if (currentCard) {
setCard(currentCard);
}
});
}
void CardInfoTextWidget::setTexts(const QString &propsText, const QString &textText)
@ -60,7 +78,7 @@ void CardInfoTextWidget::setCard(const ExactCard &exactCard)
QString text = "<table width=\"100%\" border=0 cellspacing=0 cellpadding=0>";
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>%2</td></tr>")
.arg(tr("Name:"), card->getName().toHtmlEscaped());
.arg(tr("Name:"), CardLocalization::displayName(card).toHtmlEscaped());
if (!exactCard.getPrinting().isEmpty()) {
QString setShort = exactCard.getPrinting().getSet()->getShortName().toHtmlEscaped();
@ -94,7 +112,8 @@ void CardInfoTextWidget::setCard(const ExactCard &exactCard)
}
text += "</table>";
setTexts(text, card->getText());
setTexts(text, CardLocalization::displayText(card));
currentCard = exactCard;
}
void CardInfoTextWidget::setInvalidCardName(const QString &cardName)

View file

@ -10,10 +10,13 @@
#include "libcockatrice/card/printing/exact_card.h"
#include <QFrame>
#include <libcockatrice/card/card_info.h>
#include <QString>
#include <qtmetamacros.h>
class QLabel;
class QScrollArea;
class QTextEdit;
class QWidget;
class CardInfoTextWidget : public QFrame
{
@ -23,7 +26,7 @@ private:
QLabel *propsLabel;
QScrollArea *propsScroll;
QTextEdit *textLabel;
CardInfoPtr info;
ExactCard currentCard; ///< Last card set, re-rendered when the card language changes.
void setTexts(const QString &propsText, const QString &textText);
public:

View file

@ -1,7 +1,19 @@
#include "card_size_widget.h"
#include "../printing_selector/printing_selector.h"
#include "../visual_deck_storage/visual_deck_storage_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include <QEvent>
#include <QFlags>
#include <QHBoxLayout>
#include <QLabel>
#include <QList>
#include <QPoint>
#include <QScrollArea>
#include <QSizePolicy>
#include <QSlider>
#include <QWheelEvent>
#include <QtGlobal>
#include <qnamespace.h>
/**
* @class CardSizeWidget
@ -58,3 +70,39 @@ QSlider *CardSizeWidget::getSlider() const
{
return cardSizeSlider;
}
void CardSizeWidget::enableCtrlScrollResize(QWidget *host)
{
host->installEventFilter(this);
const auto scrollAreas = host->findChildren<QScrollArea *>();
for (QScrollArea *scrollArea : scrollAreas) {
if (QWidget *content = scrollArea->widget()) {
content->installEventFilter(this);
}
}
}
bool CardSizeWidget::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::Wheel && adjustSliderForWheel(static_cast<QWheelEvent *>(event))) {
return true;
}
return QWidget::eventFilter(watched, event);
}
bool CardSizeWidget::adjustSliderForWheel(QWheelEvent *event)
{
if (!(event->modifiers() & Qt::ControlModifier)) {
return false;
}
const int angleDelta = event->angleDelta().y();
if (angleDelta == 0) {
return false;
}
const int wheelSteps = angleDelta > 0 ? qMax(1, angleDelta / 120) : qMin(-1, angleDelta / 120);
cardSizeSlider->setValue(cardSizeSlider->value() + wheelSteps * CARD_SIZE_WHEEL_STEP);
return true;
}

View file

@ -9,13 +9,15 @@
#ifndef CARD_SIZE_WIDGET_H
#define CARD_SIZE_WIDGET_H
#include "../general/layout_containers/flow_widget.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
#include <QTimer>
#include <QWidget>
#include <qtmetamacros.h>
class QWheelEvent;
class FlowWidget;
class QHBoxLayout;
class QLabel;
class QSlider;
class CardSizeWidget : public QWidget
{
@ -25,6 +27,18 @@ public:
explicit CardSizeWidget(QWidget *parent, FlowWidget *flowWidget = nullptr, int defaultValue = 100);
[[nodiscard]] QSlider *getSlider() const;
/**
* @brief Resizes the cards when the user Ctrl + scrolls over @p host or any of its descendants.
*
* Installs this widget as an event filter on the given host widget. If the host contains a
* scroll area the filter is also installed on the scroll area's content widget, so the resize
* intercepts the wheel event before the scroll area would scroll the view.
*/
void enableCtrlScrollResize(QWidget *host);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private slots:
void updateCardSizeSetting(int newValue);
@ -44,6 +58,10 @@ private:
QSlider *cardSizeSlider;
QTimer debounceTimer; // Debounce timer
int pendingValue; // Stores the latest slider value
bool adjustSliderForWheel(QWheelEvent *event);
static constexpr int CARD_SIZE_WHEEL_STEP = 10; ///< Slider step applied per Ctrl + scroll notch.
};
#endif // CARD_SIZE_WIDGET_H

View file

@ -1,11 +1,38 @@
#include "deck_card_zone_display_widget.h"
#include "../../../client/settings/cache_settings.h"
#include "../general/display/banner_widget.h"
#include "../visual_deck_editor/visual_deck_editor_widget.h"
#include "card_group_display_widgets/card_group_display_widget.h"
#include "card_group_display_widgets/flat_card_group_display_widget.h"
#include "card_group_display_widgets/overlapped_card_group_display_widget.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_manager.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/deck_list/tree/deck_list_card_node.h"
#include "libcockatrice/settings/cards_display_settings.h"
#include <QResizeEvent>
#include <QAbstractItemModel>
#include <QDebug>
#include <QItemSelection>
#include <QItemSelectionModel>
#include <QItemSelectionRange>
#include <QLayout>
#include <QLayoutItem>
#include <QModelIndex>
#include <QSharedPointer>
#include <QSizePolicy>
#include <QStringLiteral>
#include <QTimer>
#include <QVBoxLayout>
#include <QVariant>
#include <QtGlobal>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <qnamespace.h>
class CardSizeWidget;
class QMouseEvent;
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,
@ -38,6 +65,7 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
banner->setBuddy(cardGroupContainer);
displayCards();
updateZoneCardCount();
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &DeckCardZoneDisplayWidget::onCategoryAddition);
if (selectionModel) {
@ -45,17 +73,17 @@ DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
&DeckCardZoneDisplayWidget::onSelectionChanged);
}
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &DeckCardZoneDisplayWidget::onCategoryRemoval);
connect(deckListModel, &QAbstractItemModel::rowsInserted, this, &DeckCardZoneDisplayWidget::updateZoneCardCount);
connect(deckListModel, &QAbstractItemModel::rowsRemoved, this, &DeckCardZoneDisplayWidget::updateZoneCardCount);
connect(deckListModel, &QAbstractItemModel::dataChanged, this, &DeckCardZoneDisplayWidget::updateZoneCardCount);
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::visualDeckEditorShowCardCountsChanged,
this, &DeckCardZoneDisplayWidget::updateZoneCardCount);
}
// =====================================================================================================================
// User Interaction
// =====================================================================================================================
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, const ExactCard &card)
{
emit cardClicked(event, card, zoneName);
}
void DeckCardZoneDisplayWidget::onHover(const ExactCard &card)
{
emit cardHovered(card);
@ -95,12 +123,18 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
}
auto categoryName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
// Cards in a custom zone belong to that zone, not the board zone, so that
// increment/decrement/swap actions target the custom zone.
const bool isCustomZone = index.data(DeckRoles::IsCustomZoneRole).toBool();
const QString effectiveZoneName = isCustomZone ? categoryName : zoneName;
const auto routeCardClick = [this, effectiveZoneName](QMouseEvent *event, const ExactCard &card) {
emit cardClicked(event, card, effectiveZoneName);
};
if (displayType == DisplayType::Overlap) {
auto *displayWidget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria,
activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this,
&DeckCardZoneDisplayWidget::onClick);
cardGroupContainer, deckListModel, selectionModel, index, effectiveZoneName, categoryName,
activeGroupCriteria, activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardClicked, this, routeCardClick);
connect(displayWidget, &OverlappedCardGroupDisplayWidget::cardHovered, this,
&DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
@ -111,9 +145,9 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
indexToWidgetMap.insert(index, displayWidget);
} else if (displayType == DisplayType::Flat) {
auto *displayWidget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, selectionModel, index,
zoneName, categoryName, activeGroupCriteria,
effectiveZoneName, categoryName, activeGroupCriteria,
activeSortCriteria, subBannerOpacity, cardSizeWidget);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardClicked, this, &DeckCardZoneDisplayWidget::onClick);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardClicked, this, routeCardClick);
connect(displayWidget, &FlatCardGroupDisplayWidget::cardHovered, this, &DeckCardZoneDisplayWidget::onHover);
connect(displayWidget, &CardGroupDisplayWidget::cleanupRequested, this,
&DeckCardZoneDisplayWidget::cleanupInvalidCardGroup);
@ -126,24 +160,18 @@ void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex
void DeckCardZoneDisplayWidget::displayCards()
{
QSortFilterProxyModel proxy;
proxy.setSourceModel(deckListModel);
proxy.setSortRole(Qt::EditRole);
proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
if (!trackedIndex.isValid()) {
return;
}
// 1. trackedIndex is a source index → map it to proxy space
QModelIndex proxyParent = proxy.mapFromSource(trackedIndex);
// 2. iterate children under the proxy parent
for (int i = 0; i < proxy.rowCount(proxyParent); ++i) {
QModelIndex proxyIndex = proxy.index(i, 0, proxyParent);
// 3. map back to source
QModelIndex sourceIndex = proxy.mapToSource(proxyIndex);
// 4. persist the source index
QPersistentModelIndex persistent(sourceIndex);
// Iterate the direct children of the tracked zone, keeping the tree view's row
// order (criteria groups first, then custom zones, both in the model's sort order).
QList<QPersistentModelIndex> rows;
for (int i = 0; i < deckListModel->rowCount(trackedIndex); ++i) {
rows.append(QPersistentModelIndex(deckListModel->index(i, 0, trackedIndex)));
}
for (const QPersistentModelIndex &persistent : rows) {
constructAppropriateWidget(persistent);
}
}
@ -249,3 +277,21 @@ QList<QString> DeckCardZoneDisplayWidget::getGroupCriteriaValueList()
return groupCriteriaValues;
}
void DeckCardZoneDisplayWidget::updateZoneCardCount()
{
if (!banner || !deckListModel) {
return;
}
QString text = zoneName;
if (SettingsCache::instance().cardsDisplay().getVisualDeckEditorShowCardCounts()) {
int total = 0;
const auto cardNodes = deckListModel->getCardNodesForZone(zoneName);
for (const auto *node : cardNodes) {
total += node->getNumber();
}
text += QStringLiteral(" (%1)").arg(total);
}
banner->setText(text);
}

View file

@ -7,16 +7,27 @@
#ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H
#define DECK_CARD_ZONE_DISPLAY_WIDGET_H
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h"
#include "../visual_deck_editor/visual_deck_editor_widget.h"
#include "card_group_display_widgets/card_group_display_widget.h"
#include "card_info_picture_with_text_overlay_widget.h"
#include "card_size_widget.h"
#include <QVBoxLayout>
#include <QHash>
#include <QList>
#include <QMouseEvent>
#include <QPersistentModelIndex>
#include <QString>
#include <QStringList>
#include <QWidget>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <qtmetamacros.h>
class BannerWidget;
class CardGroupDisplayWidget;
class CardSizeWidget;
class DeckListModel;
class ExactCard;
class OverlapWidget;
class QItemSelection;
class QItemSelectionModel;
class QModelIndex;
class QVBoxLayout;
class DeckCardZoneDisplayWidget : public QWidget
{
@ -42,7 +53,6 @@ public:
void addCardsToOverlapWidget();
public slots:
void onClick(QMouseEvent *event, const ExactCard &card);
void onHover(const ExactCard &card);
void cleanupInvalidCardGroup(CardGroupDisplayWidget *displayWidget);
void constructAppropriateWidget(QPersistentModelIndex index);
@ -53,6 +63,7 @@ public slots:
QList<QString> getGroupCriteriaValueList();
void onCategoryAddition(const QModelIndex &parent, int first, int last);
void onCategoryRemoval(const QModelIndex &parent, int first, int last);
void updateZoneCardCount();
signals:
void cardClicked(QMouseEvent *event, const ExactCard &card, const QString &zoneName);

View file

@ -1,15 +1,15 @@
#include "deck_preview_card_picture_widget.h"
#include "../../../client/settings/cache_settings.h"
#include "card_info_picture_widget.h"
#include <QApplication>
#include <QFileInfo>
#include <QFontMetrics>
#include <QMouseEvent>
#include <QPainterPath>
#include <QStylePainter>
#include <QTimer>
#include <libcockatrice/settings/visual_deck_storage_settings.h>
class QWidget;
/**
* @brief Constructs a CardPictureWithTextOverlay widget.
* @param parent The parent widget.
@ -27,18 +27,23 @@ DeckPreviewCardPictureWidget::DeckPreviewCardPictureWidget(QWidget *parent,
const QColor &textColor,
const QColor &outlineColor,
const int fontSize,
const Qt::Alignment alignment)
const Qt::Alignment alignment,
const bool _emitClickImmediately)
: CardInfoPictureWithTextOverlayWidget(parent,
hoverToZoomEnabled,
raiseOnEnter,
textColor,
outlineColor,
fontSize,
alignment)
alignment),
emitClickImmediately(_emitClickImmediately)
{
singleClickTimer = new QTimer(this);
singleClickTimer->setSingleShot(true);
connect(singleClickTimer, &QTimer::timeout, this, [this]() { emit imageClicked(lastMouseEvent, this); });
connect(singleClickTimer, &QTimer::timeout, this, [this]() {
emit imageClicked(lastMouseEvent, this);
emit imageSingleClicked();
});
connect(&SettingsCache::instance().visualDeckStorage(),
&VisualDeckStorageSettings::visualDeckStorageSelectionAnimationChanged, this,
&CardInfoPictureWidget::setRaiseOnEnterEnabled);
@ -47,8 +52,13 @@ DeckPreviewCardPictureWidget::DeckPreviewCardPictureWidget(QWidget *parent,
void DeckPreviewCardPictureWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
lastMouseEvent = event;
singleClickTimer->start(QApplication::doubleClickInterval());
if (emitClickImmediately) {
emit imageClicked(event, this);
emit imageSingleClicked();
} else {
lastMouseEvent = event;
singleClickTimer->start(QApplication::doubleClickInterval());
}
} else {
emit imageClicked(event, this);
event->accept();
@ -58,7 +68,14 @@ void DeckPreviewCardPictureWidget::mousePressEvent(QMouseEvent *event)
void DeckPreviewCardPictureWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
singleClickTimer->stop(); // Prevent single-click logic
emit imageDoubleClicked(lastMouseEvent, this);
if (emitClickImmediately) {
// Do not report a second single click for the second press of the
// double-click; the consumer maps the double-click to select+open.
lastMouseEvent = event;
emit imageDoubleClicked(event, this);
} else {
singleClickTimer->stop(); // Prevent single-click logic
emit imageDoubleClicked(lastMouseEvent, this);
}
}
}

View file

@ -11,30 +11,50 @@
#include "card_info_picture_with_text_overlay_widget.h"
#include <QColor>
#include <QSize>
#include <QTextOption>
#include <QTimer>
#include <QMouseEvent>
#include <qnamespace.h>
#include <qtmetamacros.h>
class QTimer;
class QWidget;
class DeckPreviewCardPictureWidget final : public CardInfoPictureWithTextOverlayWidget
{
Q_OBJECT
public:
/**
* @brief Constructs a DeckPreviewCardPictureWidget.
* @param parent The parent widget.
* @param hoverToZoomEnabled If this widget will spawn a larger widget when hovered over.
* @param raiseOnEnter If the widget raises its border when the mouse enters.
* @param textColor The color of the overlay text.
* @param outlineColor The color of the outline around the text.
* @param fontSize The font size of the overlay text.
* @param alignment The alignment of the text within the overlay.
* @param emitClickImmediately If true, a left click is reported immediately on click
* instead of after the double-click interval. Use this for selection surfaces
* where reacting to a double-click (select-and-open) would needlessly delay the
* single-click feedback. The double-click signal is still emitted.
*/
explicit DeckPreviewCardPictureWidget(QWidget *parent,
bool hoverToZoomEnabled = false,
bool raiseOnEnter = false,
const QColor &textColor = Qt::white,
const QColor &outlineColor = Qt::black,
int fontSize = 12,
Qt::Alignment alignment = Qt::AlignCenter);
Qt::Alignment alignment = Qt::AlignCenter,
bool _emitClickImmediately = false);
signals:
void imageClicked(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void imageSingleClicked();
void imageDoubleClicked(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
private:
QTimer *singleClickTimer;
QMouseEvent *lastMouseEvent = nullptr; // Store the last mouse event
bool emitClickImmediately;
protected:
void mousePressEvent(QMouseEvent *event) override;

View file

@ -1,8 +1,16 @@
#include "abstract_analytics_panel_widget.h"
#include "../../pixel_map_generator.h"
#include "../general/display/banner_widget.h"
#include "deck_list_statistics_analyzer.h"
#include <QDialog>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSizePolicy>
#include <QStringLiteral>
#include <QVBoxLayout>
#include <qnamespace.h>
AbstractAnalyticsPanelWidget::AbstractAnalyticsPanelWidget(QWidget *parent, DeckListStatisticsAnalyzer *analyzer)
: QWidget(parent), analyzer(analyzer)
@ -20,7 +28,7 @@ AbstractAnalyticsPanelWidget::AbstractAnalyticsPanelWidget(QWidget *parent, Deck
// config button
configureButton = new QPushButton(this);
configureButton->setIcon(QPixmap("theme:icons/cogwheel"));
configureButton->setIcon(themePixmap(QStringLiteral("icons/cogwheel")));
configureButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
connect(configureButton, &QPushButton::clicked, this, &AbstractAnalyticsPanelWidget::applyConfigFromDialog);
bannerAndSettingsLayout->addWidget(configureButton, 0);

View file

@ -3,12 +3,16 @@
#include "../general/display/banner_widget.h"
#include <QDialog>
#include <QJsonObject>
#include <QVBoxLayout>
#include <QString>
#include <QWidget>
#include <qtmetamacros.h>
class DeckListStatisticsAnalyzer;
class QDialog;
class QHBoxLayout;
class QPushButton;
class QVBoxLayout;
class AbstractAnalyticsPanelWidget : public QWidget
{

View file

@ -3,8 +3,11 @@
#include "analytics_panel_widget_factory.h"
#include <QDialogButtonBox>
#include <QList>
#include <QVBoxLayout>
class QWidget;
AddAnalyticsPanelDialog::AddAnalyticsPanelDialog(QWidget *parent) : QDialog(parent)
{
setWindowTitle(tr("Add Analytics Panel"));

View file

@ -2,12 +2,15 @@
#ifndef COCKATRICE_ADD_ANALYTICS_PANEL_DIALOG_H
#define COCKATRICE_ADD_ANALYTICS_PANEL_DIALOG_H
#include "analytics_panel_widget_factory.h"
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QString>
#include <QVariant>
#include <qtmetamacros.h>
class QDialogButtonBox;
class QVBoxLayout;
class QWidget;
class AddAnalyticsPanelDialog : public QDialog
{

View file

@ -2,6 +2,8 @@
#include "abstract_analytics_panel_widget.h"
#include <QSharedPointer>
AnalyticsPanelWidgetFactory &AnalyticsPanelWidgetFactory::instance()
{
static AnalyticsPanelWidgetFactory f;

View file

@ -1,9 +1,9 @@
#ifndef COCKATRICE_DECK_ANALYTICS_WIDGET_FACTORY_H
#define COCKATRICE_DECK_ANALYTICS_WIDGET_FACTORY_H
#include <QList>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QWidget>
#include <functional>

View file

@ -3,6 +3,8 @@
#include "analytics_panel_widget_factory.h"
#include <QString>
class AnalyticsPanelWidgetRegistrar
{
public:

View file

@ -1,5 +1,8 @@
#include "draw_probability_config.h"
#include <QJsonValue>
#include <QJsonValueRef>
QJsonObject DrawProbabilityConfig::toJson() const
{
QJsonObject o;

View file

@ -5,6 +5,10 @@
#include <QFormLayout>
#include <QLabel>
#include <QSpinBox>
#include <QString>
#include <QVariant>
class QWidget;
DrawProbabilityConfigDialog::DrawProbabilityConfigDialog(QWidget *parent) : QDialog(parent)
{

View file

@ -3,11 +3,13 @@
#include "draw_probability_config.h"
#include <QDialog>
#include <QFormLayout>
#include <qtmetamacros.h>
class QComboBox;
class QSpinBox;
class QLabel;
class QFormLayout;
class QWidget;
class DrawProbabilityConfigDialog : public QDialog
{

View file

@ -1,21 +1,40 @@
#include "draw_probability_widget.h"
#include "../../../general/display/banner_widget.h"
#include "../../../general/layout_containers/flow_widget.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../deck_list_statistics_analyzer.h"
#include "deck_list_model.h"
#include "draw_probability_config_dialog.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/deck_list/tree/deck_list_card_node.h"
#include <QAbstractItemView>
#include <QChar>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QJsonObject>
#include <QLabel>
#include <QList>
#include <QMap>
#include <QObject>
#include <QSharedPointer>
#include <QSizePolicy>
#include <QSpinBox>
#include <QString>
#include <QStringList>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QWidget>
#include <QVBoxLayout>
#include <QVariant>
#include <QtGlobal>
#include <QtMath>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <qnamespace.h>
class QWidget;
DrawProbabilityWidget::DrawProbabilityWidget(QWidget *parent, DeckListStatisticsAnalyzer *analyzer)
: AbstractAnalyticsPanelWidget(parent, analyzer)

View file

@ -1,16 +1,19 @@
#ifndef COCKATRICE_DRAW_PROBABILITY_WIDGET_H
#define COCKATRICE_DRAW_PROBABILITY_WIDGET_H
#include "../../../../layouts/flow_layout.h"
#include "../../../general/layout_containers/flow_widget.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../deck_list_statistics_analyzer.h"
#include "draw_probability_config.h"
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <QTableWidget>
#include <qtmetamacros.h>
class DeckListStatisticsAnalyzer;
class FlowWidget;
class QComboBox;
class QLabel;
class QLineEdit;
class QSpinBox;
class QTableWidget;
class QWidget;
class DrawProbabilityWidget : public AbstractAnalyticsPanelWidget
{

View file

@ -1,5 +1,9 @@
#include "mana_base_config.h"
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonValueRef>
QJsonObject ManaBaseConfig::toJson() const
{
QJsonObject jsonObject;

View file

@ -2,8 +2,9 @@
#ifndef COCKATRICE_MANA_BASE_CONFIG_H
#define COCKATRICE_MANA_BASE_CONFIG_H
#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <QStringList>
struct ManaBaseConfig

View file

@ -1,6 +1,19 @@
#include "mana_base_config_dialog.h"
#include "../../deck_list_statistics_analyzer.h"
#include <QAbstractItemView>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QHash>
#include <QLabel>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPushButton>
#include <QStringList>
#include <QVBoxLayout>
class QWidget;
ManaBaseConfigDialog::ManaBaseConfigDialog(DeckListStatisticsAnalyzer *analyzer,
ManaBaseConfig initial,

View file

@ -2,18 +2,20 @@
#ifndef COCKATRICE_MANA_BASE_ADD_DIALOG_H
#define COCKATRICE_MANA_BASE_ADD_DIALOG_H
#include "../../deck_list_statistics_analyzer.h"
#include "mana_base_config.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QListWidget>
#include <QVBoxLayout>
#include <QList>
#include <QString>
#include <qtmetamacros.h>
class DeckListStatisticsAnalyzer;
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QListWidget;
class QVBoxLayout;
class QWidget;
class ManaBaseConfigDialog : public QDialog
{

View file

@ -3,12 +3,27 @@
#include "../../../general/display/charts/bars/bar_widget.h"
#include "../../../general/display/charts/bars/color_bar.h"
#include "../../../general/display/charts/pies/color_pie.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../analytics_panel_widget_registrar.h"
#include "../../deck_list_statistics_analyzer.h"
#include "mana_base_config_dialog.h"
#include <QColor>
#include <QDialog>
#include <QListWidget>
#include <QHBoxLayout>
#include <QHash>
#include <QLayoutItem>
#include <QMap>
#include <QObject>
#include <QPair>
#include <QSharedPointer>
#include <QSize>
#include <QVBoxLayout>
#include <QWidget>
#include <algorithm>
#include <libcockatrice/utility/color.h>
#include <qnamespace.h>
#include <utility>
namespace
{

View file

@ -7,16 +7,17 @@
#ifndef MANA_BASE_WIDGET_H
#define MANA_BASE_WIDGET_H
#include "../../../general/display/banner_widget.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../deck_list_statistics_analyzer.h"
#include "mana_base_config.h"
#include <QHBoxLayout>
#include <QWidget>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/models/deck_list/deck_list_model.h>
#include <utility>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <qtmetamacros.h>
class DeckListStatisticsAnalyzer;
class QHBoxLayout;
class QWidget;
class ManaBaseWidget : public AbstractAnalyticsPanelWidget
{

View file

@ -1,9 +1,21 @@
#include "mana_curve_category_widget.h"
#include "../../../general/display/charts/bars/bar_chart_widget.h"
#include "libcockatrice/utility/color.h"
#include "libcockatrice/utility/qt_utils.h"
#include "mana_curve_config.h"
#include "mana_curve_total_widget.h"
#include <QHBoxLayout>
#include <QHash>
#include <QLabel>
#include <QList>
#include <QSharedPointer>
#include <QSize>
#include <QSizePolicy>
#include <QString>
#include <QVBoxLayout>
#include <QVector>
#include <algorithm>
constexpr int MIN_ROW_HEIGHT = 100; // Minimum readable height per row

View file

@ -1,12 +1,14 @@
#ifndef COCKATRICE_MANA_CURVE_CATEGORY_WIDGET_H
#define COCKATRICE_MANA_CURVE_CATEGORY_WIDGET_H
#include "../../../general/display/charts/bars/bar_chart_widget.h"
#include "mana_curve_config.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QStringList>
#include <QWidget>
#include <qtmetamacros.h>
class QString;
class QVBoxLayout;
struct ManaCurveConfig;
template <typename Key, typename T> class QHash;
class ManaCurveCategoryWidget : public QWidget
{

View file

@ -1,5 +1,9 @@
#include "mana_curve_config.h"
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonValueRef>
QJsonObject ManaCurveConfig::toJson() const
{
QJsonObject jsonObject;

View file

@ -2,8 +2,9 @@
#ifndef COCKATRICE_MANA_CURVE_CONFIG_H
#define COCKATRICE_MANA_CURVE_CONFIG_H
#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <QStringList>
struct ManaCurveConfig

View file

@ -1,12 +1,22 @@
#include "mana_curve_config_dialog.h"
#include "../../deck_list_statistics_analyzer.h"
#include <QAbstractItemView>
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QHash>
#include <QLabel>
#include <QList>
#include <QListWidget>
#include <QListWidgetItem>
#include <QString>
#include <QStringList>
#include <QVBoxLayout>
class QWidget;
ManaCurveConfigDialog::ManaCurveConfigDialog(DeckListStatisticsAnalyzer *analyzer, QWidget *parent)
: QDialog(parent), analyzer(analyzer)
{

View file

@ -1,16 +1,18 @@
#ifndef COCKATRICE_MANA_CURVE_ADD_DIALOG_H
#define COCKATRICE_MANA_CURVE_ADD_DIALOG_H
#include "../../deck_list_statistics_analyzer.h"
#include "mana_curve_config.h"
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <qtmetamacros.h>
class QListWidget;
class QCheckBox;
class QComboBox;
class DeckListStatisticsAnalyzer;
class QDialogButtonBox;
class QLabel;
class QWidget;
class ManaCurveConfigDialog : public QDialog
{

View file

@ -2,10 +2,18 @@
#include "../../../general/display/charts/bars/bar_chart_widget.h"
#include "libcockatrice/utility/color.h"
#include "libcockatrice/utility/qt_utils.h"
#include "mana_curve_config.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QList>
#include <QMap>
#include <QSharedPointer>
#include <QSize>
#include <QSizePolicy>
#include <QVector>
#include <algorithm>
#include <compare>
ManaCurveTotalWidget::ManaCurveTotalWidget(QWidget *parent) : QWidget(parent)
{

View file

@ -1,11 +1,15 @@
#ifndef COCKATRICE_MANA_CURVE_TOTAL_WIDGET_H
#define COCKATRICE_MANA_CURVE_TOTAL_WIDGET_H
#include "../../../general/display/charts/bars/bar_chart_widget.h"
#include "mana_curve_config.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QString>
#include <QStringList>
#include <QWidget>
#include <qtmetamacros.h>
class BarChartWidget;
class QHBoxLayout;
class QLabel;
struct ManaCurveConfig;
template <class Key, class T> class QMap;
class ManaCurveTotalWidget : public QWidget
{

View file

@ -1,19 +1,23 @@
#include "mana_curve_widget.h"
#include "../../../general/display/charts/bars/bar_chart_background_widget.h"
#include "../../../general/display/charts/bars/bar_chart_widget.h"
#include "../../../general/display/charts/bars/segmented_bar_widget.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../analytics_panel_widget_registrar.h"
#include "../../deck_list_statistics_analyzer.h"
#include "libcockatrice/utility/color.h"
#include "libcockatrice/utility/qt_utils.h"
#include "mana_curve_category_widget.h"
#include "mana_curve_config_dialog.h"
#include "mana_curve_total_widget.h"
#include <QInputDialog>
#include <QJsonArray>
#include <QLabel>
#include <QPushButton>
#include <QSettings>
#include <QDialog>
#include <QHash>
#include <QMap>
#include <QObject>
#include <QSharedPointer>
#include <QSizePolicy>
#include <QStringList>
#include <QVBoxLayout>
#include <QtGlobal>
class QWidget;
namespace
{

View file

@ -8,14 +8,17 @@
#define MANA_CURVE_WIDGET_H
#include "../../abstract_analytics_panel_widget.h"
#include "mana_curve_category_widget.h"
#include "mana_curve_config.h"
#include "mana_curve_total_widget.h"
#include <QVBoxLayout>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <qtmetamacros.h>
class SegmentedBarWidget;
class DeckListStatisticsAnalyzer;
class ManaCurveCategoryWidget;
class ManaCurveTotalWidget;
class QWidget;
class ManaCurveWidget : public AbstractAnalyticsPanelWidget
{

View file

@ -1,5 +1,9 @@
#include "mana_devotion_config.h"
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonValueRef>
QJsonObject ManaDevotionConfig::toJson() const
{
QJsonObject jsonObject;

View file

@ -1,8 +1,9 @@
#ifndef COCKATRICE_MANA_DEVOTION_CONFIG_H
#define COCKATRICE_MANA_DEVOTION_CONFIG_H
#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <QStringList>
struct ManaDevotionConfig

View file

@ -1,5 +1,19 @@
#include "mana_devotion_config_dialog.h"
#include "../../deck_list_statistics_analyzer.h"
#include <QAbstractItemView>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QHash>
#include <QLabel>
#include <QListWidget>
#include <QListWidgetItem>
#include <QStringList>
#include <QVBoxLayout>
class QWidget;
ManaDevotionConfigDialog::ManaDevotionConfigDialog(DeckListStatisticsAnalyzer *analyzer,
ManaDevotionConfig initial,
QWidget *parent)

View file

@ -2,16 +2,20 @@
#ifndef COCKATRICE_MANA_DEVOTION_ADD_DIALOG_H
#define COCKATRICE_MANA_DEVOTION_ADD_DIALOG_H
#include "../../deck_list_statistics_analyzer.h"
#include "mana_devotion_config.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QListWidget>
#include <QVBoxLayout>
#include <QList>
#include <QString>
#include <qtmetamacros.h>
class DeckListStatisticsAnalyzer;
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QListWidget;
class QVBoxLayout;
class QWidget;
class ManaDevotionConfigDialog : public QDialog
{

View file

@ -3,12 +3,26 @@
#include "../../../general/display/charts/bars/bar_widget.h"
#include "../../../general/display/charts/bars/color_bar.h"
#include "../../../general/display/charts/pies/color_pie.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../analytics_panel_widget_registrar.h"
#include "../../deck_list_statistics_analyzer.h"
#include "mana_devotion_config_dialog.h"
#include <QChar>
#include <QColor>
#include <QDialog>
#include <QHBoxLayout>
#include <QHash>
#include <QInputDialog>
#include <QLayoutItem>
#include <QMap>
#include <QObject>
#include <QSharedPointer>
#include <QSize>
#include <QVBoxLayout>
#include <QWidget>
#include <algorithm>
#include <qnamespace.h>
#include <utility>
namespace
{

View file

@ -6,11 +6,17 @@
#ifndef MANA_DEVOTION_WIDGET_H
#define MANA_DEVOTION_WIDGET_H
#include "../../../general/display/banner_widget.h"
#include "../../abstract_analytics_panel_widget.h"
#include "mana_devotion_config.h"
#include <QHBoxLayout>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <qtmetamacros.h>
class DeckListStatisticsAnalyzer;
class QHBoxLayout;
class QWidget;
class ManaDevotionWidget : public AbstractAnalyticsPanelWidget
{

View file

@ -1,5 +1,9 @@
#include "mana_distribution_config.h"
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonValueRef>
QJsonObject ManaDistributionConfig::toJson() const
{
QJsonObject o;

View file

@ -1,8 +1,8 @@
#ifndef COCKATRICE_MANA_DISTRIBUTION_CONFIG_H
#define COCKATRICE_MANA_DISTRIBUTION_CONFIG_H
#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QString>
#include <QStringList>

View file

@ -1,12 +1,19 @@
#include "mana_distribution_config_dialog.h"
#include <QAbstractItemView>
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QList>
#include <QListWidget>
#include <QListWidgetItem>
#include <QString>
#include <QStringList>
#include <QVBoxLayout>
class QWidget;
static const QStringList kColors = {"W", "U", "B", "R", "G", "C"};
ManaDistributionConfigDialog::ManaDistributionConfigDialog(DeckListStatisticsAnalyzer *analyzer, QWidget *parent)

View file

@ -4,14 +4,15 @@
#include "mana_distribution_config.h"
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QStringList>
#include <qtmetamacros.h>
class QComboBox;
class QListWidget;
class QCheckBox;
class DeckListStatisticsAnalyzer;
class QDialogButtonBox;
class QLabel;
class QWidget;
class ManaDistributionConfigDialog : public QDialog
{

View file

@ -2,7 +2,10 @@
#include "../../../cards/additional_info/mana_symbol_widget.h"
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
#include <qnamespace.h>
ManaDistributionSingleDisplayWidget::ManaDistributionSingleDisplayWidget(const QString &colorSymbol, QWidget *parent)
: QWidget(parent)

View file

@ -1,10 +1,12 @@
#ifndef COCKATRICE_MANA_DISTRIBUTION_SINGLE_DISPLAY_WIDGET_H
#define COCKATRICE_MANA_DISTRIBUTION_SINGLE_DISPLAY_WIDGET_H
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QString>
#include <QWidget>
#include <qtmetamacros.h>
class QLabel;
class QProgressBar;
class ManaDistributionSingleDisplayWidget : public QWidget
{

View file

@ -1,12 +1,21 @@
#include "mana_distribution_widget.h"
#include "../../../general/display/charts/bars/color_bar.h"
#include "../../../general/display/charts/pies/color_pie.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../analytics_panel_widget_registrar.h"
#include "../../deck_list_statistics_analyzer.h"
#include "mana_distribution_config_dialog.h"
#include "mana_distribution_single_display_widget.h"
#include <QComboBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QDialog>
#include <QHBoxLayout>
#include <QHash>
#include <QList>
#include <QSharedPointer>
#include <QStringList>
#include <QVBoxLayout>
#include <QWidget>
namespace
{

View file

@ -1,17 +1,21 @@
#ifndef COCKATRICE_MANA_DISTRIBUTION_WIDGET_H
#define COCKATRICE_MANA_DISTRIBUTION_WIDGET_H
#include "../../../general/display/charts/bars/color_bar.h"
#include "../../../general/display/charts/pies/color_pie.h"
#include "../../abstract_analytics_panel_widget.h"
#include "../../deck_list_statistics_analyzer.h"
#include "mana_distribution_config.h"
#include "mana_distribution_single_display_widget.h"
#include <QHBoxLayout>
#include <QJsonObject>
#include <QMap>
#include <QVBoxLayout>
#include <QWidget>
#include <QString>
#include <qtmetamacros.h>
class ColorBar;
class ColorPie;
class DeckListStatisticsAnalyzer;
class ManaDistributionSingleDisplayWidget;
class QHBoxLayout;
class QVBoxLayout;
class QWidget;
class ManaDistributionWidget : public AbstractAnalyticsPanelWidget
{

View file

@ -1,5 +1,6 @@
#include "deck_analytics_widget.h"
#include "../general/layout_containers/flow_widget.h"
#include "../general/tutorial/tutorial_controller.h"
#include "../tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h"
#include "abstract_analytics_panel_widget.h"
@ -12,13 +13,19 @@
#include "libcockatrice/utility/qt_utils.h"
#include "resizable_panel.h"
#include <QCheckBox>
#include <QDialog>
#include <QEvent>
#include <QFrame>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonValueRef>
#include <QPushButton>
#include <QScrollArea>
#include <QSettings>
#include <QVBoxLayout>
#include <QVariant>
#include <qnamespace.h>
DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnalyzer *_statsAnalyzer)
: QWidget(parent), statsAnalyzer(_statsAnalyzer)

View file

@ -15,12 +15,21 @@
#include <QCheckBox>
#include <QJsonObject>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QList>
#include <QString>
#include <QVector>
#include <QWidget>
#include <qtmetamacros.h>
class LayoutInspector;
class AbstractAnalyticsPanelWidget;
class DeckListStatisticsAnalyzer;
class FlowWidget;
class QCheckBox;
class QPushButton;
class QScrollArea;
class QVBoxLayout;
class ResizablePanel;
class DeckAnalyticsWidget : public QWidget
{

View file

@ -1,11 +1,19 @@
#include "deck_list_statistics_analyzer.h"
#include "deck_list_model.h"
#include "deck_list_statistics_analyzer.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/deck_list/tree/deck_list_card_node.h"
#include "libcockatrice/deck_list/tree/inner_deck_list_node.h"
#include <QChar>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QSharedPointer>
#include <QStringLiteral>
#include <initializer_list>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <utility>
DeckListStatisticsAnalyzer::DeckListStatisticsAnalyzer(QObject *parent,
DeckListModel *_model,

View file

@ -1,10 +1,12 @@
#ifndef COCKATRICE_DECK_LIST_STATISTICS_ANALYZER_H
#define COCKATRICE_DECK_LIST_STATISTICS_ANALYZER_H
#include "deck_list_model.h"
#include <QHash>
#include <QList>
#include <QObject>
#include <QString>
#include <QStringList>
#include <qtmetamacros.h>
#include <unordered_map>
class DeckListModel;

View file

@ -1,11 +1,34 @@
#include "resizable_panel.h"
#include "abstract_analytics_panel_widget.h"
#include "libcockatrice/utility/qt_utils.h"
#include <QApplication>
#include <QByteArray>
#include <QColor>
#include <QDrag>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QEvent>
#include <QFlags>
#include <QFrame>
#include <QHBoxLayout>
#include <QMimeData>
#include <QMouseEvent>
#include <QPalette>
#include <QPixmap>
#include <QPointF>
#include <QPushButton>
#include <QRect>
#include <QScrollArea>
#include <QScrollBar>
#include <QSize>
#include <QSizePolicy>
#include <QTimer>
#include <QVBoxLayout>
#include <QtGlobal>
#include <qnamespace.h>
ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWidget *analyticsPanel, QWidget *parent)
: QWidget(parent), panel(analyticsPanel), typeId(_typeId)

View file

@ -1,19 +1,15 @@
#ifndef COCKATRICE_RESIZABLE_PANEL_H
#define COCKATRICE_RESIZABLE_PANEL_H
#include "abstract_analytics_panel_widget.h"
#include <QApplication>
#include <QDrag>
#include <QFrame>
#include <QMimeData>
#include <QMouseEvent>
#include <QPushButton>
#include <QScrollArea>
#include <QScrollBar>
#include <QTimer>
#include <QVBoxLayout>
#include <QPoint>
#include <QString>
#include <QWidget>
#include <qtmetamacros.h>
class AbstractAnalyticsPanelWidget;
class QFrame;
class QPushButton;
class QTimer;
class ResizablePanel : public QWidget
{

View file

@ -1,17 +1,35 @@
#include "card_database_view.h"
#include "../../../client/settings/cache_settings.h"
#include "../../key_signals.h"
#include "card_database_display_model.h"
#include "card_database_model.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include <QAbstractItemModel>
#include <QAction>
#include <QApplication>
#include <QByteArray>
#include <QClipboard>
#include <QFlags>
#include <QHeaderView>
#include <QItemSelectionModel>
#include <QList>
#include <QMenu>
#include <QModelIndex>
#include <QModelIndexList>
#include <QPoint>
#include <QSharedPointer>
#include <QVariant>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
#include <libcockatrice/settings/layouts_settings.h>
#include <qnamespace.h>
#include <utility>
class QWidget;
static bool canBeCommander(const CardInfo &cardInfo)
{
@ -90,6 +108,13 @@ void CardDatabaseView::decrementCard(const QString &zoneName)
emit cardDecremented(currentCardName(), zoneName);
}
void CardDatabaseView::setZoneMenuProvider(const std::function<QList<QPair<QString, QStringList>>()> &provider,
const std::function<QString()> &newZoneHandler)
{
zoneMenuProvider = provider;
this->newZoneHandler = newZoneHandler;
}
void CardDatabaseView::updateCard(const QModelIndex &current, const QModelIndex & /*previous*/)
{
if (!current.isValid()) {
@ -142,6 +167,50 @@ void CardDatabaseView::openCustomMenu(QPoint point)
[this, card] { emit cardAdded(card->getName(), DECK_ZONE_SIDE); });
connect(selectPrinting, &QAction::triggered, this, &CardDatabaseView::selectPrintingClicked);
if (zoneMenuProvider) {
QMenu *addToZoneMenu = menu.addMenu(tr("Add to Zone"));
const auto zoneBoards = zoneMenuProvider();
for (const QString &boardName : InnerDecklistNode::boardZoneNames()) {
// Boards with zones nest their children so no two menu entries
// share a visible name: "Maindeck ▸ { Maindeck (whole board), … }".
const QStringList customZones = [&zoneBoards, boardName] {
for (const auto &zoneBoard : zoneBoards) {
if (zoneBoard.first == boardName) {
return zoneBoard.second;
}
}
return QStringList();
}();
if (customZones.isEmpty()) {
QAction *action = addToZoneMenu->addAction(InnerDecklistNode::visibleNameFromName(boardName));
connect(action, &QAction::triggered, this,
[this, card, boardName] { emit cardAdded(card->getName(), boardName); });
} else {
QMenu *boardSubmenu = addToZoneMenu->addMenu(InnerDecklistNode::visibleNameFromName(boardName));
QAction *wholeBoardAction = boardSubmenu->addAction(InnerDecklistNode::visibleNameFromName(boardName));
connect(wholeBoardAction, &QAction::triggered, this,
[this, card, boardName] { emit cardAdded(card->getName(), boardName); });
for (const QString &zoneName : customZones) {
QAction *action = boardSubmenu->addAction(zoneName);
connect(action, &QAction::triggered, this,
[this, card, zoneName] { emit cardAdded(card->getName(), zoneName); });
}
}
}
if (newZoneHandler) {
addToZoneMenu->addSeparator();
QAction *newZoneAction = addToZoneMenu->addAction(tr("Create &new zone..."));
connect(newZoneAction, &QAction::triggered, this, [this, card] {
const QString zoneName = newZoneHandler();
if (!zoneName.isEmpty()) {
emit cardAdded(card->getName(), zoneName);
}
});
}
}
if (canBeCommander(*card)) {
QAction *edhRecCommander = menu.addAction(tr("Show on EDHRec (Commander)"));
connect(edhRecCommander, &QAction::triggered, this, [this, card] { emit edhrecClicked(card, true); });

View file

@ -3,11 +3,19 @@
#include "../../key_signals.h"
#include <QPair>
#include <QString>
#include <QStringList>
#include <QTreeView>
#include <functional>
#include <libcockatrice/card/card_info.h>
#include <qtmetamacros.h>
class CardDatabaseModel;
class CardDatabaseDisplayModel;
class QModelIndex;
class QPoint;
class QWidget;
template <typename T> class QList;
/**
* @brief The card database table.
@ -19,6 +27,13 @@ class CardDatabaseView : public QTreeView
KeySignals searchKeySignals;
CardDatabaseDisplayModel *databaseDisplayModel;
/// Provides the custom zones available in the current deck, grouped by board zone.
/// The list contains (board zone name, custom zone names) pairs for every board.
std::function<QList<QPair<QString, QStringList>>()> zoneMenuProvider;
/// Handler invoked when the user picks "New zone..." from the add-to-zone menu.
/// Returns the name of the created zone, or an empty string if creation was cancelled.
std::function<QString()> newZoneHandler;
public:
explicit CardDatabaseView(QWidget *parent, CardDatabaseDisplayModel *model);
@ -33,6 +48,17 @@ public:
return &searchKeySignals;
}
/**
* @brief Sets the provider used to populate the "Add to zone" submenu of the context menu.
* If no provider is set, the submenu is not shown.
*
* @param provider Returns the custom zones of the current deck, grouped by board zone
* @param newZoneHandler Creates a new custom zone and returns its name, or an empty string
* if creation was cancelled. The menu entry is hidden when not provided.
*/
void setZoneMenuProvider(const std::function<QList<QPair<QString, QStringList>>()> &provider,
const std::function<QString()> &newZoneHandler);
signals:
void cardChanged(const QString &cardName);

View file

@ -1,5 +1,25 @@
#include "deck_editor_card_database_dock_widget.h"
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "card_database_view.h"
#include "deck_editor_database_display_widget.h"
#include "deck_list_model.h"
#include "deck_state_manager.h"
#include "deck_zone_dialog.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/deck_list/tree/inner_deck_list_node.h"
#include <QFlags>
#include <QList>
#include <QPair>
#include <QString>
#include <QStringList>
#include <QVBoxLayout>
#include <QWidget>
#include <functional>
#include <qnamespace.h>
#include <utility>
DeckEditorCardDatabaseDockWidget::DeckEditorCardDatabaseDockWidget(AbstractTabDeckEditor *parent) : QDockWidget(parent)
{
setObjectName("databaseDisplayDock");
@ -15,6 +35,27 @@ void DeckEditorCardDatabaseDockWidget::createDatabaseDisplayDock(AbstractTabDeck
{
databaseDisplayWidget = new DeckEditorDatabaseDisplayWidget(this, deckEditor->databaseModel);
databaseDisplayWidget->getDatabaseView()->setZoneMenuProvider(
[deckEditor]() -> QList<QPair<QString, QStringList>> {
QList<QPair<QString, QStringList>> result;
auto *deckListModel = deckEditor->deckStateManager->getModel();
for (const QString &boardName : InnerDecklistNode::boardZoneNames()) {
result.append({boardName, deckListModel->getCustomZoneNames(boardName)});
}
return result;
},
[this, deckEditor]() -> QString {
QString boardName;
const QString zoneName =
DeckZoneDialog::promptForNewZone(this, {}, &boardName, [deckEditor](const QString &candidate) {
return deckEditor->deckStateManager->validateNewZoneName(candidate);
});
if (!zoneName.isEmpty()) {
deckEditor->deckStateManager->createCustomZone(boardName, zoneName);
}
return zoneName;
});
auto *frame = new QVBoxLayout;
frame->setObjectName("databaseDisplayFrame");
frame->addWidget(databaseDisplayWidget);

View file

@ -1,12 +1,10 @@
#ifndef COCKATRICE_DECK_EDITOR_CARD_DATABASE_DOCK_WIDGET_H
#define COCKATRICE_DECK_EDITOR_CARD_DATABASE_DOCK_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include <QDockWidget>
#include <qtmetamacros.h>
class AbstractTabDeckEditor;
class CardDatabase;
class DeckEditorDatabaseDisplayWidget;
class FilterTree;

View file

@ -1,8 +1,14 @@
#include "deck_editor_card_info_dock_widget.h"
#include "../cards/card_info_frame_widget.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include <QFlags>
#include <QVBoxLayout>
#include <QWidget>
#include <qnamespace.h>
class ExactCard;
DeckEditorCardInfoDockWidget::DeckEditorCardInfoDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent)

View file

@ -8,12 +8,13 @@
#ifndef DECK_EDITOR_CARD_INFO_DOCK_WIDGET_H
#define DECK_EDITOR_CARD_INFO_DOCK_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../cards/card_info_frame_widget.h"
#include <QDockWidget>
#include <qtmetamacros.h>
class AbstractTabDeckEditor;
class CardInfoFrameWidget;
class ExactCard;
class DeckEditorCardInfoDockWidget : public QDockWidget
{
Q_OBJECT

View file

@ -2,17 +2,30 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../filters/syntax_help.h"
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../../../interface/widgets/tabs/tab_supervisor.h"
#include "../../key_signals.h"
#include "../../pixel_map_generator.h"
#include "../utility/custom_line_edit.h"
#include "card_database_display_model.h"
#include "card_database_model.h"
#include "card_database_view.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/deck_list/tree/inner_deck_list_node.h"
#include <QClipboard>
#include <QHeaderView>
#include <QAction>
#include <QHBoxLayout>
#include <QIcon>
#include <QLineEdit>
#include <QStringLiteral>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>
#include <libcockatrice/card/card_localization.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/utility/card_ref.h>
#include <qnamespace.h>
class FilterTree;
DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent, CardDatabaseModel *databaseModel)
: QWidget(parent)
@ -28,7 +41,7 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
searchEdit->setClearButtonEnabled(true);
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
auto help = searchEdit->addAction(themePixmap(QStringLiteral("icons/info")), QLineEdit::TrailingPosition);
setFocusProxy(searchEdit);
setFocusPolicy(Qt::ClickFocus);
@ -40,6 +53,17 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent
databaseDisplayModel->setSourceModel(databaseModel);
databaseDisplayModel->setFilterKeyColumn(0);
const auto applyCardSearchLanguage = [this]() {
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
databaseDisplayModel->setSearchLanguage(CardSearchLanguage{
cardsDisplay.getCardLang(), static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
};
applyCardSearchLanguage();
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
applyCardSearchLanguage);
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
applyCardSearchLanguage);
databaseView = new CardDatabaseView(this, databaseDisplayModel);
databaseView->setObjectName("databaseView");
databaseView->setFocusProxy(searchEdit);
@ -59,13 +83,13 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent
&DeckEditorDatabaseDisplayWidget::onRelatedCardClicked);
aAddCard = new QAction(QString(), this);
aAddCard->setIcon(QPixmap("theme:icons/arrow_right_green"));
aAddCard->setIcon(themePixmap(QStringLiteral("icons/arrow_right_green")));
connect(aAddCard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck);
auto *tbAddCard = new QToolButton(this);
tbAddCard->setDefaultAction(aAddCard);
aAddCardToSideboard = new QAction(QString(), this);
aAddCardToSideboard->setIcon(QPixmap("theme:icons/arrow_right_blue"));
aAddCardToSideboard->setIcon(themePixmap(QStringLiteral("icons/arrow_right_blue")));
connect(aAddCardToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
auto *tbAddCardToSideboard = new QToolButton(this);
tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard);

View file

@ -8,16 +8,21 @@
#ifndef DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H
#define DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../utility/custom_line_edit.h"
#include "libcockatrice/card/card_info.h"
#include <QHBoxLayout>
#include <QString>
#include <QWidget>
#include <libcockatrice/models/database/card_database_display_model.h>
#include <libcockatrice/models/database/card_database_model.h>
#include <qtmetamacros.h>
class CardDatabaseView;
class AbstractTabDeckEditor;
class CardDatabaseDisplayModel;
class CardDatabaseModel;
class ExactCard;
class FilterTree;
class QAction;
class QHBoxLayout;
class QVBoxLayout;
class SearchLineEdit;
class DeckEditorDatabaseDisplayWidget : public QWidget
{

View file

@ -2,22 +2,77 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../key_signals.h"
#include "../../pixel_map_generator.h"
#include "../general/tutorial/tutorial_controller.h"
#include "../playmat/playmat_settings_dialog.h"
#include "../quick_settings/settings_button_widget.h"
#include "../settings_page/user_interface_settings_page.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include "../tabs/api/commander_spellbook/commander_bracket_widget.h"
#include "../utility/custom_line_edit.h"
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "deck_list_history_manager_widget.h"
#include "deck_list_model.h"
#include "deck_list_style_proxy.h"
#include "deck_state_manager.h"
#include "deck_zone_dialog.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database.h"
#include "libcockatrice/card/database/card_database_loader.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/printing_info.h"
#include "libcockatrice/deck_list/deck_list.h"
#include "libcockatrice/deck_list/tree/abstract_deck_list_node.h"
#include "libcockatrice/deck_list/tree/inner_deck_list_node.h"
#include "libcockatrice/utility/card_ref.h"
#include "libcockatrice/utility/playmat_params.h"
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QAction>
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QDialog>
#include <QDockWidget>
#include <QFlags>
#include <QGridLayout>
#include <QHeaderView>
#include <QItemSelectionModel>
#include <QLabel>
#include <QList>
#include <QMap>
#include <QMenu>
#include <QMessageBox>
#include <QModelIndex>
#include <QPair>
#include <QPoint>
#include <QPushButton>
#include <QSet>
#include <QSharedPointer>
#include <QSize>
#include <QSizePolicy>
#include <QSplitter>
#include <QStringList>
#include <QStringLiteral>
#include <QTextEdit>
#include <QTimer>
#include <QToolButton>
#include <QVBoxLayout>
#include <QVariant>
#include <QWidget>
#include <QtGlobal>
#include <algorithm>
#include <compare>
#include <functional>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list_node_tree.h>
#include <libcockatrice/settings/deck_editor_settings.h>
#include <libcockatrice/utility/macros.h>
#include <libcockatrice/utility/string_limits.h>
#include <qnamespace.h>
#include <utility>
static int findRestoreIndex(const CardRef &wanted, const QComboBox *combo)
{
@ -187,25 +242,25 @@ void DeckEditorDeckDockWidget::createDeckDock()
&DeckEditorDeckDockWidget::applyActiveGroupCriteria);
aIncrement = new QAction(QString(), this);
aIncrement->setIcon(QPixmap("theme:icons/increment"));
aIncrement->setIcon(themePixmap(QStringLiteral("icons/increment")));
connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection);
auto *tbIncrement = new QToolButton(this);
tbIncrement->setDefaultAction(aIncrement);
aDecrement = new QAction(QString(), this);
aDecrement->setIcon(QPixmap("theme:icons/decrement"));
aDecrement->setIcon(themePixmap(QStringLiteral("icons/decrement")));
connect(aDecrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actDecrementSelection);
auto *tbDecrement = new QToolButton(this);
tbDecrement->setDefaultAction(aDecrement);
aRemoveCard = new QAction(QString(), this);
aRemoveCard->setIcon(QPixmap("theme:icons/remove_row"));
aRemoveCard->setIcon(themePixmap(QStringLiteral("icons/remove_row")));
connect(aRemoveCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actRemoveCard);
auto *tbRemoveCard = new QToolButton(this);
tbRemoveCard->setDefaultAction(aRemoveCard);
aSwapCard = new QAction(QString(), this);
aSwapCard->setIcon(QPixmap("theme:icons/swap"));
aSwapCard->setIcon(themePixmap(QStringLiteral("icons/swap")));
connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapSelection);
auto *tbSwapCard = new QToolButton(this);
tbSwapCard->setDefaultAction(aSwapCard);
@ -229,10 +284,18 @@ void DeckEditorDeckDockWidget::createDeckDock()
upperLayout->addWidget(bannerCardLabel, 4, 0);
upperLayout->addWidget(bannerCardComboBox, 4, 1);
upperLayout->addWidget(deckTagsDisplayWidget, 5, 1);
playmatLabel = new QLabel();
playmatLabel->setObjectName("playmatLabel");
playmatLabel->setText(tr("Playmat"));
playmatSettingsButton = new QPushButton(tr("Edit Playmat..."));
connect(playmatSettingsButton, &QPushButton::clicked, this, &DeckEditorDeckDockWidget::openPlaymatSettings);
upperLayout->addWidget(playmatLabel, 5, 0);
upperLayout->addWidget(playmatSettingsButton, 5, 1);
upperLayout->addWidget(activeGroupCriteriaLabel, 6, 0);
upperLayout->addWidget(activeGroupCriteriaComboBox, 6, 1);
upperLayout->addWidget(deckTagsDisplayWidget, 6, 1);
upperLayout->addWidget(activeGroupCriteriaLabel, 7, 0);
upperLayout->addWidget(activeGroupCriteriaComboBox, 7, 1);
hashLabel1 = new QLabel();
hashLabel1->setObjectName("hashLabel1");
@ -347,7 +410,9 @@ ExactCard DeckEditorDeckDockWidget::getCurrentCard()
if (!current.isValid()) {
return {};
}
const QString cardName = current.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
// The display role holds the localized card name; the edit role always carries the
// canonical English name needed to look the card up in the database.
const QString cardName = current.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
const QString cardProviderID = current.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString();
const QModelIndex gparent = current.parent().parent();
@ -457,6 +522,35 @@ void DeckEditorDeckDockWidget::writeBannerCard(int index)
deckStateManager->setBannerCard(bannerCard);
}
void DeckEditorDeckDockWidget::openPlaymatSettings()
{
PlaymatInfo current = deckStateManager->getMetadata().playmat;
PlaymatSettingsDialog dialog(current.card, current.params, this);
if (dialog.exec() == QDialog::Accepted) {
CardRef newCard = dialog.card();
PlaymatParams newParams = dialog.params();
if (newCard.isEmpty()) {
deckStateManager->setPlaymat(PlaymatInfo{});
} else {
deckStateManager->setPlaymat({newCard, newParams});
}
updatePlaymatLabel();
}
}
void DeckEditorDeckDockWidget::updatePlaymatLabel()
{
CardRef playmat = deckStateManager->getMetadata().playmat.card;
if (playmat.isEmpty()) {
playmatSettingsButton->setText(tr("Edit Playmat..."));
} else {
playmatSettingsButton->setText(tr("Edit Playmat (%1)").arg(playmat.name));
}
}
void DeckEditorDeckDockWidget::applyActiveGroupCriteria()
{
getModel()->setActiveGroupCriteria(
@ -491,8 +585,11 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex, bool preserveWidgetFocus)
{
const QModelIndex proxyIndex = proxy->mapFromSource(newCardIndex);
deckView->clearSelection();
deckView->setCurrentIndex(newCardIndex);
deckView->setCurrentIndex(proxyIndex);
deckView->scrollTo(proxyIndex);
recursiveExpand(newCardIndex);
if (!preserveWidgetFocus) {
@ -514,6 +611,7 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
syncBannerCardComboBoxSelectionWithDeck();
updateBannerCardComboBox();
bannerCardComboBox->blockSignals(false);
updatePlaymatLabel();
updateHash();
formatComboBox->blockSignals(true);
@ -748,14 +846,213 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool i
void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point)
{
const QModelIndex sourceIndex = proxy->mapToSource(deckView->indexAt(point));
QMenu menu;
const bool isCustomZoneRow = sourceIndex.isValid() && sourceIndex.data(DeckRoles::IsCustomZoneRole).toBool();
const bool isBoardZoneRow = sourceIndex.isValid() && !isCustomZoneRow && !sourceIndex.parent().isValid();
const bool isCardRow =
sourceIndex.isValid() && !isCustomZoneRow && !isBoardZoneRow && !getModel()->hasChildren(sourceIndex);
// Walk the row up to its top-level node to find the hosting board. Cards in
// the tokens board cannot be moved (moveCardToZone bails for it), so the
// move menu is skipped for them.
QString currentBoardName;
QModelIndex board = sourceIndex.parent();
while (board.isValid() && board.parent().isValid()) {
board = board.parent();
}
if (board.isValid()) {
currentBoardName = board.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
}
if (isCardRow) {
if (currentBoardName != DECK_ZONE_TOKENS) {
addMoveToZoneMenu(&menu, sourceIndex, currentBoardName);
menu.addSeparator();
}
} else if (isCustomZoneRow) {
const QString zoneName =
sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
QAction *renameAction = menu.addAction(tr("&Rename zone..."));
connect(renameAction, &QAction::triggered, this, [this, zoneName] {
// The unchanged name must not validate as a duplicate.
const QString newName =
DeckZoneDialog::promptForRename(this, zoneName, [this, zoneName](const QString &candidate) {
return candidate == zoneName ? QString() : deckStateManager->validateNewZoneName(candidate);
});
if (!newName.isEmpty() && newName != zoneName) {
deckStateManager->renameCustomZone(zoneName, newName);
}
});
QMenu *boardMenu = menu.addMenu(tr("Change &board"));
addChangeBoardMenu(boardMenu, zoneName);
QAction *deleteAction = menu.addAction(tr("&Delete zone"));
const bool zoneHasCards = getModel()->hasChildren(sourceIndex);
deleteAction->setEnabled(!zoneHasCards);
if (zoneHasCards) {
deleteAction->setToolTip(tr("Move or remove all cards first."));
menu.setToolTipsVisible(true);
}
connect(deleteAction, &QAction::triggered, this, [this, zoneName] {
const auto result =
QMessageBox::warning(this, tr("Delete zone"), tr("Delete the zone \"%1\"?").arg(zoneName),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (result == QMessageBox::Yes) {
deckStateManager->removeCustomZone(zoneName);
}
});
menu.addSeparator();
} else if (isBoardZoneRow) {
const QString boardName =
sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
// Tokens cannot host custom zones, so only offer the action on real boards.
const bool canHostCustomZones =
boardName == DECK_ZONE_MAIN || boardName == DECK_ZONE_SIDE || boardName == DECK_ZONE_MAYBEBOARD;
if (canHostCustomZones) {
addNewZoneAction(&menu, boardName);
menu.addSeparator();
}
} else if (!sourceIndex.isValid()) {
addNewZoneAction(&menu);
menu.addSeparator();
}
QAction *selectPrinting = menu.addAction(tr("Select Printing"));
connect(selectPrinting, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::showPrintingSelector);
menu.exec(deckView->mapToGlobal(point));
}
void DeckEditorDeckDockWidget::addMoveToZoneMenu(QMenu *menu,
const QModelIndex &sourceCardIndex,
const QString &currentBoardName)
{
// The card's current *zone*, derived with the same ancestor walk as
// DeckStateManager::moveCardToZone (nearest custom-zone ancestor, else the
// top-level board/zone): a card inside "Removal" under the maindeck lives in
// "Removal", not "main". Comparing against that instead of the board keeps
// the enabled state and the same-zone no-op consistent with the move logic.
QString currentZoneName;
for (QModelIndex ancestor = sourceCardIndex.parent(); ancestor.isValid(); ancestor = ancestor.parent()) {
if (ancestor.data(DeckRoles::IsCustomZoneRole).toBool() || !ancestor.parent().isValid()) {
currentZoneName = ancestor.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
break;
}
}
const auto addMoveAction = [this, sourceCardIndex](QMenu *targetMenu, const QString &targetZoneName,
const QString &label, bool enabled) {
QAction *action = targetMenu->addAction(label);
action->setEnabled(enabled);
if (enabled) {
connect(action, &QAction::triggered, this, [this, sourceCardIndex, targetZoneName] {
deckStateManager->moveCardToZone(sourceCardIndex, targetZoneName);
});
}
};
const auto tree = deckStateManager->getDeckListShared()->getTree();
QMenu *moveMenu = menu->addMenu(tr("Move to &zone"));
for (const QString &boardName : InnerDecklistNode::boardZoneNames()) {
const QString boardLabel = InnerDecklistNode::visibleNameFromName(boardName);
const auto customZones = tree->getCustomZones(boardName);
// Boards with zones nest their children so no two menu entries share a
// visible name: "Maindeck ▸ { Maindeck (whole board), Removal, … }".
// The board the card already lives on is marked instead of offered.
if (!customZones.isEmpty()) {
QMenu *boardSubmenu = moveMenu->addMenu(boardLabel);
addMoveAction(boardSubmenu, boardName, boardLabel, boardName != currentZoneName);
for (const auto *customZone : customZones) {
addMoveAction(boardSubmenu, customZone->getName(), customZone->getName(),
customZone->getName() != currentZoneName);
}
} else {
addMoveAction(moveMenu, boardName, boardLabel, boardName != currentZoneName);
}
}
moveMenu->addSeparator();
QAction *newZoneAction = moveMenu->addAction(tr("Create new zone and move &here..."));
connect(newZoneAction, &QAction::triggered, this, [this, sourceCardIndex, currentBoardName, currentZoneName] {
// Resolve the card's identity before creating the zone:
// createNewCustomZone rebuilds the model tree, so sourceCardIndex's
// internal pointer is freed by the time it would be used.
const QString cardName =
sourceCardIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
const QString providerId =
sourceCardIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString();
const QString collectorNumber = sourceCardIndex.siblingAtColumn(DeckListModelColumns::CARD_COLLECTOR_NUMBER)
.data(Qt::DisplayRole)
.toString();
const QString zoneName = createNewCustomZone(currentBoardName);
if (!zoneName.isEmpty()) {
// Re-find the card: the old index is no longer safe since rows were
// rebuilt. Mirror DeckStateManager::decrementCard's re-find pattern.
const QModelIndex refreshed = getModel()->findCard(cardName, currentZoneName, providerId, collectorNumber);
if (refreshed.isValid()) {
deckStateManager->moveCardToZone(refreshed, zoneName);
}
}
});
}
void DeckEditorDeckDockWidget::addChangeBoardMenu(QMenu *menu, const QString &zoneName)
{
const auto tree = deckStateManager->getDeckListShared()->getTree();
for (const QString &boardName : InnerDecklistNode::boardZoneNames()) {
QAction *action = menu->addAction(InnerDecklistNode::visibleNameFromName(boardName));
// The board currently holding the zone is marked instead of offered.
// Duplicate names cannot come up through the editor, so this doubles as
// the uniqueness guard for imported decks.
bool holdsTheZone = false;
for (const auto *customZone : tree->getCustomZones(boardName)) {
if (customZone->getName() == zoneName) {
holdsTheZone = true;
break;
}
}
if (holdsTheZone) {
action->setCheckable(true);
action->setChecked(true);
continue;
}
connect(action, &QAction::triggered, this,
[this, zoneName, boardName] { deckStateManager->moveCustomZone(zoneName, boardName); });
}
}
void DeckEditorDeckDockWidget::addNewZoneAction(QMenu *menu, const QString &initialBoardName)
{
QAction *newZoneAction = menu->addAction(tr("Create &new zone..."));
connect(newZoneAction, &QAction::triggered, this,
[this, initialBoardName] { createNewCustomZone(initialBoardName); });
}
QString DeckEditorDeckDockWidget::createNewCustomZone(const QString &initialBoardName)
{
QString boardName;
const QString zoneName =
DeckZoneDialog::promptForNewZone(this, initialBoardName, &boardName, [this](const QString &candidate) {
return deckStateManager->validateNewZoneName(candidate);
});
if (!zoneName.isEmpty()) {
deckStateManager->createCustomZone(boardName, zoneName);
}
return zoneName;
}
void DeckEditorDeckDockWidget::refreshShortcuts()
{
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();

View file

@ -8,23 +8,41 @@
#ifndef DECK_EDITOR_DECK_DOCK_WIDGET_H
#define DECK_EDITOR_DECK_DOCK_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../../key_signals.h"
#include "../general/tutorial/tutorial_controller.h"
#include "../utility/custom_line_edit.h"
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include "deck_list_history_manager_widget.h"
#include "deck_list_style_proxy.h"
#include "libcockatrice/card/printing/exact_card.h"
#include <QDockWidget>
#include <QLabel>
#include <QTextEdit>
#include <QModelIndexList>
#include <QString>
#include <QTreeView>
#include <libcockatrice/card/card_info.h>
#include <qtmetamacros.h>
class CommanderBracketWidget;
class DeckListModel;
class AbstractTabDeckEditor;
class DeckListHistoryManagerWidget;
class DeckListStyleProxy;
class DeckPreviewDeckTagsDisplayWidget;
class DeckStateManager;
class LineEditUnfocusable;
class QAction;
class QCheckBox;
class QComboBox;
class QItemSelectionModel;
class QLabel;
class QMenu;
class QModelIndex;
class QPoint;
class QPushButton;
class QTextEdit;
class QTimer;
class SettingsButtonWidget;
class DeckEditorDeckDockWidget : public QDockWidget
{
Q_OBJECT
@ -34,6 +52,8 @@ public:
DeckListStyleProxy *proxy;
QTreeView *deckView;
QComboBox *bannerCardComboBox;
QLabel *playmatLabel;
QPushButton *playmatSettingsButton;
void createDeckDock();
ExactCard getCurrentCard();
void retranslateUi();
@ -99,12 +119,19 @@ private:
[[nodiscard]] QModelIndexList getSelectedCardNodeSourceIndices() const;
void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
void addMoveToZoneMenu(QMenu *menu, const QModelIndex &sourceCardIndex, const QString &currentBoardName);
void addChangeBoardMenu(QMenu *menu, const QString &zoneName);
QString createNewCustomZone(const QString &initialBoardName = {});
void addNewZoneAction(QMenu *menu, const QString &initialBoardName = {});
private slots:
void decklistCustomMenu(QPoint point);
void updateCard(QModelIndex, const QModelIndex &current);
void writeName();
void writeComments();
void writeBannerCard(int);
void openPlaymatSettings();
void updatePlaymatLabel();
void applyActiveGroupCriteria();
void setSelectedIndex(const QModelIndex &newCardIndex, bool preserveWidgetFocus);
void updateHash();

View file

@ -4,10 +4,31 @@
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../filters/filter_builder.h"
#include "../../../filters/filter_tree_model.h"
#include "../../key_signals.h"
#include "../../pixel_map_generator.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include "libcockatrice/filters/filter_card.h"
#include <QAbstractItemModel>
#include <QAction>
#include <QDebug>
#include <QFlags>
#include <QGridLayout>
#include <QItemSelectionModel>
#include <QList>
#include <QMenu>
#include <QModelIndex>
#include <QModelIndexList>
#include <QPersistentModelIndex>
#include <QPoint>
#include <QString>
#include <QStringLiteral>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>
#include <QVariant>
#include <QWidget>
#include <qnamespace.h>
DeckEditorFilterDockWidget::DeckEditorFilterDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent)
@ -42,11 +63,11 @@ void DeckEditorFilterDockWidget::createFiltersDock()
connect(filterBuilder, &FilterBuilder::add, filterModel, &FilterTreeModel::addFilter);
aClearFilterOne = new QAction(QString(), this);
aClearFilterOne->setIcon(QPixmap("theme:icons/decrement"));
aClearFilterOne->setIcon(themePixmap(QStringLiteral("icons/decrement")));
connect(aClearFilterOne, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterOne);
aClearFilterAll = new QAction(QString(), this);
aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch"));
aClearFilterAll->setIcon(themePixmap(QStringLiteral("icons/clearsearch")));
connect(aClearFilterAll, &QAction::triggered, this, &DeckEditorFilterDockWidget::actClearFilterAll);
auto *filterDelOne = new QToolButton();

View file

@ -8,13 +8,18 @@
#ifndef DECK_EDITOR_FILTER_DOCK_WIDGET_H
#define DECK_EDITOR_FILTER_DOCK_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../../key_signals.h"
#include <QDockWidget>
#include <qtmetamacros.h>
class FilterTreeModel;
class AbstractTabDeckEditor;
class QAction;
class QPoint;
class QTreeView;
class QWidget;
class DeckEditorFilterDockWidget : public QDockWidget
{
Q_OBJECT

View file

@ -2,10 +2,15 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../printing_selector/printing_selector.h"
#include "deck_editor_deck_dock_widget.h"
#include "printing_disabled_info_widget.h"
#include <QFlags>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h>
DeckEditorPrintingSelectorDockWidget::DeckEditorPrintingSelectorDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent)

View file

@ -7,13 +7,13 @@
#ifndef DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H
#define DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../printing_selector/printing_selector.h"
#include <QDockWidget>
#include <qtmetamacros.h>
class PrintingDisabledInfoWidget;
class TabDeckEditor;
class AbstractTabDeckEditor;
class PrintingSelector;
class QWidget;
class DeckEditorPrintingSelectorDockWidget : public QDockWidget
{

Some files were not shown because too many files have changed in this diff Show more