mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-18 16:32:16 -07:00
Introduce a tagging system.
This commit is contained in:
parent
7d76221e39
commit
7b3ded013e
22 changed files with 760 additions and 20 deletions
|
|
@ -161,12 +161,19 @@ set(cockatrice_SOURCES
|
|||
src/game/zones/view_zone_widget.cpp
|
||||
src/game/zones/view_zone.cpp
|
||||
src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
|
||||
src/client/ui/widgets/cards/deck_preview_card_picture_widget.cpp src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_widget.cpp
|
||||
src/client/ui/widgets/cards/deck_preview_card_picture_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
|
||||
${VERSION_STRING_CPP}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@ void FlowWidget::addWidget(QWidget *widget_to_add) const
|
|||
flowLayout->addWidget(widget_to_add);
|
||||
}
|
||||
|
||||
void FlowWidget::removeWidget(QWidget *widget_to_remove) const
|
||||
{
|
||||
flowLayout->removeWidget(widget_to_remove);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears all widgets from the flow layout.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class FlowWidget final : public QWidget
|
|||
public:
|
||||
FlowWidget(QWidget *parent, Qt::ScrollBarPolicy horizontalPolicy, Qt::ScrollBarPolicy verticalPolicy);
|
||||
void addWidget(QWidget *widget_to_add) const;
|
||||
void removeWidget(QWidget *widget_to_remove) const;
|
||||
void clearLayout();
|
||||
[[nodiscard]] int count() const;
|
||||
[[nodiscard]] QLayoutItem *itemAt(int index) const;
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ void DeckPreviewColorCircleWidget::resizeEvent(QResizeEvent *event)
|
|||
// Get the parent of the DeckPreviewColorIdentityWidget
|
||||
QWidget *identityParent = parentWidget() ? parentWidget()->parentWidget() : nullptr;
|
||||
if (identityParent) {
|
||||
// Calculate the circle diameter as 10% of the parent's height
|
||||
int maxSize = identityParent->height() * 0.1;
|
||||
// Calculate the circle diameter as 15% of the parent's height
|
||||
int maxSize = identityParent->width() * 0.15;
|
||||
circleDiameter = maxSize;
|
||||
|
||||
// Update the widget size based on the diameter
|
||||
|
|
@ -124,6 +124,8 @@ DeckPreviewColorIdentityWidget::DeckPreviewColorIdentityWidget(const QString &co
|
|||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
// Define the full WUBRG set (White, Blue, Black, Red, Green)
|
||||
QString fullColorIdentity = "WUBRG";
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include "../../general/layout_containers/flow_widget.h"
|
||||
#include "deck_preview_tag_addition_widget.h"
|
||||
#include "deck_preview_tag_display_widget.h"
|
||||
#include "deck_preview_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(DeckPreviewWidget *_parent, DeckLoader *_deckLoader)
|
||||
: QWidget(_parent), parent(_parent), deckLoader(_deckLoader)
|
||||
{
|
||||
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
layout->setSpacing(5);
|
||||
|
||||
setFixedHeight(100);
|
||||
|
||||
auto *flowWidget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
||||
for (const QString &tag : this->deckLoader->getTags()) {
|
||||
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
|
||||
}
|
||||
flowWidget->addWidget(new DeckPreviewTagAdditionWidget(this, "Add tags ..."));
|
||||
layout->addWidget(flowWidget);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
|
||||
#define DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../../deck/deck_loader.h"
|
||||
#include "deck_preview_widget.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class DeckPreviewWidget;
|
||||
class DeckPreviewDeckTagsDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeckPreviewDeckTagsDisplayWidget(DeckPreviewWidget *_parent, DeckLoader *_deckLoader);
|
||||
DeckPreviewWidget *parent;
|
||||
DeckLoader *deckLoader;
|
||||
};
|
||||
#endif // DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#include "deck_preview_tag_addition_widget.h"
|
||||
|
||||
#include "deck_preview_tag_dialog.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
|
||||
DeckPreviewTagAdditionWidget::DeckPreviewTagAdditionWidget(DeckPreviewDeckTagsDisplayWidget *_parent,
|
||||
const QString &tagName)
|
||||
: QWidget(_parent), parent(_parent), tagName_(tagName)
|
||||
{
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
layout->setSpacing(5);
|
||||
|
||||
// Create label for the tag name
|
||||
tagLabel_ = new QLabel(tagName_, this);
|
||||
layout->addWidget(tagLabel_);
|
||||
|
||||
// Create close button
|
||||
closeButton_ = new QPushButton("x", this);
|
||||
closeButton_->setFixedSize(16, 16); // Small square button
|
||||
layout->addWidget(closeButton_);
|
||||
|
||||
// Adjust widget size
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
|
||||
// Connect close button to the remove signal
|
||||
connect(closeButton_, &QPushButton::clicked, this, &DeckPreviewTagAdditionWidget::tagClosed);
|
||||
}
|
||||
|
||||
void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
emit tagClicked();
|
||||
}
|
||||
QWidget::mousePressEvent(event);
|
||||
QStringList knownTags = parent->parent->parent->gatherAllTagsFromFlowWidget();
|
||||
QStringList activeTags = parent->deckLoader->getTags();
|
||||
|
||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QStringList updatedTags = dialog.getActiveTags();
|
||||
qDebug() << "Updated active tags:" << updatedTags;
|
||||
parent->deckLoader->setTags(updatedTags);
|
||||
parent->deckLoader->saveToFile(parent->parent->filePath, DeckLoader::CockatriceFormat);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef DECK_PREVIEW_TAG_ADDITION_WIDGET_H
|
||||
#define DECK_PREVIEW_TAG_ADDITION_WIDGET_H
|
||||
|
||||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class DeckPreviewTagAdditionWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeckPreviewTagAdditionWidget(DeckPreviewDeckTagsDisplayWidget *_parent, const QString &tagName);
|
||||
|
||||
signals:
|
||||
void tagClicked(); // Emitted when the tag is clicked
|
||||
void tagClosed(); // Emitted when the close button is clicked
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
DeckPreviewDeckTagsDisplayWidget *parent;
|
||||
QString tagName_;
|
||||
QLabel *tagLabel_;
|
||||
QPushButton *closeButton_;
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_TAG_ADDITION_WIDGET_H
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
#include "deck_preview_tag_dialog.h"
|
||||
|
||||
#include "deck_preview_tag_item_widget.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const QStringList &activeTags, QWidget *parent)
|
||||
: QDialog(parent), activeTags_(activeTags)
|
||||
{
|
||||
setWindowTitle("Deck Tags Manager");
|
||||
resize(400, 500);
|
||||
|
||||
QStringList defaultTags = {"🏃️Aggro", "🧙 Control️", "⚔️ Midrange", "🌀 Combo",
|
||||
"🪓 Mill", "🐉 Tribal", "🔒 Stax"};
|
||||
|
||||
// Merge knownTags with defaultTags, ensuring no duplicates
|
||||
QStringList combinedTags = knownTags + defaultTags + activeTags;
|
||||
combinedTags.removeDuplicates();
|
||||
|
||||
// Main layout
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
|
||||
// Instruction label
|
||||
auto *instructionLabel =
|
||||
new QLabel("Manage your deck tags. Check or uncheck tags as needed, or add new ones:", this);
|
||||
instructionLabel->setWordWrap(true);
|
||||
mainLayout->addWidget(instructionLabel);
|
||||
|
||||
// Tag list view
|
||||
tagListView_ = new QListWidget(this);
|
||||
mainLayout->addWidget(tagListView_);
|
||||
|
||||
// Populate combined tags
|
||||
for (const auto &tag : combinedTags) {
|
||||
auto *item = new QListWidgetItem(tagListView_);
|
||||
auto *tagWidget = new DeckPreviewTagItemWidget(tag, activeTags.contains(tag), this);
|
||||
tagListView_->addItem(item);
|
||||
tagListView_->setItemWidget(item, tagWidget);
|
||||
|
||||
connect(tagWidget->checkBox(), &QCheckBox::checkStateChanged, this,
|
||||
&DeckPreviewTagDialog::onCheckboxStateChanged);
|
||||
}
|
||||
|
||||
// Add tag input layout
|
||||
auto *addTagLayout = new QHBoxLayout();
|
||||
newTagInput_ = new QLineEdit(this);
|
||||
newTagInput_->setPlaceholderText("Add a new tag (e.g., Aggro️)");
|
||||
addTagButton_ = new QPushButton("Add Tag", this);
|
||||
addTagLayout->addWidget(newTagInput_);
|
||||
addTagLayout->addWidget(addTagButton_);
|
||||
mainLayout->addLayout(addTagLayout);
|
||||
|
||||
connect(addTagButton_, &QPushButton::clicked, this, &DeckPreviewTagDialog::addTag);
|
||||
|
||||
// OK and Cancel buttons
|
||||
auto *buttonLayout = new QHBoxLayout();
|
||||
auto *okButton = new QPushButton("OK", this);
|
||||
auto *cancelButton = new QPushButton("Cancel", this);
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(okButton);
|
||||
buttonLayout->addWidget(cancelButton);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
|
||||
connect(okButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::accept);
|
||||
connect(cancelButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::reject);
|
||||
}
|
||||
|
||||
QStringList DeckPreviewTagDialog::getActiveTags() const
|
||||
{
|
||||
return activeTags_;
|
||||
}
|
||||
|
||||
void DeckPreviewTagDialog::addTag()
|
||||
{
|
||||
QString newTag = newTagInput_->text().trimmed();
|
||||
if (newTag.isEmpty()) {
|
||||
QMessageBox::warning(this, "Invalid Input", "Tag name cannot be empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent duplicate tags
|
||||
for (int i = 0; i < tagListView_->count(); ++i) {
|
||||
auto *item = tagListView_->item(i);
|
||||
auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView_->itemWidget(item));
|
||||
if (tagWidget && tagWidget->checkBox()->text() == newTag) {
|
||||
QMessageBox::warning(this, "Duplicate Tag", "This tag already exists.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new tag
|
||||
auto *item = new QListWidgetItem(tagListView_);
|
||||
auto *tagWidget = new DeckPreviewTagItemWidget(newTag, true, this);
|
||||
tagListView_->addItem(item);
|
||||
tagListView_->setItemWidget(item, tagWidget);
|
||||
activeTags_.append(newTag);
|
||||
|
||||
connect(tagWidget->checkBox(), &QCheckBox::checkStateChanged, this, &DeckPreviewTagDialog::onCheckboxStateChanged);
|
||||
|
||||
// Clear the input field
|
||||
newTagInput_->clear();
|
||||
}
|
||||
|
||||
void DeckPreviewTagDialog::onCheckboxStateChanged()
|
||||
{
|
||||
activeTags_.clear();
|
||||
for (int i = 0; i < tagListView_->count(); ++i) {
|
||||
auto *item = tagListView_->item(i);
|
||||
auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView_->itemWidget(item));
|
||||
if (tagWidget && tagWidget->checkBox()->isChecked()) {
|
||||
activeTags_.append(tagWidget->checkBox()->text());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef DECK_PREVIEW_TAG_DIALOG_H
|
||||
#define DECK_PREVIEW_TAG_DIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
|
||||
class DeckPreviewTagDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeckPreviewTagDialog(const QStringList &knownTags,
|
||||
const QStringList &activeTags,
|
||||
QWidget *parent = nullptr);
|
||||
QStringList getActiveTags() const;
|
||||
|
||||
private slots:
|
||||
void addTag();
|
||||
void onCheckboxStateChanged();
|
||||
|
||||
private:
|
||||
QListWidget *tagListView_;
|
||||
QLineEdit *newTagInput_;
|
||||
QPushButton *addTagButton_;
|
||||
QStringList activeTags_;
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_TAG_DIALOG_H
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
#include "deck_preview_tag_display_widget.h"
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
DeckPreviewTagDisplayWidget::DeckPreviewTagDisplayWidget(QWidget *parent, const QString &_tagName)
|
||||
: QWidget(parent), tagName(_tagName), isSelected(false)
|
||||
{
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
layout->setSpacing(5);
|
||||
|
||||
// Add a stretch spacer for text and close button separation
|
||||
layout->addStretch(); // Ensures the close button stays at the far-right side
|
||||
|
||||
// Create close button
|
||||
closeButton = new QPushButton("x", this);
|
||||
closeButton->setFixedSize(16, 16); // Small square button
|
||||
closeButton->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
// Set font for close button to ensure the "x" appears correctly
|
||||
QFont closeButtonFont = closeButton->font();
|
||||
closeButtonFont.setPointSize(10); // Adjust the size to make the "x" clear
|
||||
closeButton->setFont(closeButtonFont);
|
||||
|
||||
layout->addWidget(closeButton);
|
||||
|
||||
// Adjust widget size
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
|
||||
// Connect close button to the remove signal
|
||||
connect(closeButton, &QPushButton::clicked, this, &DeckPreviewTagDisplayWidget::tagClosed);
|
||||
}
|
||||
|
||||
QSize DeckPreviewTagDisplayWidget::sizeHint() const
|
||||
{
|
||||
// Calculate the size based on the tag name and close button
|
||||
QFontMetrics fm(font());
|
||||
int textWidth = fm.horizontalAdvance(tagName);
|
||||
int width = textWidth + closeButton->width() + 50; // Add extra padding
|
||||
int height = fm.height() + 10; // Height based on font size + padding
|
||||
|
||||
return QSize(width, height);
|
||||
}
|
||||
|
||||
void DeckPreviewTagDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
setSelected(!isSelected);
|
||||
emit tagClicked();
|
||||
}
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DeckPreviewTagDisplayWidget::setSelected(bool selected)
|
||||
{
|
||||
isSelected = selected;
|
||||
update(); // Trigger a repaint
|
||||
}
|
||||
|
||||
void DeckPreviewTagDisplayWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
// Set background color
|
||||
QColor backgroundColor = isSelected ? QColor(173, 216, 230) : Qt::white;
|
||||
painter.setBrush(backgroundColor);
|
||||
painter.setPen(Qt::NoPen);
|
||||
|
||||
// Draw background
|
||||
painter.drawRect(rect());
|
||||
|
||||
// Draw border
|
||||
QColor borderColor = isSelected ? Qt::blue : Qt::gray;
|
||||
QPen borderPen(borderColor, isSelected ? 2 : 1);
|
||||
painter.setPen(borderPen);
|
||||
painter.drawRect(rect().adjusted(0, 0, -1, -1)); // Adjust for pen width
|
||||
|
||||
// Calculate font size based on widget height
|
||||
QFont font = painter.font();
|
||||
int fontSize = std::max(10, height() / 2); // Ensure a minimum font size of 10
|
||||
font.setPointSize(fontSize);
|
||||
painter.setFont(font);
|
||||
|
||||
// Calculate text rect to avoid overlap with the close button
|
||||
int closeButtonWidth = closeButton->width();
|
||||
int margin = 10; // Left and right margins
|
||||
QRect textRect(margin, 0, width() - closeButtonWidth - margin * 2, height());
|
||||
|
||||
// Draw the text with a black border for better legibility
|
||||
painter.setPen(Qt::black);
|
||||
|
||||
// Draw text border by offsetting
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
if (dx != 0 || dy != 0) {
|
||||
painter.drawText(textRect.translated(dx, dy), Qt::AlignLeft | Qt::AlignVCenter, tagName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the actual text
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, tagName);
|
||||
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef DECK_PREVIEW_TAG_DISPLAY_WIDGET_H
|
||||
#define DECK_PREVIEW_TAG_DISPLAY_WIDGET_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class DeckPreviewTagDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for DeckPreviewTagDisplayWidget.
|
||||
* @param parent The parent widget.
|
||||
* @param tagName The name of the tag to display.
|
||||
*/
|
||||
explicit DeckPreviewTagDisplayWidget(QWidget *parent = nullptr, const QString &tagName = "");
|
||||
QSize sizeHint() const override;
|
||||
QString getTagName() const
|
||||
{
|
||||
return tagName;
|
||||
}
|
||||
bool getSelected() const
|
||||
{
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the selected state of the tag.
|
||||
* @param selected True if the tag is selected, false otherwise.
|
||||
*/
|
||||
void setSelected(bool selected);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted when the tag is clicked.
|
||||
*/
|
||||
void tagClicked();
|
||||
|
||||
/**
|
||||
* @brief Emitted when the close button is clicked.
|
||||
*/
|
||||
void tagClosed();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Custom paint event for drawing the widget.
|
||||
* @param event The paint event.
|
||||
*/
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
/**
|
||||
* @brief Custom mouse press event handler.
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
QLabel *tagLabel; ///< Label for displaying the tag name.
|
||||
QPushButton *closeButton; ///< Button to close/remove the tag.
|
||||
QString tagName; ///< The name of the tag.
|
||||
bool isSelected; ///< Indicates whether the tag is selected.
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_TAG_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include "deck_preview_tag_item_widget.h"
|
||||
|
||||
DeckPreviewTagItemWidget::DeckPreviewTagItemWidget(const QString &tagName, bool isChecked, QWidget *parent)
|
||||
: QWidget(parent), checkBox_(new QCheckBox(this))
|
||||
{
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(5);
|
||||
|
||||
checkBox_->setText(tagName); // Set the tag name as the checkbox label
|
||||
checkBox_->setChecked(isChecked); // Set the initial state of the checkbox
|
||||
|
||||
layout->addWidget(checkBox_); // Add the checkbox to the layout
|
||||
setLayout(layout); // Set the layout of this widget
|
||||
}
|
||||
|
||||
QCheckBox *DeckPreviewTagItemWidget::checkBox() const
|
||||
{
|
||||
return checkBox_; // Return the checkbox widget
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef DECK_PREVIEW_TAG_ITEM_WIDGET_H
|
||||
#define DECK_PREVIEW_TAG_ITEM_WIDGET_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class DeckPreviewTagItemWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Constructor: Initializes the tag item widget with a tag name and initial checkbox state
|
||||
DeckPreviewTagItemWidget(const QString &tagName, bool isChecked, QWidget *parent = nullptr);
|
||||
|
||||
// Accessor for the checkbox widget
|
||||
QCheckBox *checkBox() const;
|
||||
|
||||
private:
|
||||
QCheckBox *checkBox_; // Checkbox to represent the tag's state
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_TAG_ITEM_WIDGET_H
|
||||
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
#include "../../../../../game/cards/card_database_manager.h"
|
||||
#include "../../cards/deck_preview_card_picture_widget.h"
|
||||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QMouseEvent>
|
||||
#include <QSet>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
DeckPreviewWidget::DeckPreviewWidget(QWidget *parent, const QString &_filePath) : QWidget(parent), filePath(_filePath)
|
||||
DeckPreviewWidget::DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QString &_filePath)
|
||||
: QWidget(_parent), parent(_parent), filePath(_filePath)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
|
@ -35,9 +37,11 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *parent, const QString &_filePath)
|
|||
setFilePath(deckLoader->getLastFileName());
|
||||
|
||||
colorIdentityWidget = new DeckPreviewColorIdentityWidget(getColorIdentity());
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader);
|
||||
|
||||
layout->addWidget(bannerCardDisplayWidget);
|
||||
layout->addWidget(colorIdentityWidget);
|
||||
layout->addWidget(deckTagsDisplayWidget);
|
||||
}
|
||||
|
||||
QString DeckPreviewWidget::getColorIdentity()
|
||||
|
|
|
|||
|
|
@ -3,23 +3,29 @@
|
|||
|
||||
#include "../../../../../deck/deck_loader.h"
|
||||
#include "../../cards/deck_preview_card_picture_widget.h"
|
||||
#include "../visual_deck_storage_widget.h"
|
||||
#include "deck_preview_color_identity_widget.h"
|
||||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDeckStorageWidget;
|
||||
class DeckPreviewDeckTagsDisplayWidget;
|
||||
class DeckPreviewWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DeckPreviewWidget(QWidget *parent, const QString &_filePath);
|
||||
explicit DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QString &_filePath);
|
||||
QString getColorIdentity();
|
||||
|
||||
VisualDeckStorageWidget *parent;
|
||||
QVBoxLayout *layout;
|
||||
QString filePath;
|
||||
DeckLoader *deckLoader;
|
||||
DeckPreviewCardPictureWidget *bannerCardDisplayWidget;
|
||||
DeckPreviewColorIdentityWidget *colorIdentityWidget;
|
||||
DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget;
|
||||
|
||||
signals:
|
||||
void deckPreviewClicked(QMouseEvent *event, DeckPreviewWidget *instance);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
#include "visual_deck_storage_tag_filter_widget.h"
|
||||
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
#include "deck_preview/deck_preview_tag_addition_widget.h"
|
||||
#include "deck_preview/deck_preview_tag_display_widget.h"
|
||||
#include "deck_preview/deck_preview_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
VisualDeckStorageTagFilterWidget::VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent)
|
||||
: QWidget(_parent), parent(_parent)
|
||||
{
|
||||
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
layout->setSpacing(5);
|
||||
|
||||
setFixedHeight(100);
|
||||
|
||||
auto *flowWidget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
||||
layout->addWidget(flowWidget);
|
||||
}
|
||||
|
||||
QList<DeckPreviewWidget *>
|
||||
VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const
|
||||
{
|
||||
// Collect selected tags from DeckPreviewTagDisplayWidget
|
||||
QStringList selectedTags;
|
||||
foreach (DeckPreviewTagDisplayWidget *tagWidget, findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||
if (tagWidget->getSelected()) {
|
||||
selectedTags.append(tagWidget->getTagName());
|
||||
}
|
||||
}
|
||||
|
||||
// If no tags are selected, return all decks
|
||||
if (selectedTags.isEmpty()) {
|
||||
return deckPreviews;
|
||||
}
|
||||
|
||||
// Filter DeckPreviewWidgets that contain all of the selected tags
|
||||
QList<DeckPreviewWidget *> filteredDecks;
|
||||
for (DeckPreviewWidget *deckPreview : deckPreviews) {
|
||||
QStringList deckTags = deckPreview->deckLoader->getTags();
|
||||
|
||||
// Check if all selectedTags are in deckTags
|
||||
bool allTagsPresent = std::all_of(selectedTags.begin(), selectedTags.end(),
|
||||
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
|
||||
|
||||
if (allTagsPresent) {
|
||||
filteredDecks.append(deckPreview);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredDecks;
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QStringList &tags)
|
||||
{
|
||||
// Iterate through all DeckPreviewTagDisplayWidgets
|
||||
foreach (DeckPreviewTagDisplayWidget *tagWidget, findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||
// If the tag is not in the provided tags list, remove the widget
|
||||
if (!tags.contains(tagWidget->getTagName())) {
|
||||
auto *flowWidget = findChild<FlowWidget *>();
|
||||
flowWidget->removeWidget(tagWidget);
|
||||
tagWidget->deleteLater(); // Safely delete the widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::addTagsIfNotPresent(const QStringList &tags)
|
||||
{
|
||||
for (const QString &tag : tags) {
|
||||
addTagIfNotPresent(tag);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::addTagIfNotPresent(const QString &tag)
|
||||
{
|
||||
// Check if the tag already exists in the flow widget
|
||||
bool tagExists = false;
|
||||
foreach (DeckPreviewTagDisplayWidget *tagWidget, findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||
if (tagWidget->getTagName() == tag) {
|
||||
tagExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the tag doesn't exist, add a new DeckPreviewTagDisplayWidget
|
||||
if (!tagExists) {
|
||||
auto *newTagWidget = new DeckPreviewTagDisplayWidget(this, tag);
|
||||
connect(newTagWidget, &DeckPreviewTagDisplayWidget::tagClicked, parent,
|
||||
&VisualDeckStorageWidget::refreshBannerCards);
|
||||
auto *flowWidget = findChild<FlowWidget *>();
|
||||
flowWidget->addWidget(newTagWidget);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef VISUAL_DECK_STORAGE_TAG_FILTER_WIDGET_H
|
||||
#define VISUAL_DECK_STORAGE_TAG_FILTER_WIDGET_H
|
||||
|
||||
#include "visual_deck_storage_widget.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDeckStorageWidget;
|
||||
class VisualDeckStorageTagFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent);
|
||||
void refreshTags();
|
||||
QList<DeckPreviewWidget *> filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const;
|
||||
void removeTagsNotInList(const QStringList &tags);
|
||||
void addTagsIfNotPresent(const QStringList &tags);
|
||||
void addTagIfNotPresent(const QString &tag);
|
||||
VisualDeckStorageWidget *parent;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_TAG_FILTER_WIDGET_H
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include "deck_preview/deck_preview_widget.h"
|
||||
#include "visual_deck_storage_search_widget.h"
|
||||
#include "visual_deck_storage_sort_widget.h"
|
||||
#include "visual_deck_storage_tag_filter_widget.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDirIterator>
|
||||
|
|
@ -24,11 +25,13 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
|
|||
|
||||
sortWidget = new VisualDeckStorageSortWidget(this);
|
||||
searchWidget = new VisualDeckStorageSearchWidget(this);
|
||||
tagFilterWidget = new VisualDeckStorageTagFilterWidget(this);
|
||||
deckPreviewColorIdentityFilterWidget = new DeckPreviewColorIdentityFilterWidget(this);
|
||||
|
||||
searchAndSortLayout->addWidget(sortWidget);
|
||||
searchAndSortLayout->addWidget(searchWidget);
|
||||
layout->addLayout(searchAndSortLayout);
|
||||
layout->addWidget(tagFilterWidget);
|
||||
layout->addWidget(deckPreviewColorIdentityFilterWidget);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
|
@ -73,7 +76,7 @@ void VisualDeckStorageWidget::refreshBannerCards()
|
|||
}
|
||||
|
||||
foreach (const QString &file, allFiles) {
|
||||
auto *display = new DeckPreviewWidget(flowWidget, file);
|
||||
auto *display = new DeckPreviewWidget(this, file);
|
||||
|
||||
connect(display, &DeckPreviewWidget::deckPreviewClicked, this,
|
||||
&VisualDeckStorageWidget::deckPreviewClickedEvent);
|
||||
|
|
@ -87,11 +90,56 @@ void VisualDeckStorageWidget::refreshBannerCards()
|
|||
|
||||
auto filteredByColorIdentity =
|
||||
deckPreviewColorIdentityFilterWidget->filterWidgets(sortWidget->filterFiles(allDecks));
|
||||
auto filteredFiles = searchWidget->filterFiles(filteredByColorIdentity, searchWidget->getSearchText());
|
||||
auto filteredByTags = tagFilterWidget->filterDecksBySelectedTags(filteredByColorIdentity);
|
||||
auto filteredFiles = searchWidget->filterFiles(filteredByTags, searchWidget->getSearchText());
|
||||
|
||||
tagFilterWidget->removeTagsNotInList(gatherAllTags(filteredFiles));
|
||||
tagFilterWidget->addTagsIfNotPresent(gatherAllTags(filteredFiles));
|
||||
|
||||
flowWidget->clearLayout(); // Clear existing widgets in the flow layout
|
||||
|
||||
foreach (DeckPreviewWidget *deck, filteredFiles) {
|
||||
flowWidget->addWidget(deck);
|
||||
}
|
||||
|
||||
emit bannerCardsRefreshed();
|
||||
}
|
||||
|
||||
QStringList VisualDeckStorageWidget::gatherAllTagsFromFlowWidget() const
|
||||
{
|
||||
QStringList allTags;
|
||||
|
||||
if (flowWidget) {
|
||||
// Iterate through all DeckPreviewWidgets
|
||||
foreach (DeckPreviewWidget *display, flowWidget->findChildren<DeckPreviewWidget *>()) {
|
||||
// Get tags from each DeckPreviewWidget
|
||||
QStringList tags = display->deckLoader->getTags();
|
||||
|
||||
// Add tags to the list while avoiding duplicates
|
||||
allTags.append(tags);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates by calling 'removeDuplicates'
|
||||
allTags.removeDuplicates();
|
||||
|
||||
return allTags;
|
||||
}
|
||||
|
||||
QStringList VisualDeckStorageWidget::gatherAllTags(const QList<DeckPreviewWidget *> &allDecks)
|
||||
{
|
||||
QStringList allTags;
|
||||
|
||||
// Iterate through all decks provided as input
|
||||
for (DeckPreviewWidget *deck : allDecks) {
|
||||
QStringList tags = deck->deckLoader->getTags();
|
||||
|
||||
// Add tags to the list while avoiding duplicates
|
||||
allTags.append(tags);
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
allTags.removeDuplicates();
|
||||
|
||||
return allTags;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@
|
|||
#include "deck_preview/deck_preview_widget.h"
|
||||
#include "visual_deck_storage_search_widget.h"
|
||||
#include "visual_deck_storage_sort_widget.h"
|
||||
#include "visual_deck_storage_tag_filter_widget.h"
|
||||
|
||||
#include <QFileSystemModel>
|
||||
|
||||
class VisualDeckStorageSearchWidget;
|
||||
class VisualDeckStorageSortWidget;
|
||||
class VisualDeckStorageTagFilterWidget;
|
||||
class DeckPreviewColorIdentityFilterWidget;
|
||||
class VisualDeckStorageWidget final : public QWidget
|
||||
{
|
||||
|
|
@ -26,10 +28,13 @@ public slots:
|
|||
void deckPreviewClickedEvent(QMouseEvent *event, DeckPreviewWidget *instance);
|
||||
void deckPreviewDoubleClickedEvent(QMouseEvent *event, DeckPreviewWidget *instance);
|
||||
void refreshBannerCards(); // Refresh the display of cards based on the current sorting option
|
||||
QStringList gatherAllTagsFromFlowWidget() const;
|
||||
QStringList gatherAllTags(const QList<DeckPreviewWidget *> &allDecks);
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void updateSortOrder();
|
||||
|
||||
signals:
|
||||
void bannerCardsRefreshed();
|
||||
void deckPreviewClicked(QMouseEvent *event, DeckPreviewWidget *instance);
|
||||
void deckPreviewDoubleClicked(QMouseEvent *event, DeckPreviewWidget *instance);
|
||||
|
||||
|
|
@ -42,6 +47,7 @@ private:
|
|||
|
||||
VisualDeckStorageSortWidget *sortWidget;
|
||||
VisualDeckStorageSearchWidget *searchWidget;
|
||||
VisualDeckStorageTagFilterWidget *tagFilterWidget;
|
||||
DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget;
|
||||
CardSizeWidget *cardSizeWidget;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -422,26 +422,35 @@ bool DeckList::readElement(QXmlStreamReader *xml)
|
|||
if (xml->isStartElement()) {
|
||||
if (childName == "lastLoadedTimestamp") {
|
||||
lastLoadedTimestamp = xml->readElementText();
|
||||
} else if (childName == "deckname")
|
||||
} else if (childName == "deckname") {
|
||||
name = xml->readElementText();
|
||||
else if (childName == "comments")
|
||||
} else if (childName == "comments") {
|
||||
comments = xml->readElementText();
|
||||
else if (childName == "bannerCard") {
|
||||
} else if (childName == "bannerCard") {
|
||||
QString providerId = xml->attributes().value("providerId").toString();
|
||||
QString cardName = xml->readElementText();
|
||||
bannerCard = QPair<QString, QString>(cardName, providerId);
|
||||
} else if (childName == "tags") {
|
||||
tags.clear(); // Clear existing tags
|
||||
while (!(xml->isEndElement() && xml->name() == "tags")) {
|
||||
if (xml->readNextStartElement() && xml->name() == "tag") {
|
||||
tags.append(xml->readElementText());
|
||||
}
|
||||
}
|
||||
} else if (childName == "zone") {
|
||||
InnerDecklistNode *newZone = getZoneObjFromName(xml->attributes().value("name").toString());
|
||||
newZone->readElement(xml);
|
||||
} else if (childName == "sideboard_plan") {
|
||||
SideboardPlan *newSideboardPlan = new SideboardPlan;
|
||||
if (newSideboardPlan->readElement(xml))
|
||||
if (newSideboardPlan->readElement(xml)) {
|
||||
sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan);
|
||||
else
|
||||
} else {
|
||||
delete newSideboardPlan;
|
||||
}
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "cockatrice_deck"))
|
||||
} else if (xml->isEndElement() && (childName == "cockatrice_deck")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -457,14 +466,25 @@ void DeckList::write(QXmlStreamWriter *xml)
|
|||
xml->writeEndElement();
|
||||
xml->writeTextElement("comments", comments);
|
||||
|
||||
for (int i = 0; i < root->size(); i++)
|
||||
root->at(i)->writeElement(xml);
|
||||
|
||||
QMapIterator<QString, SideboardPlan *> i(sideboardPlans);
|
||||
while (i.hasNext())
|
||||
i.next().value()->write(xml);
|
||||
|
||||
// Write tags
|
||||
xml->writeStartElement("tags");
|
||||
for (const QString &tag : tags) {
|
||||
xml->writeTextElement("tag", tag);
|
||||
}
|
||||
xml->writeEndElement();
|
||||
|
||||
// Write zones
|
||||
for (int i = 0; i < root->size(); i++) {
|
||||
root->at(i)->writeElement(xml);
|
||||
}
|
||||
|
||||
// Write sideboard plans
|
||||
QMapIterator<QString, SideboardPlan *> i(sideboardPlans);
|
||||
while (i.hasNext()) {
|
||||
i.next().value()->write(xml);
|
||||
}
|
||||
|
||||
xml->writeEndElement(); // Close "cockatrice_deck"
|
||||
}
|
||||
|
||||
bool DeckList::loadFromXml(QXmlStreamReader *xml)
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ private:
|
|||
QPair<QString, QString> bannerCard;
|
||||
QString deckHash;
|
||||
QString lastLoadedTimestamp;
|
||||
QStringList tags;
|
||||
QMap<QString, SideboardPlan *> sideboardPlans;
|
||||
InnerDecklistNode *root;
|
||||
void getCardListHelper(InnerDecklistNode *node, QSet<QString> &result) const;
|
||||
|
|
@ -281,6 +282,18 @@ public slots:
|
|||
{
|
||||
comments = _comments;
|
||||
}
|
||||
void setTags(const QStringList &_tags = QStringList())
|
||||
{
|
||||
tags = _tags;
|
||||
}
|
||||
void addTag(const QString &_tag)
|
||||
{
|
||||
tags.append(_tag);
|
||||
}
|
||||
void clearTags()
|
||||
{
|
||||
tags.clear();
|
||||
}
|
||||
void setBannerCard(const QPair<QString, QString> &_bannerCard = QPair<QString, QString>())
|
||||
{
|
||||
bannerCard = _bannerCard;
|
||||
|
|
@ -303,6 +316,10 @@ public:
|
|||
{
|
||||
return comments;
|
||||
}
|
||||
QStringList getTags() const
|
||||
{
|
||||
return tags;
|
||||
}
|
||||
QPair<QString, QString> getBannerCard() const
|
||||
{
|
||||
return bannerCard;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue