Compare commits

...

3 Commits

Author SHA1 Message Date
RickyRister
3ff2df2796 [DeckList] Move metadata into struct (#6380)
* [DeckList] Move metadata into struct

* wipe metadata if preserveMetadata is false
2025-11-30 13:09:09 +01:00
RickyRister
d57bec8ec6 [DeckList] Move decklist node classes into new folder (#6381)
* [DeckList] Move decklist node classes into new folder

* reformat

* fix
2025-11-30 13:05:49 +01:00
BruebachL
2b64e65f45 [CardInfoPictureEnlargedWidget] Fix DPR scaling. (#6382) 2025-11-30 13:03:18 +01:00
21 changed files with 123 additions and 77 deletions

View File

@@ -7,7 +7,7 @@
#include <QRegularExpression>
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)

View File

@@ -7,7 +7,7 @@
#include <QRegularExpression>
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)

View File

@@ -10,7 +10,7 @@
#include <algorithm>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
DeckViewCardDragItem::DeckViewCardDragItem(DeckViewCard *_item,
const QPointF &_hotSpot,

View File

@@ -3,9 +3,10 @@
#include "../../../interface/deck_loader/deck_loader.h"
#include "../player.h"
#include "../player_actions.h"
#include "libcockatrice/deck_list/inner_deck_list_node.h"
#include "player_menu.h"
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
UtilityMenu::UtilityMenu(Player *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player)
{
PlayerActions *playerActions = player->getPlayerActions();

View File

@@ -18,7 +18,7 @@
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
const QStringList DeckLoader::ACCEPTED_FILE_EXTENSIONS = {"*.cod", "*.dec", "*.dek", "*.txt", "*.mwDeck"};

View File

@@ -33,10 +33,13 @@ CardInfoPictureEnlargedWidget::CardInfoPictureEnlargedWidget(QWidget *parent) :
*/
void CardInfoPictureEnlargedWidget::loadPixmap(const QSize &size)
{
// Handle DPI scaling
qreal dpr = devicePixelRatio(); // Get the actual scaling factor
QSize availableSize = size * dpr; // Convert to physical pixel size
if (card) {
CardPictureLoader::getPixmap(enlargedPixmap, card, size);
CardPictureLoader::getPixmap(enlargedPixmap, card, availableSize);
} else {
CardPictureLoader::getCardBackPixmap(enlargedPixmap, size);
CardPictureLoader::getCardBackPixmap(enlargedPixmap, availableSize);
}
pixmapDirty = false;
}
@@ -77,25 +80,35 @@ void CardInfoPictureEnlargedWidget::paintEvent(QPaintEvent *event)
loadPixmap(size());
}
// Scale the size of the pixmap to fit the widget while maintaining the aspect ratio
QSize scaledSize = enlargedPixmap.size().scaled(size().width(), size().height(), Qt::KeepAspectRatio);
qreal dpr = enlargedPixmap.devicePixelRatio();
// Calculate the position to center the scaled pixmap
QPoint topLeft{(width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2};
QSize logicalPixmapSize(enlargedPixmap.width() / dpr, enlargedPixmap.height() / dpr);
// Define the radius for rounded corners
// Adjust the radius as needed for rounded corners
qreal radius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * scaledSize.width() : 0.;
// Scale the pixmap to fit the widget (logical → logical)
QSize scaledLogicalSize = logicalPixmapSize.scaled(size(), Qt::KeepAspectRatio);
// Convert scaled logical size → physical size for scaled()
QSize scaledPhysicalSize = scaledLogicalSize * dpr;
// Pixmap scaled in PHYSICAL pixels
QPixmap finalPixmap = enlargedPixmap.scaled(scaledPhysicalSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
finalPixmap.setDevicePixelRatio(dpr);
// Center inside widget
QPoint topLeft{(width() - scaledLogicalSize.width()) / 2, (height() - scaledLogicalSize.height()) / 2};
// Rounded corner radius based on logical width
qreal radius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * scaledLogicalSize.width() : 0.0;
QStylePainter painter(this);
// Fill the background with transparent color to ensure rounded corners are rendered properly
painter.fillRect(rect(), Qt::transparent); // Use the transparent background
QPainterPath shape;
shape.addRoundedRect(QRect(topLeft, scaledSize), radius, radius);
shape.addRoundedRect(QRect(topLeft, scaledLogicalSize), radius, radius);
painter.setClipPath(shape); // Set the clipping path
// Draw the pixmap scaled to the calculated size
painter.drawItemPixmap(QRect(topLeft, scaledSize), Qt::AlignCenter,
enlargedPixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
painter.drawPixmap(QRect(topLeft, scaledLogicalSize), finalPixmap);
}

View File

@@ -3,13 +3,13 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(HEADERS
libcockatrice/deck_list/abstract_deck_list_card_node.h
libcockatrice/deck_list/abstract_deck_list_node.h
libcockatrice/deck_list/tree/abstract_deck_list_card_node.h
libcockatrice/deck_list/tree/abstract_deck_list_node.h
libcockatrice/deck_list/tree/deck_list_card_node.h
libcockatrice/deck_list/tree/inner_deck_list_node.h
libcockatrice/deck_list/deck_list.h
libcockatrice/deck_list/deck_list_card_node.h
libcockatrice/deck_list/deck_list_history_manager.h
libcockatrice/deck_list/deck_list_memento.h
libcockatrice/deck_list/inner_deck_list_node.h
)
if(Qt6_FOUND)
@@ -21,12 +21,12 @@ endif()
add_library(
libcockatrice_deck_list STATIC
${MOC_SOURCES}
libcockatrice/deck_list/abstract_deck_list_card_node.cpp
libcockatrice/deck_list/abstract_deck_list_node.cpp
libcockatrice/deck_list/tree/abstract_deck_list_card_node.cpp
libcockatrice/deck_list/tree/abstract_deck_list_node.cpp
libcockatrice/deck_list/tree/deck_list_card_node.cpp
libcockatrice/deck_list/tree/inner_deck_list_node.cpp
libcockatrice/deck_list/deck_list.cpp
libcockatrice/deck_list/deck_list_card_node.cpp
libcockatrice/deck_list/deck_list_history_manager.cpp
libcockatrice/deck_list/inner_deck_list_node.cpp
)
add_dependencies(libcockatrice_deck_list libcockatrice_protocol)

View File

@@ -1,9 +1,9 @@
#include "deck_list.h"
#include "abstract_deck_list_node.h"
#include "deck_list_card_node.h"
#include "deck_list_memento.h"
#include "inner_deck_list_node.h"
#include "tree/abstract_deck_list_node.h"
#include "tree/deck_list_card_node.h"
#include "tree/inner_deck_list_node.h"
#include <QCryptographicHash>
#include <QDebug>
@@ -77,6 +77,11 @@ void SideboardPlan::write(QXmlStreamWriter *xml)
xml->writeEndElement();
}
bool DeckList::Metadata::isEmpty() const
{
return name.isEmpty() && comments.isEmpty() && bannerCard.isEmpty() && tags.isEmpty();
}
DeckList::DeckList()
{
root = new InnerDecklistNode;
@@ -122,20 +127,20 @@ bool DeckList::readElement(QXmlStreamReader *xml)
const QString childName = xml->name().toString();
if (xml->isStartElement()) {
if (childName == "lastLoadedTimestamp") {
lastLoadedTimestamp = xml->readElementText();
metadata.lastLoadedTimestamp = xml->readElementText();
} else if (childName == "deckname") {
name = xml->readElementText();
metadata.name = xml->readElementText();
} else if (childName == "comments") {
comments = xml->readElementText();
metadata.comments = xml->readElementText();
} else if (childName == "bannerCard") {
QString providerId = xml->attributes().value("providerId").toString();
QString cardName = xml->readElementText();
bannerCard = {cardName, providerId};
metadata.bannerCard = {cardName, providerId};
} else if (childName == "tags") {
tags.clear(); // Clear existing tags
metadata.tags.clear(); // Clear existing tags
while (xml->readNextStartElement()) {
if (xml->name().toString() == "tag") {
tags.append(xml->readElementText());
metadata.tags.append(xml->readElementText());
}
}
} else if (childName == "zone") {
@@ -155,24 +160,30 @@ bool DeckList::readElement(QXmlStreamReader *xml)
return true;
}
void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata)
{
xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp);
xml->writeTextElement("deckname", metadata.name);
xml->writeStartElement("bannerCard");
xml->writeAttribute("providerId", metadata.bannerCard.providerId);
xml->writeCharacters(metadata.bannerCard.name);
xml->writeEndElement();
xml->writeTextElement("comments", metadata.comments);
// Write tags
xml->writeStartElement("tags");
for (const QString &tag : metadata.tags) {
xml->writeTextElement("tag", tag);
}
xml->writeEndElement();
}
void DeckList::write(QXmlStreamWriter *xml) const
{
xml->writeStartElement("cockatrice_deck");
xml->writeAttribute("version", "1");
xml->writeTextElement("lastLoadedTimestamp", lastLoadedTimestamp);
xml->writeTextElement("deckname", name);
xml->writeStartElement("bannerCard");
xml->writeAttribute("providerId", bannerCard.providerId);
xml->writeCharacters(bannerCard.name);
xml->writeEndElement();
xml->writeTextElement("comments", comments);
// Write tags
xml->writeStartElement("tags");
for (const QString &tag : tags) {
xml->writeTextElement("tag", tag);
}
xml->writeEndElement();
writeMetadata(xml, metadata);
// Write zones
for (int i = 0; i < root->size(); i++) {
@@ -329,7 +340,7 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
const auto &current = inputs.at(index++);
if (!current.contains(reEmpty)) {
match = reComment.match(current);
name = match.captured();
metadata.name = match.captured();
break;
}
}
@@ -337,10 +348,10 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
const auto &current = inputs.at(index++);
if (!current.contains(reEmpty)) {
match = reComment.match(current);
comments += match.captured() + '\n';
metadata.comments += match.captured() + '\n';
}
}
comments.chop(1);
metadata.comments.chop(1);
// Discard empty lines
while (index < max_line && inputs.at(index).contains(reEmpty)) {
@@ -504,9 +515,7 @@ void DeckList::cleanList(bool preserveMetadata)
{
root->clearTree();
if (!preserveMetadata) {
setName();
setComments();
setTags();
metadata = {};
}
refreshDeckHash();
}

View File

@@ -11,7 +11,7 @@
#define DECKLIST_H
#include "deck_list_memento.h"
#include "inner_deck_list_node.h"
#include "tree/inner_deck_list_node.h"
#include <QMap>
#include <QVector>
@@ -126,12 +126,24 @@ public:
class DeckList : public QObject
{
Q_OBJECT
public:
struct Metadata
{
QString name; ///< User-defined deck name.
QString comments; ///< Free-form comments or notes.
CardRef bannerCard; ///< Optional representative card for the deck.
QStringList tags; ///< User-defined tags for deck classification.
QString lastLoadedTimestamp; ///< Timestamp string of last load.
/**
* @brief Checks if all values (except for lastLoadedTimestamp) in the metadata is empty.
*/
bool isEmpty() const;
};
private:
QString name; ///< User-defined deck name.
QString comments; ///< Free-form comments or notes.
CardRef bannerCard; ///< Optional representative card for the deck.
QString lastLoadedTimestamp; ///< Timestamp string of last load.
QStringList tags; ///< User-defined tags for deck classification.
Metadata metadata; ///< Deck metadata that is stored in the deck file
QMap<QString, SideboardPlan *> sideboardPlans; ///< Named sideboard plans.
InnerDecklistNode *root; ///< Root of the deck tree (zones + cards).
@@ -181,34 +193,34 @@ public slots:
///@{
void setName(const QString &_name = QString())
{
name = _name;
metadata.name = _name;
}
void setComments(const QString &_comments = QString())
{
comments = _comments;
metadata.comments = _comments;
}
void setTags(const QStringList &_tags = QStringList())
{
tags = _tags;
metadata.tags = _tags;
emit deckTagsChanged();
}
void addTag(const QString &_tag)
{
tags.append(_tag);
metadata.tags.append(_tag);
emit deckTagsChanged();
}
void clearTags()
{
tags.clear();
metadata.tags.clear();
emit deckTagsChanged();
}
void setBannerCard(const CardRef &_bannerCard = {})
{
bannerCard = _bannerCard;
metadata.bannerCard = _bannerCard;
}
void setLastLoadedTimestamp(const QString &_lastLoadedTimestamp = QString())
{
lastLoadedTimestamp = _lastLoadedTimestamp;
metadata.lastLoadedTimestamp = _lastLoadedTimestamp;
}
///@}
@@ -223,32 +235,38 @@ public:
~DeckList() override;
/// @name Metadata getters
/// The individual metadata getters still exist for backwards compatibility.
/// TODO: Figure out when we can remove them.
///@{
const Metadata &getMetadata() const
{
return metadata;
}
QString getName() const
{
return name;
return metadata.name;
}
QString getComments() const
{
return comments;
return metadata.comments;
}
QStringList getTags() const
{
return tags;
return metadata.tags;
}
CardRef getBannerCard() const
{
return bannerCard;
return metadata.bannerCard;
}
QString getLastLoadedTimestamp() const
{
return lastLoadedTimestamp;
return metadata.lastLoadedTimestamp;
}
///@}
bool isBlankDeck() const
{
return name.isEmpty() && comments.isEmpty() && getCardList().isEmpty();
return metadata.isEmpty() && getCardList().isEmpty();
}
/// @name Sideboard plans
@@ -286,7 +304,7 @@ public:
void cleanList(bool preserveMetadata = false);
bool isEmpty() const
{
return root->isEmpty() && name.isEmpty() && comments.isEmpty() && sideboardPlans.isEmpty();
return root->isEmpty() && metadata.isEmpty() && sideboardPlans.isEmpty();
}
QStringList getCardList() const;
QList<CardRef> getCardRefList() const;

View File

@@ -1,12 +1,12 @@
#ifndef DECKLISTMODEL_H
#define DECKLISTMODEL_H
#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h>
#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <QAbstractItemModel>
#include <QList>
#include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/deck_list/abstract_deck_list_card_node.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/deck_list_card_node.h>
class CardDatabase;
class QPrinter;

View File

@@ -14,7 +14,7 @@
#include <QRegularExpression>
#include <algorithm>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
#include <libcockatrice/protocol/pb/command_concede.pb.h>

View File

@@ -19,6 +19,11 @@ struct CardRef
{
return name == other.name && providerId == other.providerId;
}
bool isEmpty() const
{
return name.isEmpty() && providerId.isEmpty();
}
};
#endif // CARD_REF_H

View File

@@ -1,7 +1,7 @@
#include "clipboard_testing.h"
#include <QTextStream>
#include <libcockatrice/deck_list/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
void testEmpty(const QString &clipboard)
{