mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-10 00:04:48 -07:00
* move message_log_widget to game * move files * update headers * fix cmakelists * oracle fixes * split implementation out to cpp * fix recursive import * fix main file * format
This commit is contained in:
parent
f484c98152
commit
17dcaf9afa
337 changed files with 728 additions and 721 deletions
|
|
@ -0,0 +1,121 @@
|
|||
#include "deck_preview_color_identity_filter_widget.h"
|
||||
|
||||
#include "../../cards/additional_info/mana_symbol_widget.h"
|
||||
#include "deck_preview_widget.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent)
|
||||
: QWidget(parent), layout(new QHBoxLayout(this))
|
||||
{
|
||||
setLayout(layout);
|
||||
layout->setSpacing(5);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QString fullColorIdentity = "WUBRG";
|
||||
for (const QChar &color : fullColorIdentity) {
|
||||
auto *manaSymbol = new ManaSymbolWidget(this, color, false, true);
|
||||
manaSymbol->setFixedWidth(25);
|
||||
|
||||
layout->addWidget(manaSymbol);
|
||||
|
||||
// Initialize the activeColors map
|
||||
activeColors[color] = false;
|
||||
|
||||
// Connect the color toggled signal
|
||||
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
|
||||
&DeckPreviewColorIdentityFilterWidget::handleColorToggled);
|
||||
}
|
||||
|
||||
toggleButton = new QPushButton(this);
|
||||
toggleButton->setCheckable(true);
|
||||
layout->addWidget(toggleButton);
|
||||
|
||||
// Connect the button's toggled signal
|
||||
connect(toggleButton, &QPushButton::toggled, this, &DeckPreviewColorIdentityFilterWidget::updateFilterMode);
|
||||
connect(this, &DeckPreviewColorIdentityFilterWidget::activeColorsChanged, parent,
|
||||
&VisualDeckStorageWidget::updateColorFilter);
|
||||
connect(this, &DeckPreviewColorIdentityFilterWidget::filterModeChanged, parent,
|
||||
&VisualDeckStorageWidget::updateColorFilter);
|
||||
|
||||
// Call retranslateUi to set the initial text
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::retranslateUi()
|
||||
{
|
||||
// Set the toggle button text based on the current mode
|
||||
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
|
||||
toggleButton->setToolTip(tr("Color identity filter mode (AND/OR/NOT conjunctions of filters)"));
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar color, bool active)
|
||||
{
|
||||
activeColors[color] = active;
|
||||
emit activeColorsChanged();
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::updateFilterMode(bool checked)
|
||||
{
|
||||
exactMatchMode = checked; // Toggle between modes
|
||||
retranslateUi(); // Update the button text
|
||||
emit filterModeChanged(exactMatchMode);
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::filterWidgets(QList<DeckPreviewWidget *> widgets)
|
||||
{
|
||||
// Check if no colors are active
|
||||
bool noColorsActive = true;
|
||||
for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) {
|
||||
if (it.value()) {
|
||||
noColorsActive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no colors are active, return the unfiltered list of widgets
|
||||
if (noColorsActive) {
|
||||
for (DeckPreviewWidget *previewWidget : widgets) {
|
||||
previewWidget->filteredByColor = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &widget : widgets) {
|
||||
QString colorIdentity = widget->getColorIdentity();
|
||||
|
||||
bool matchesFilter = true;
|
||||
if (exactMatchMode) {
|
||||
// Exact match mode: active colors must exactly match colorIdentity
|
||||
|
||||
// Create a set of active colors
|
||||
QSet<QChar> activeColorSet;
|
||||
for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) {
|
||||
if (it.value()) {
|
||||
activeColorSet.insert(it.key().toUpper()); // Use uppercase for uniformity
|
||||
}
|
||||
}
|
||||
|
||||
// Create a set of colors from the color identity string
|
||||
QSet<QChar> colorIdentitySet;
|
||||
for (const QChar &color : colorIdentity) {
|
||||
colorIdentitySet.insert(color.toUpper()); // Ensure case uniformity
|
||||
}
|
||||
|
||||
// Compare the sets: the sets must match exactly
|
||||
if (activeColorSet != colorIdentitySet) {
|
||||
matchesFilter = false;
|
||||
}
|
||||
} else {
|
||||
// Includes mode: colorIdentity must contain all active colors
|
||||
for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) {
|
||||
if (it.value() && !colorIdentity.contains(it.key())) {
|
||||
matchesFilter = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
widget->filteredByColor = !matchesFilter;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef DECK_PREVIEW_COLOR_IDENTITY_FILTER_WIDGET_H
|
||||
#define DECK_PREVIEW_COLOR_IDENTITY_FILTER_WIDGET_H
|
||||
|
||||
#include "../visual_deck_storage_widget.h"
|
||||
|
||||
#include <QChar>
|
||||
#include <QHBoxLayout>
|
||||
#include <QList>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class DeckPreviewWidget;
|
||||
class VisualDeckStorageWidget;
|
||||
|
||||
class DeckPreviewColorIdentityFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent);
|
||||
void retranslateUi();
|
||||
void filterWidgets(QList<DeckPreviewWidget *> widgets);
|
||||
|
||||
signals:
|
||||
void filterModeChanged(bool exactMatchMode);
|
||||
void activeColorsChanged();
|
||||
|
||||
private slots:
|
||||
void handleColorToggled(QChar color, bool active);
|
||||
void updateFilterMode(bool checked);
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QPushButton *toggleButton;
|
||||
QMap<QChar, bool> activeColors;
|
||||
bool exactMatchMode = false; // Default to "includes" mode
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_COLOR_IDENTITY_FILTER_WIDGET_H
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include "../../../../dialogs/dlg_convert_deck_to_cod_format.h"
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "../../../../tabs/tab_deck_editor.h"
|
||||
#include "../../general/layout_containers/flow_widget.h"
|
||||
#include "deck_preview_tag_addition_widget.h"
|
||||
#include "deck_preview_tag_dialog.h"
|
||||
#include "deck_preview_tag_display_widget.h"
|
||||
#include "deck_preview_widget.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList)
|
||||
: QWidget(_parent), deckList(nullptr)
|
||||
{
|
||||
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setFixedHeight(100);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
||||
if (_deckList) {
|
||||
connectDeckList(_deckList);
|
||||
}
|
||||
|
||||
layout->addWidget(flowWidget);
|
||||
}
|
||||
|
||||
void DeckPreviewDeckTagsDisplayWidget::connectDeckList(DeckList *_deckList)
|
||||
{
|
||||
if (deckList) {
|
||||
disconnect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
|
||||
}
|
||||
|
||||
deckList = _deckList;
|
||||
connect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
|
||||
|
||||
refreshTags();
|
||||
}
|
||||
|
||||
void DeckPreviewDeckTagsDisplayWidget::refreshTags()
|
||||
{
|
||||
flowWidget->clearLayout();
|
||||
|
||||
for (const QString &tag : deckList->getTags()) {
|
||||
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
|
||||
}
|
||||
|
||||
auto tagAdditionWidget = new DeckPreviewTagAdditionWidget(this, tr("Edit tags ..."));
|
||||
connect(tagAdditionWidget, &DeckPreviewTagAdditionWidget::tagClicked, this,
|
||||
&DeckPreviewDeckTagsDisplayWidget::openTagEditDlg);
|
||||
flowWidget->addWidget(tagAdditionWidget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filepath of all files (no directories) in target directory and all subdirectories
|
||||
*/
|
||||
static QStringList getAllFiles(const QString &filePath)
|
||||
{
|
||||
QStringList allFiles;
|
||||
|
||||
// QDirIterator with QDir::Files ensures only files are listed (no directories)
|
||||
QDirIterator it(filePath, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
|
||||
while (it.hasNext()) {
|
||||
allFiles << it.next(); // Add each file path to the list
|
||||
}
|
||||
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
||||
{
|
||||
QFileInfo fileInfo(filePath);
|
||||
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
|
||||
|
||||
if (QFile::exists(newFileName)) {
|
||||
QMessageBox::StandardButton reply =
|
||||
QMessageBox::question(parent, QObject::tr("Overwrite Existing File?"),
|
||||
QObject::tr("A .cod version of this deck already exists. Overwrite it?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
return reply == QMessageBox::Yes;
|
||||
}
|
||||
return true; // Safe to proceed
|
||||
}
|
||||
|
||||
void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
|
||||
{
|
||||
if (qobject_cast<DeckPreviewWidget *>(parentWidget())) {
|
||||
auto *deckPreviewWidget = qobject_cast<DeckPreviewWidget *>(parentWidget());
|
||||
QStringList knownTags = deckPreviewWidget->visualDeckStorageWidget->tagFilterWidget->getAllKnownTags();
|
||||
QStringList activeTags = deckList->getTags();
|
||||
|
||||
bool canAddTags = true;
|
||||
|
||||
if (DeckLoader::getFormatFromName(deckPreviewWidget->filePath) != DeckLoader::CockatriceFormat) {
|
||||
canAddTags = false;
|
||||
// Retrieve saved preference if the prompt is disabled
|
||||
if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
|
||||
if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
|
||||
|
||||
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath))
|
||||
return;
|
||||
|
||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName();
|
||||
deckPreviewWidget->refreshBannerCardText();
|
||||
canAddTags = true;
|
||||
}
|
||||
} else {
|
||||
// Show the dialog to the user
|
||||
DialogConvertDeckToCodFormat conversionDialog(parentWidget());
|
||||
if (conversionDialog.exec() == QDialog::Accepted) {
|
||||
|
||||
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath))
|
||||
return;
|
||||
|
||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName();
|
||||
deckPreviewWidget->refreshBannerCardText();
|
||||
canAddTags = true;
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(true);
|
||||
}
|
||||
} else {
|
||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(false);
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
||||
} else {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canAddTags) {
|
||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QStringList updatedTags = dialog.getActiveTags();
|
||||
deckList->setTags(updatedTags);
|
||||
deckPreviewWidget->deckLoader->saveToFile(deckPreviewWidget->filePath, DeckLoader::CockatriceFormat);
|
||||
}
|
||||
}
|
||||
} else if (parentWidget()) {
|
||||
// If we're the child of an AbstractTabDeckEditor, we are buried under a ton of childWidgets in the
|
||||
// DeckInfoDock.
|
||||
QWidget *currentParent = parentWidget();
|
||||
while (currentParent) {
|
||||
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
|
||||
auto *deckEditor = qobject_cast<AbstractTabDeckEditor *>(currentParent);
|
||||
QStringList knownTags;
|
||||
QStringList allFiles = getAllFiles(SettingsCache::instance().getDeckPath());
|
||||
DeckLoader loader;
|
||||
for (const QString &file : allFiles) {
|
||||
loader.loadFromFile(file, DeckLoader::getFormatFromName(file), false);
|
||||
QStringList tags = loader.getTags();
|
||||
knownTags.append(tags);
|
||||
knownTags.removeDuplicates();
|
||||
}
|
||||
|
||||
QStringList activeTags = deckList->getTags();
|
||||
|
||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QStringList updatedTags = dialog.getActiveTags();
|
||||
deckList->setTags(updatedTags);
|
||||
deckEditor->setModified(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#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>
|
||||
|
||||
inline bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath);
|
||||
|
||||
class DeckPreviewWidget;
|
||||
class DeckPreviewDeckTagsDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList);
|
||||
void connectDeckList(DeckList *_deckList);
|
||||
void refreshTags();
|
||||
DeckList *deckList;
|
||||
FlowWidget *flowWidget;
|
||||
|
||||
public slots:
|
||||
void openTagEditDlg();
|
||||
};
|
||||
#endif // DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
#include "deck_preview_tag_addition_widget.h"
|
||||
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "../../../../tabs/abstract_tab_deck_editor.h"
|
||||
#include "deck_preview_tag_dialog.h"
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <utility>
|
||||
|
||||
DeckPreviewTagAdditionWidget::DeckPreviewTagAdditionWidget(QWidget *_parent, QString _tagName)
|
||||
: QWidget(_parent), tagName_(std::move(_tagName))
|
||||
{
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
layout->setSpacing(5);
|
||||
|
||||
// Adjust widget size
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
QSize DeckPreviewTagAdditionWidget::sizeHint() const
|
||||
{
|
||||
// Calculate the size based on the tag name
|
||||
QFontMetrics fm(font());
|
||||
int textWidth = fm.horizontalAdvance(tagName_);
|
||||
int width = textWidth + 50; // Add extra padding
|
||||
int height = fm.height() + 10; // Height based on font size + padding
|
||||
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
emit tagClicked();
|
||||
}
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DeckPreviewTagAdditionWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
// Set background color
|
||||
QColor backgroundColor = Qt::lightGray;
|
||||
painter.setBrush(backgroundColor);
|
||||
painter.setPen(Qt::NoPen);
|
||||
|
||||
// Draw background
|
||||
painter.drawRect(rect());
|
||||
|
||||
// Draw border
|
||||
QColor borderColor = Qt::gray;
|
||||
QPen borderPen(borderColor, 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 with margin
|
||||
int margin = 10; // Left and right margins
|
||||
QRect textRect(margin, 0, width() - 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,30 @@
|
|||
#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(QWidget *_parent, QString _tagName);
|
||||
[[nodiscard]] QSize sizeHint() const override;
|
||||
|
||||
signals:
|
||||
void tagClicked(); // Emitted when the tag is clicked
|
||||
void tagClosed(); // Emitted when the close button is clicked
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QString tagName_;
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_TAG_ADDITION_WIDGET_H
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
#include "deck_preview_tag_dialog.h"
|
||||
|
||||
#include "../../../../dialogs/dlg_default_tags_editor.h"
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "deck_preview_tag_item_widget.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags,
|
||||
const QStringList &_activeTags,
|
||||
QWidget *parent)
|
||||
: QDialog(parent), activeTags(_activeTags), knownTags_(knownTags)
|
||||
{
|
||||
resize(400, 500);
|
||||
|
||||
QStringList defaultTags = SettingsCache::instance().getVisualDeckStorageDefaultTagsList();
|
||||
|
||||
// Merge knownTags with defaultTags, ensuring no duplicates
|
||||
QStringList combinedTags = defaultTags + knownTags + activeTags;
|
||||
combinedTags.removeDuplicates();
|
||||
|
||||
// Main layout
|
||||
mainLayout = new QVBoxLayout(this);
|
||||
|
||||
// Filter bar
|
||||
filterInput = new QLineEdit(this);
|
||||
mainLayout->addWidget(filterInput);
|
||||
|
||||
connect(filterInput, &QLineEdit::textChanged, this, &DeckPreviewTagDialog::filterTags);
|
||||
|
||||
// Instruction label
|
||||
instructionLabel = new QLabel(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::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged);
|
||||
}
|
||||
|
||||
// Add tag input layout
|
||||
auto *addTagLayout = new QHBoxLayout();
|
||||
newTagInput = new QLineEdit(this);
|
||||
addTagButton = new QPushButton(this);
|
||||
editButton = new QPushButton(this);
|
||||
connect(editButton, &QPushButton::clicked, this, [this]() {
|
||||
auto *editor = new DlgDefaultTagsEditor(this);
|
||||
editor->setAttribute(Qt::WA_DeleteOnClose);
|
||||
editor->setModal(true);
|
||||
editor->show();
|
||||
QTimer::singleShot(0, editor, [editor]() {
|
||||
editor->raise();
|
||||
editor->activateWindow();
|
||||
});
|
||||
});
|
||||
addTagLayout->addWidget(newTagInput);
|
||||
addTagLayout->addWidget(addTagButton);
|
||||
addTagLayout->addWidget(editButton);
|
||||
mainLayout->addLayout(addTagLayout);
|
||||
|
||||
connect(addTagButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::addTag);
|
||||
connect(newTagInput, &QLineEdit::textChanged, this,
|
||||
[this](const QString &text) { addTagButton->setEnabled(!text.trimmed().isEmpty()); });
|
||||
|
||||
// OK and Cancel buttons
|
||||
buttonLayout = new QHBoxLayout(this);
|
||||
okButton = new QPushButton(this);
|
||||
cancelButton = new QPushButton(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);
|
||||
|
||||
connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageDefaultTagsListChanged, this,
|
||||
&DeckPreviewTagDialog::refreshTagList);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void DeckPreviewTagDialog::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Deck Tags Manager"));
|
||||
instructionLabel->setText(tr("Manage your deck tags. Check or uncheck tags as needed, or add new ones:"));
|
||||
newTagInput->setPlaceholderText(tr("Add a new tag (e.g., Aggro️)"));
|
||||
addTagButton->setText(tr("Add Tag"));
|
||||
filterInput->setPlaceholderText(tr("Filter tags..."));
|
||||
okButton->setText(tr("OK"));
|
||||
editButton->setText(tr("Edit default tags"));
|
||||
cancelButton->setText(tr("Cancel"));
|
||||
}
|
||||
|
||||
void DeckPreviewTagDialog::refreshTagList()
|
||||
{
|
||||
// First, clear the current tags in the list view
|
||||
tagListView->clear();
|
||||
|
||||
// Get the updated list of tags from SettingsCache
|
||||
QStringList defaultTags = SettingsCache::instance().getVisualDeckStorageDefaultTagsList();
|
||||
QStringList combinedTags = defaultTags + knownTags_ + activeTags;
|
||||
combinedTags.removeDuplicates();
|
||||
|
||||
// Re-populate the tag list view
|
||||
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::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DeckPreviewTagDialog::getActiveTags() const
|
||||
{
|
||||
return activeTags;
|
||||
}
|
||||
|
||||
void DeckPreviewTagDialog::addTag()
|
||||
{
|
||||
QString newTag = newTagInput->text().trimmed();
|
||||
if (newTag.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Invalid Input"), tr("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, tr("Duplicate Tag"), tr("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::toggled, this, &DeckPreviewTagDialog::onCheckboxStateChanged);
|
||||
|
||||
// Clear the input field
|
||||
newTagInput->clear();
|
||||
}
|
||||
|
||||
void DeckPreviewTagDialog::filterTags(const QString &text)
|
||||
{
|
||||
for (int i = 0; i < tagListView->count(); ++i) {
|
||||
auto *item = tagListView->item(i);
|
||||
auto *tagWidget = qobject_cast<DeckPreviewTagItemWidget *>(tagListView->itemWidget(item));
|
||||
if (tagWidget) {
|
||||
bool matches = tagWidget->checkBox()->text().contains(text, Qt::CaseInsensitive);
|
||||
item->setHidden(!matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,46 @@
|
|||
#ifndef DECK_PREVIEW_TAG_DIALOG_H
|
||||
#define DECK_PREVIEW_TAG_DIALOG_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class DeckPreviewTagDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeckPreviewTagDialog(const QStringList &knownTags,
|
||||
const QStringList &activeTags,
|
||||
QWidget *parent = nullptr);
|
||||
QStringList getActiveTags() const;
|
||||
void filterTags(const QString &text);
|
||||
|
||||
private slots:
|
||||
void addTag();
|
||||
void onCheckboxStateChanged();
|
||||
void retranslateUi();
|
||||
void refreshTagList();
|
||||
|
||||
private:
|
||||
QVBoxLayout *mainLayout;
|
||||
QLabel *instructionLabel;
|
||||
QListWidget *tagListView;
|
||||
QLineEdit *filterInput;
|
||||
QHBoxLayout *addTagLayout;
|
||||
QLineEdit *newTagInput;
|
||||
QPushButton *addTagButton;
|
||||
QHBoxLayout *buttonLayout;
|
||||
QPushButton *okButton;
|
||||
QPushButton *editButton;
|
||||
QPushButton *cancelButton;
|
||||
QStringList activeTags;
|
||||
QStringList knownTags_;
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_TAG_DIALOG_H
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#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), state(TagState::NotSelected)
|
||||
{
|
||||
// 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 + 50; // Add extra padding
|
||||
int height = fm.height() + 10; // Height based on font size + padding
|
||||
|
||||
return QSize(width, height);
|
||||
}
|
||||
|
||||
void DeckPreviewTagDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
switch (event->button()) {
|
||||
case Qt::LeftButton:
|
||||
setState(state != TagState::Selected ? TagState::Selected : TagState::NotSelected);
|
||||
break;
|
||||
case Qt::RightButton:
|
||||
setState(state != TagState::Excluded ? TagState::Excluded : TagState::NotSelected);
|
||||
break;
|
||||
case Qt::MiddleButton:
|
||||
setState(TagState::NotSelected);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
emit tagClicked();
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DeckPreviewTagDisplayWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
QColor backgroundColor;
|
||||
QColor borderColor;
|
||||
int borderWidth;
|
||||
|
||||
switch (state) {
|
||||
case TagState::Selected:
|
||||
backgroundColor = QColor(173, 216, 230); // Light blue
|
||||
borderColor = Qt::blue;
|
||||
borderWidth = 2;
|
||||
break;
|
||||
case TagState::Excluded:
|
||||
backgroundColor = QColor(255, 182, 193); // Light red/pink
|
||||
borderColor = Qt::red;
|
||||
borderWidth = 2;
|
||||
break;
|
||||
case TagState::NotSelected:
|
||||
default:
|
||||
backgroundColor = Qt::white;
|
||||
borderColor = Qt::gray;
|
||||
borderWidth = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
painter.setBrush(backgroundColor);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(rect());
|
||||
|
||||
QPen borderPen(borderColor, borderWidth);
|
||||
painter.setPen(borderPen);
|
||||
painter.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||
|
||||
// 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() - 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,74 @@
|
|||
#ifndef DECK_PREVIEW_TAG_DISPLAY_WIDGET_H
|
||||
#define DECK_PREVIEW_TAG_DISPLAY_WIDGET_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
enum class TagState
|
||||
{
|
||||
NotSelected,
|
||||
Selected,
|
||||
Excluded
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
TagState getState() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState(const TagState newState)
|
||||
{
|
||||
state = newState;
|
||||
update();
|
||||
};
|
||||
|
||||
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.
|
||||
TagState state; ///< Indicates whether the tag is unselected, selected, or excluded.
|
||||
};
|
||||
|
||||
#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
|
||||
|
|
@ -0,0 +1,441 @@
|
|||
#include "deck_preview_widget.h"
|
||||
|
||||
#include "../../../../database/card_database_manager.h"
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "../../cards/additional_info/color_identity_widget.h"
|
||||
#include "../../cards/deck_preview_card_picture_widget.h"
|
||||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFileInfo>
|
||||
#include <QInputDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QMouseEvent>
|
||||
#include <QSet>
|
||||
#include <QStandardItemModel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
|
||||
VisualDeckStorageWidget *_visualDeckStorageWidget,
|
||||
const QString &_filePath)
|
||||
: QWidget(_parent), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath),
|
||||
colorIdentityWidget(nullptr), deckTagsDisplayWidget(nullptr)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
deckLoader = new DeckLoader();
|
||||
deckLoader->setParent(this);
|
||||
connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi);
|
||||
/* TODO: We shouldn't update the tags on *every* deck load, since it's kinda expensive. We should instead count how
|
||||
many deck loads have finished already and if we've loaded all decks and THEN load all the tags at once. */
|
||||
connect(deckLoader, &DeckLoader::loadFinished, visualDeckStorageWidget->tagFilterWidget,
|
||||
&VisualDeckStorageTagFilterWidget::refreshTags);
|
||||
deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false);
|
||||
|
||||
bannerCardDisplayWidget =
|
||||
new DeckPreviewCardPictureWidget(this, false, visualDeckStorageWidget->deckPreviewSelectionAnimationEnabled);
|
||||
|
||||
connect(bannerCardDisplayWidget, &DeckPreviewCardPictureWidget::imageClicked, this,
|
||||
&DeckPreviewWidget::imageClickedEvent);
|
||||
connect(bannerCardDisplayWidget, &DeckPreviewCardPictureWidget::imageDoubleClicked, this,
|
||||
&DeckPreviewWidget::imageDoubleClickedEvent);
|
||||
|
||||
connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageShowTagsOnDeckPreviewsChanged, this,
|
||||
&DeckPreviewWidget::updateTagsVisibility);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageShowBannerCardComboBoxChanged, this,
|
||||
&DeckPreviewWidget::updateBannerCardComboBoxVisibility);
|
||||
connect(visualDeckStorageWidget->settings(), &VisualDeckStorageQuickSettingsWidget::deckPreviewTooltipChanged, this,
|
||||
&DeckPreviewWidget::refreshBannerCardToolTip);
|
||||
|
||||
layout->addWidget(bannerCardDisplayWidget);
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::retranslateUi()
|
||||
{
|
||||
bannerCardLabel->setText(tr("Banner Card"));
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
if (bannerCardDisplayWidget == nullptr) {
|
||||
return;
|
||||
}
|
||||
QList<QWidget *> widgets = findChildren<QWidget *>();
|
||||
for (QWidget *widget : widgets) {
|
||||
widget->setMaximumWidth(bannerCardDisplayWidget->width());
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess)
|
||||
{
|
||||
if (!deckLoadSuccess) {
|
||||
return;
|
||||
}
|
||||
auto bannerCard = deckLoader->getBannerCard().name.isEmpty()
|
||||
? ExactCard()
|
||||
: CardDatabaseManager::getInstance()->getCard(deckLoader->getBannerCard());
|
||||
|
||||
bannerCardDisplayWidget->setCard(bannerCard);
|
||||
bannerCardDisplayWidget->setFontSize(24);
|
||||
setFilePath(deckLoader->getLastFileName());
|
||||
|
||||
colorIdentityWidget = new ColorIdentityWidget(this, getColorIdentity());
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader);
|
||||
|
||||
bannerCardLabel = new QLabel(this);
|
||||
bannerCardLabel->setObjectName("bannerCardLabel");
|
||||
bannerCardComboBox = new QComboBox(this);
|
||||
bannerCardComboBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
bannerCardComboBox->setObjectName("bannerCardComboBox");
|
||||
bannerCardComboBox->setCurrentText(deckLoader->getBannerCard().name);
|
||||
bannerCardComboBox->installEventFilter(new NoScrollFilter());
|
||||
connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&DeckPreviewWidget::setBannerCard);
|
||||
|
||||
updateBannerCardComboBox();
|
||||
updateBannerCardComboBoxVisibility(SettingsCache::instance().getVisualDeckStorageShowBannerCardComboBox());
|
||||
updateTagsVisibility(SettingsCache::instance().getVisualDeckStorageShowTagsOnDeckPreviews());
|
||||
|
||||
layout->addWidget(colorIdentityWidget);
|
||||
layout->addWidget(deckTagsDisplayWidget);
|
||||
layout->addWidget(bannerCardLabel);
|
||||
layout->addWidget(bannerCardComboBox);
|
||||
|
||||
refreshBannerCardText();
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::updateVisibility()
|
||||
{
|
||||
setHidden(!checkVisibility());
|
||||
}
|
||||
|
||||
bool DeckPreviewWidget::checkVisibility() const
|
||||
{
|
||||
if (filteredBySearch || filteredByColor || filteredByTags) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::updateBannerCardComboBoxVisibility(bool visible)
|
||||
{
|
||||
if (bannerCardComboBox == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
bannerCardComboBox->setVisible(true);
|
||||
bannerCardLabel->setVisible(true);
|
||||
} else {
|
||||
bannerCardComboBox->setHidden(true);
|
||||
bannerCardLabel->setHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::updateTagsVisibility(bool visible)
|
||||
{
|
||||
if (deckTagsDisplayWidget == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
deckTagsDisplayWidget->setVisible(true);
|
||||
} else {
|
||||
deckTagsDisplayWidget->setHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
QString DeckPreviewWidget::getColorIdentity()
|
||||
{
|
||||
QStringList cardList = deckLoader->getCardList();
|
||||
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 = CardDatabaseManager::getInstance()->getCardInfo(cardName);
|
||||
if (currentCard) {
|
||||
QString colors = currentCard->getColors(); // Assuming this 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The display name is given by the deck name, or the filename if the deck name is not set.
|
||||
*/
|
||||
QString DeckPreviewWidget::getDisplayName() const
|
||||
{
|
||||
return deckLoader->getName().isEmpty() ? QFileInfo(deckLoader->getLastFileName()).fileName()
|
||||
: deckLoader->getName();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::setFilePath(const QString &_filePath)
|
||||
{
|
||||
filePath = _filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the banner card text.
|
||||
* This also calls `refreshBannerCardToolTip`, since those two often need to be updated together.
|
||||
*/
|
||||
void DeckPreviewWidget::refreshBannerCardText()
|
||||
{
|
||||
bannerCardDisplayWidget->setOverlayText(getDisplayName());
|
||||
|
||||
refreshBannerCardToolTip();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::refreshBannerCardToolTip()
|
||||
{
|
||||
auto type = visualDeckStorageWidget->settings()->getDeckPreviewTooltip();
|
||||
switch (type) {
|
||||
case VisualDeckStorageQuickSettingsWidget::TooltipType::None:
|
||||
bannerCardDisplayWidget->setToolTip("");
|
||||
break;
|
||||
case VisualDeckStorageQuickSettingsWidget::TooltipType::Filepath:
|
||||
bannerCardDisplayWidget->setToolTip(filePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::updateBannerCardComboBox()
|
||||
{
|
||||
// Store the current text of the combo box
|
||||
QString currentText = bannerCardComboBox->currentText();
|
||||
|
||||
// Block signals temporarily
|
||||
bool wasBlocked = bannerCardComboBox->blockSignals(true);
|
||||
bannerCardComboBox->setUpdatesEnabled(false);
|
||||
|
||||
// Clear the existing items in the combo box
|
||||
bannerCardComboBox->clear();
|
||||
|
||||
// Prepare the new items with deduplication
|
||||
QSet<QPair<QString, QString>> bannerCardSet;
|
||||
InnerDecklistNode *listRoot = deckLoader->getRoot();
|
||||
for (auto i : *listRoot) {
|
||||
auto *currentZone = dynamic_cast<InnerDecklistNode *>(i);
|
||||
for (auto j : *currentZone) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(j);
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
bannerCardSet.insert(QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QPair<QString, QString>> pairList = bannerCardSet.values();
|
||||
|
||||
// Sort QList by the first() element of the QPair
|
||||
std::sort(pairList.begin(), pairList.end(), [](const QPair<QString, QString> &a, const QPair<QString, QString> &b) {
|
||||
return a.first.toLower() < b.first.toLower();
|
||||
});
|
||||
|
||||
// This is *slightly* more performant than using addItem in a loop.
|
||||
|
||||
QStandardItemModel *model = new QStandardItemModel(pairList.size(), 1, bannerCardComboBox);
|
||||
|
||||
int row = 0;
|
||||
for (const auto &pair : pairList) {
|
||||
QStandardItem *item = new QStandardItem(pair.first);
|
||||
item->setData(QVariant::fromValue(pair), Qt::UserRole);
|
||||
model->setItem(row++, 0, item);
|
||||
}
|
||||
|
||||
bannerCardComboBox->setModel(model);
|
||||
|
||||
// Try to restore the previous selection by finding the currentText
|
||||
int restoredIndex = bannerCardComboBox->findText(currentText);
|
||||
if (restoredIndex != -1) {
|
||||
bannerCardComboBox->setCurrentIndex(restoredIndex);
|
||||
} else {
|
||||
// Add a placeholder "-" and set it as the current selection
|
||||
int bannerIndex = bannerCardComboBox->findText(deckLoader->getBannerCard().name);
|
||||
if (bannerIndex != -1) {
|
||||
bannerCardComboBox->setCurrentIndex(bannerIndex);
|
||||
} else {
|
||||
bannerCardComboBox->insertItem(0, "-");
|
||||
bannerCardComboBox->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the previous signal blocking state
|
||||
bannerCardComboBox->blockSignals(wasBlocked);
|
||||
bannerCardComboBox->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::setBannerCard(int /* changedIndex */)
|
||||
{
|
||||
auto [name, id] = bannerCardComboBox->currentData().value<QPair<QString, QString>>();
|
||||
CardRef cardRef = {name, id};
|
||||
deckLoader->setBannerCard(cardRef);
|
||||
deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath));
|
||||
bannerCardDisplayWidget->setCard(CardDatabaseManager::getInstance()->getCard(cardRef));
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance)
|
||||
{
|
||||
Q_UNUSED(instance);
|
||||
|
||||
if (event && event->button() == Qt::RightButton) {
|
||||
createRightClickMenu()->popup(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
Q_UNUSED(instance);
|
||||
emit deckLoadRequested(filePath);
|
||||
}
|
||||
|
||||
QMenu *DeckPreviewWidget::createRightClickMenu()
|
||||
{
|
||||
auto *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(menu->addAction(tr("Open in deck editor")), &QAction::triggered, this,
|
||||
[this] { emit openDeckEditor(deckLoader); });
|
||||
|
||||
connect(menu->addAction(tr("Edit Tags")), &QAction::triggered, deckTagsDisplayWidget,
|
||||
&DeckPreviewDeckTagsDisplayWidget::openTagEditDlg);
|
||||
|
||||
addSetBannerCardMenu(menu);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
connect(menu->addAction(tr("Rename Deck")), &QAction::triggered, this, &DeckPreviewWidget::actRenameDeck);
|
||||
|
||||
auto saveToClipboardMenu = menu->addMenu(tr("Save Deck to Clipboard"));
|
||||
|
||||
connect(saveToClipboardMenu->addAction(tr("Annotated")), &QAction::triggered, this,
|
||||
[this] { deckLoader->saveToClipboard(true, true); });
|
||||
connect(saveToClipboardMenu->addAction(tr("Annotated (No set info)")), &QAction::triggered, this,
|
||||
[this] { deckLoader->saveToClipboard(true, false); });
|
||||
connect(saveToClipboardMenu->addAction(tr("Not Annotated")), &QAction::triggered, this,
|
||||
[this] { deckLoader->saveToClipboard(false, true); });
|
||||
connect(saveToClipboardMenu->addAction(tr("Not Annotated (No set info)")), &QAction::triggered, this,
|
||||
[this] { deckLoader->saveToClipboard(false, false); });
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
connect(menu->addAction(tr("Rename File")), &QAction::triggered, this, &DeckPreviewWidget::actRenameFile);
|
||||
|
||||
connect(menu->addAction(tr("Delete File")), &QAction::triggered, this, &DeckPreviewWidget::actDeleteFile);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the "Set Banner Card" submenu to the given menu. Does nothing if bannerCardComboBox is null.
|
||||
* @param menu The menu to add the submenu to
|
||||
*/
|
||||
void DeckPreviewWidget::addSetBannerCardMenu(QMenu *menu)
|
||||
{
|
||||
if (!bannerCardComboBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto bannerCardMenu = menu->addMenu(tr("Set Banner Card"));
|
||||
|
||||
for (int i = 0; i < bannerCardComboBox->count(); ++i) {
|
||||
auto action = bannerCardMenu->addAction(bannerCardComboBox->itemText(i));
|
||||
connect(action, &QAction::triggered, this, [this, i] { bannerCardComboBox->setCurrentIndex(i); });
|
||||
|
||||
// the checkability is purely for visuals
|
||||
action->setCheckable(true);
|
||||
action->setChecked(bannerCardComboBox->currentIndex() == i);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::actRenameDeck()
|
||||
{
|
||||
// read input
|
||||
const QString oldName = deckLoader->getName();
|
||||
|
||||
bool ok;
|
||||
QString newName = QInputDialog::getText(this, "Rename deck", tr("New name:"), QLineEdit::Normal, oldName, &ok);
|
||||
if (!ok || oldName == newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// write change
|
||||
deckLoader->setName(newName);
|
||||
deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath));
|
||||
|
||||
// update VDS
|
||||
refreshBannerCardText();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::actRenameFile()
|
||||
{
|
||||
// read input
|
||||
const auto info = QFileInfo(filePath);
|
||||
const QString oldName = info.baseName();
|
||||
|
||||
bool ok;
|
||||
QString newName = QInputDialog::getText(this, "Rename file", tr("New name:"), QLineEdit::Normal, oldName, &ok);
|
||||
if (!ok || newName.isEmpty() || oldName == newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString newFileName = newName;
|
||||
if (!info.suffix().isEmpty()) {
|
||||
newFileName += "." + info.suffix();
|
||||
}
|
||||
|
||||
// write change
|
||||
const QString newFilePath = QFileInfo(info.dir(), newFileName).filePath();
|
||||
if (!QFile::rename(info.filePath(), newFilePath)) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Rename failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
deckLoader->setLastFileName(newFilePath);
|
||||
|
||||
// update VDS
|
||||
setFilePath(newFilePath);
|
||||
refreshBannerCardText();
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::actDeleteFile()
|
||||
{
|
||||
// read input
|
||||
auto res = QMessageBox::warning(this, tr("Delete file"), tr("Are you sure you want to delete the selected file?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (res != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// write change
|
||||
if (!QFile::remove(QFileInfo(filePath).filePath())) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Delete failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
// update VDS
|
||||
this->deleteLater();
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef DECK_PREVIEW_WIDGET_H
|
||||
#define DECK_PREVIEW_WIDGET_H
|
||||
|
||||
#include "../../../../deck/deck_loader.h"
|
||||
#include "../../cards/additional_info/color_identity_widget.h"
|
||||
#include "../../cards/deck_preview_card_picture_widget.h"
|
||||
#include "../visual_deck_storage_widget.h"
|
||||
#include "deck_preview_deck_tags_display_widget.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
#include <QEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class QMenu;
|
||||
class VisualDeckStorageWidget;
|
||||
class DeckPreviewDeckTagsDisplayWidget;
|
||||
|
||||
class DeckPreviewWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DeckPreviewWidget(QWidget *_parent,
|
||||
VisualDeckStorageWidget *_visualDeckStorageWidget,
|
||||
const QString &_filePath);
|
||||
void retranslateUi();
|
||||
QString getColorIdentity();
|
||||
QString getDisplayName() const;
|
||||
|
||||
VisualDeckStorageWidget *visualDeckStorageWidget;
|
||||
QVBoxLayout *layout;
|
||||
QString filePath;
|
||||
DeckLoader *deckLoader;
|
||||
DeckPreviewCardPictureWidget *bannerCardDisplayWidget = nullptr;
|
||||
ColorIdentityWidget *colorIdentityWidget = nullptr;
|
||||
DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget = nullptr;
|
||||
QLabel *bannerCardLabel = nullptr;
|
||||
QComboBox *bannerCardComboBox = nullptr;
|
||||
bool filteredBySearch = false;
|
||||
bool filteredByColor = false;
|
||||
bool filteredByTags = false;
|
||||
bool checkVisibility() const;
|
||||
|
||||
signals:
|
||||
void deckLoadRequested(const QString &filePath);
|
||||
void openDeckEditor(const DeckLoader *deck);
|
||||
|
||||
public slots:
|
||||
void setFilePath(const QString &filePath);
|
||||
void refreshBannerCardText();
|
||||
void refreshBannerCardToolTip();
|
||||
void updateBannerCardComboBox();
|
||||
void setBannerCard(int);
|
||||
void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
|
||||
void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
|
||||
void initializeUi(bool deckLoadSuccess);
|
||||
void updateVisibility();
|
||||
void updateBannerCardComboBoxVisibility(bool visible);
|
||||
void updateTagsVisibility(bool visible);
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
QMenu *createRightClickMenu();
|
||||
void addSetBannerCardMenu(QMenu *menu);
|
||||
|
||||
private slots:
|
||||
void actRenameDeck();
|
||||
void actRenameFile();
|
||||
void actDeleteFile();
|
||||
};
|
||||
|
||||
class NoScrollFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override
|
||||
{
|
||||
if (event->type() == QEvent::Wheel) {
|
||||
if (auto *combo = qobject_cast<QComboBox *>(obj)) {
|
||||
// If popup is not open, forward event to parent scroll area
|
||||
if (!combo->view()->isVisible()) {
|
||||
// Try to find a scrollable parent and manually send the event
|
||||
QWidget *parent = combo->parentWidget();
|
||||
while (parent) {
|
||||
if (auto *scroll = qobject_cast<QAbstractScrollArea *>(parent)) {
|
||||
QApplication::sendEvent(scroll->viewport(), event);
|
||||
return true; // Mark event as handled
|
||||
}
|
||||
parent = parent->parentWidget();
|
||||
}
|
||||
// If no scrollable parent found, just block
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DECK_PREVIEW_WIDGET_H
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
#include "visual_deck_storage_folder_display_widget.h"
|
||||
|
||||
#include "../../../settings/cache_settings.h"
|
||||
#include "deck_preview/deck_preview_widget.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QMouseEvent>
|
||||
|
||||
VisualDeckStorageFolderDisplayWidget::VisualDeckStorageFolderDisplayWidget(
|
||||
QWidget *parent,
|
||||
VisualDeckStorageWidget *_visualDeckStorageWidget,
|
||||
QString _filePath,
|
||||
bool canBeHidden,
|
||||
bool _showFolders)
|
||||
: QWidget(parent), showFolders(_showFolders), visualDeckStorageWidget(_visualDeckStorageWidget), filePath(_filePath)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
header = new BannerWidget(this, "");
|
||||
header->setClickable(canBeHidden);
|
||||
header->setHidden(!showFolders);
|
||||
layout->addWidget(header);
|
||||
|
||||
container = new QWidget(this);
|
||||
containerLayout = new QVBoxLayout(container);
|
||||
container->setLayout(containerLayout);
|
||||
|
||||
header->setBuddy(container);
|
||||
|
||||
layout->addWidget(container);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
|
||||
containerLayout->addWidget(flowWidget);
|
||||
|
||||
createWidgetsForFiles();
|
||||
createWidgetsForFolders();
|
||||
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
void VisualDeckStorageFolderDisplayWidget::refreshUi()
|
||||
{
|
||||
QString bannerText = tr("Deck Storage");
|
||||
QString deckPath = SettingsCache::instance().getDeckPath();
|
||||
if (filePath != deckPath) {
|
||||
QString relativePath = filePath;
|
||||
|
||||
if (filePath.startsWith(deckPath)) {
|
||||
relativePath = filePath.mid(deckPath.length()); // Remove the deckPath prefix
|
||||
if (relativePath.startsWith('/')) {
|
||||
relativePath.remove(0, 1); // Remove leading '/' if it exists
|
||||
}
|
||||
}
|
||||
|
||||
bannerText = relativePath;
|
||||
}
|
||||
header->setText(bannerText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all files in the directory that have an accepted decklist file extension
|
||||
*
|
||||
* @param filePath The directory to search through
|
||||
* @param recursive Whether to search through subdirectories
|
||||
*/
|
||||
static QStringList getAllFiles(const QString &filePath, bool recursive)
|
||||
{
|
||||
QStringList allFiles;
|
||||
|
||||
// QDirIterator with QDir::Files ensures only files are listed (no directories)
|
||||
auto flags =
|
||||
recursive ? QDirIterator::Subdirectories | QDirIterator::FollowSymlinks : QDirIterator::NoIteratorFlags;
|
||||
QDirIterator it(filePath, DeckLoader::ACCEPTED_FILE_EXTENSIONS, QDir::Files, flags);
|
||||
|
||||
while (it.hasNext()) {
|
||||
allFiles << it.next(); // Add each file path to the list
|
||||
}
|
||||
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
void VisualDeckStorageFolderDisplayWidget::createWidgetsForFiles()
|
||||
{
|
||||
QList<DeckPreviewWidget *> allDecks;
|
||||
for (const QString &file : getAllFiles(filePath, !showFolders)) {
|
||||
auto *display = new DeckPreviewWidget(flowWidget, visualDeckStorageWidget, file);
|
||||
|
||||
connect(display, &DeckPreviewWidget::deckLoadRequested, visualDeckStorageWidget,
|
||||
&VisualDeckStorageWidget::deckLoadRequested);
|
||||
connect(display, &DeckPreviewWidget::openDeckEditor, visualDeckStorageWidget,
|
||||
&VisualDeckStorageWidget::openDeckEditor);
|
||||
connect(visualDeckStorageWidget->settings(), &VisualDeckStorageQuickSettingsWidget::cardSizeChanged,
|
||||
display->bannerCardDisplayWidget, &CardInfoPictureWidget::setScaleFactor);
|
||||
display->bannerCardDisplayWidget->setScaleFactor(visualDeckStorageWidget->settings()->getCardSize());
|
||||
allDecks.append(display);
|
||||
}
|
||||
|
||||
flowWidget->clearLayout(); // Clear existing widgets in the flow layout
|
||||
|
||||
for (DeckPreviewWidget *deck : allDecks) {
|
||||
flowWidget->addWidget(deck);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visibility of this folder and all its DeckPreviewWidgets
|
||||
*
|
||||
* @param recursive Also update the visibility of all subfolders and their DeckPreviewWidgets.
|
||||
*/
|
||||
void VisualDeckStorageFolderDisplayWidget::updateVisibility(bool recursive)
|
||||
{
|
||||
bool atLeastOneWidgetVisible = checkVisibility();
|
||||
if (atLeastOneWidgetVisible) {
|
||||
setVisible(true);
|
||||
for (DeckPreviewWidget *display : flowWidget->findChildren<DeckPreviewWidget *>()) {
|
||||
display->updateVisibility();
|
||||
}
|
||||
if (recursive) {
|
||||
for (auto *subFolder : findChildren<VisualDeckStorageFolderDisplayWidget *>()) {
|
||||
subFolder->updateVisibility(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool VisualDeckStorageFolderDisplayWidget::checkVisibility()
|
||||
{
|
||||
bool atLeastOneWidgetVisible = false;
|
||||
if (flowWidget) {
|
||||
// Iterate through all DeckPreviewWidgets
|
||||
for (DeckPreviewWidget *display : flowWidget->findChildren<DeckPreviewWidget *>()) {
|
||||
if (display->checkVisibility()) {
|
||||
atLeastOneWidgetVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (VisualDeckStorageFolderDisplayWidget *subFolder : findChildren<VisualDeckStorageFolderDisplayWidget *>()) {
|
||||
if (subFolder->checkVisibility()) {
|
||||
atLeastOneWidgetVisible = true;
|
||||
}
|
||||
}
|
||||
return atLeastOneWidgetVisible;
|
||||
}
|
||||
|
||||
static QStringList getAllSubFolders(const QString &filePath)
|
||||
{
|
||||
QStringList allFolders;
|
||||
|
||||
// QDirIterator with QDir::Files ensures only files are listed (no directories)
|
||||
QDirIterator it(filePath, QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
while (it.hasNext()) {
|
||||
allFolders << it.next(); // Add each file path to the list
|
||||
}
|
||||
|
||||
return allFolders;
|
||||
}
|
||||
|
||||
void VisualDeckStorageFolderDisplayWidget::createWidgetsForFolders()
|
||||
{
|
||||
if (!showFolders) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QString &dir : getAllSubFolders(filePath)) {
|
||||
auto *display = new VisualDeckStorageFolderDisplayWidget(this, visualDeckStorageWidget, dir, true, showFolders);
|
||||
containerLayout->addWidget(display);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageFolderDisplayWidget::updateShowFolders(bool enabled)
|
||||
{
|
||||
showFolders = enabled;
|
||||
|
||||
if (!showFolders) {
|
||||
flattenFolderStructure();
|
||||
} else {
|
||||
// if setting was switched from disabled to enabled, we assume that there aren't any existing subfolders
|
||||
createWidgetsForFiles();
|
||||
createWidgetsForFolders();
|
||||
}
|
||||
|
||||
header->setHidden(!showFolders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steals all DeckPreviewWidgets from this widget's nested subfolders, and deletes those subfolders
|
||||
*/
|
||||
void VisualDeckStorageFolderDisplayWidget::flattenFolderStructure()
|
||||
{
|
||||
for (auto *subFolder : findChildren<VisualDeckStorageFolderDisplayWidget *>()) {
|
||||
// steal all DeckPreviewWidgets from the subfolder
|
||||
for (auto *deck : subFolder->getFlowWidget()->findChildren<DeckPreviewWidget *>()) {
|
||||
flowWidget->addWidget(deck);
|
||||
}
|
||||
|
||||
// delete the subfolder
|
||||
subFolder->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget() const
|
||||
{
|
||||
QStringList allTags;
|
||||
|
||||
if (flowWidget) {
|
||||
// Iterate through all DeckPreviewWidgets
|
||||
for (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;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H
|
||||
#define VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../general/display/banner_widget.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
#include "visual_deck_storage_widget.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDeckStorageFolderDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VisualDeckStorageFolderDisplayWidget(QWidget *parent,
|
||||
VisualDeckStorageWidget *_visualDeckStorageWidget,
|
||||
QString _filePath,
|
||||
bool canBeHidden,
|
||||
bool _showFolders);
|
||||
void refreshUi();
|
||||
void createWidgetsForFiles();
|
||||
void createWidgetsForFolders();
|
||||
void flattenFolderStructure();
|
||||
QStringList gatherAllTagsFromFlowWidget() const;
|
||||
FlowWidget *getFlowWidget() const
|
||||
{
|
||||
return flowWidget;
|
||||
};
|
||||
|
||||
public slots:
|
||||
void updateVisibility(bool recursive = true);
|
||||
bool checkVisibility();
|
||||
void updateShowFolders(bool enabled);
|
||||
|
||||
private:
|
||||
bool showFolders;
|
||||
QVBoxLayout *layout;
|
||||
VisualDeckStorageWidget *visualDeckStorageWidget;
|
||||
QString filePath;
|
||||
BannerWidget *header;
|
||||
QWidget *container;
|
||||
QVBoxLayout *containerLayout;
|
||||
FlowWidget *flowWidget;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_FOLDER_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
#include "visual_deck_storage_quick_settings_widget.h"
|
||||
|
||||
#include "../../../settings/cache_settings.h"
|
||||
#include "visual_deck_storage_widget.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
|
||||
VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidget *parent)
|
||||
: SettingsButtonWidget(parent)
|
||||
{
|
||||
// show folders checkbox
|
||||
showFoldersCheckBox = new QCheckBox(this);
|
||||
showFoldersCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowFolders());
|
||||
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::showFoldersChanged);
|
||||
connect(showFoldersCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageShowFolders);
|
||||
|
||||
// show tag filter widget checkbox
|
||||
showTagFilterCheckBox = new QCheckBox(this);
|
||||
showTagFilterCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowTagFilter());
|
||||
connect(showTagFilterCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::showTagFilterChanged);
|
||||
connect(showTagFilterCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageShowTagFilter);
|
||||
|
||||
// show tags on DeckPreviewWidget checkbox
|
||||
showTagsOnDeckPreviewsCheckBox = new QCheckBox(this);
|
||||
showTagsOnDeckPreviewsCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowTagsOnDeckPreviews());
|
||||
connect(showTagsOnDeckPreviewsCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::showTagsOnDeckPreviewsChanged);
|
||||
connect(showTagsOnDeckPreviewsCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageShowTagsOnDeckPreviews);
|
||||
|
||||
// show banner card selector checkbox
|
||||
showBannerCardComboBoxCheckBox = new QCheckBox(this);
|
||||
showBannerCardComboBoxCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageShowBannerCardComboBox());
|
||||
connect(showBannerCardComboBoxCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::showBannerCardComboBoxChanged);
|
||||
connect(showBannerCardComboBoxCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageShowBannerCardComboBox);
|
||||
|
||||
// draw unused color identities checkbox
|
||||
drawUnusedColorIdentitiesCheckBox = new QCheckBox(this);
|
||||
drawUnusedColorIdentitiesCheckBox->setChecked(
|
||||
SettingsCache::instance().getVisualDeckStorageDrawUnusedColorIdentities());
|
||||
connect(drawUnusedColorIdentitiesCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::drawUnusedColorIdentitiesChanged);
|
||||
connect(drawUnusedColorIdentitiesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageDrawUnusedColorIdentities);
|
||||
|
||||
// color identity opacity selector
|
||||
auto unusedColorIdentityOpacityWidget = new QWidget(this);
|
||||
|
||||
unusedColorIdentitiesOpacityLabel = new QLabel(unusedColorIdentityOpacityWidget);
|
||||
unusedColorIdentitiesOpacitySpinBox = new QSpinBox(unusedColorIdentityOpacityWidget);
|
||||
|
||||
unusedColorIdentitiesOpacitySpinBox->setMinimum(0);
|
||||
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
|
||||
unusedColorIdentitiesOpacitySpinBox->setValue(
|
||||
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
|
||||
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
&VisualDeckStorageQuickSettingsWidget::unusedColorIdentitiesOpacityChanged);
|
||||
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
&SettingsCache::instance(), &SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
|
||||
|
||||
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);
|
||||
|
||||
auto unusedColorIdentityOpacityLayout = new QHBoxLayout(unusedColorIdentityOpacityWidget);
|
||||
unusedColorIdentityOpacityLayout->setContentsMargins(11, 0, 11, 0);
|
||||
unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacityLabel);
|
||||
unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacitySpinBox);
|
||||
|
||||
// tooltip selector
|
||||
auto deckPreviewTooltipWidget = new QWidget(this);
|
||||
|
||||
deckPreviewTooltipLabel = new QLabel(deckPreviewTooltipWidget);
|
||||
deckPreviewTooltipComboBox = new QComboBox(deckPreviewTooltipWidget);
|
||||
deckPreviewTooltipComboBox->setFocusPolicy(Qt::StrongFocus);
|
||||
deckPreviewTooltipComboBox->addItem("", TooltipType::None);
|
||||
deckPreviewTooltipComboBox->addItem("", TooltipType::Filepath);
|
||||
|
||||
deckPreviewTooltipComboBox->setCurrentIndex(SettingsCache::instance().getVisualDeckStorageTooltipType());
|
||||
connect(deckPreviewTooltipComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
[this] { emit deckPreviewTooltipChanged(getDeckPreviewTooltip()); });
|
||||
connect(deckPreviewTooltipComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageTooltipType);
|
||||
|
||||
auto deckPreviewTooltipLayout = new QHBoxLayout(deckPreviewTooltipWidget);
|
||||
deckPreviewTooltipLayout->setContentsMargins(11, 0, 11, 0);
|
||||
deckPreviewTooltipLayout->addWidget(deckPreviewTooltipLabel);
|
||||
deckPreviewTooltipLayout->addWidget(deckPreviewTooltipComboBox);
|
||||
|
||||
// card size slider
|
||||
cardSizeWidget = new CardSizeWidget(this, nullptr, SettingsCache::instance().getVisualDeckStorageCardSize());
|
||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::cardSizeChanged);
|
||||
connect(cardSizeWidget, &CardSizeWidget::cardSizeSettingUpdated, &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageCardSize);
|
||||
|
||||
// putting everything together
|
||||
this->addSettingsWidget(showFoldersCheckBox);
|
||||
this->addSettingsWidget(showTagFilterCheckBox);
|
||||
this->addSettingsWidget(showTagsOnDeckPreviewsCheckBox);
|
||||
this->addSettingsWidget(showBannerCardComboBoxCheckBox);
|
||||
this->addSettingsWidget(drawUnusedColorIdentitiesCheckBox);
|
||||
this->addSettingsWidget(unusedColorIdentityOpacityWidget);
|
||||
this->addSettingsWidget(deckPreviewTooltipWidget);
|
||||
this->addSettingsWidget(cardSizeWidget);
|
||||
|
||||
connect(&SettingsCache::instance(), &SettingsCache::langChanged, this,
|
||||
&VisualDeckStorageQuickSettingsWidget::retranslateUi);
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void VisualDeckStorageQuickSettingsWidget::retranslateUi()
|
||||
{
|
||||
showFoldersCheckBox->setText(tr("Show Folders"));
|
||||
showTagFilterCheckBox->setText(tr("Show Tag Filter"));
|
||||
showTagsOnDeckPreviewsCheckBox->setText(tr("Show Tags On Deck Previews"));
|
||||
showBannerCardComboBoxCheckBox->setText(tr("Show Banner Card Selection Option"));
|
||||
drawUnusedColorIdentitiesCheckBox->setText(tr("Draw unused Color Identities"));
|
||||
unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity"));
|
||||
unusedColorIdentitiesOpacitySpinBox->setSuffix("%");
|
||||
|
||||
deckPreviewTooltipLabel->setText(tr("Deck tooltip:"));
|
||||
deckPreviewTooltipComboBox->setItemText(0, tr("None"));
|
||||
deckPreviewTooltipComboBox->setItemText(1, tr("Filepath"));
|
||||
}
|
||||
|
||||
bool VisualDeckStorageQuickSettingsWidget::getShowFolders() const
|
||||
{
|
||||
return showFoldersCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool VisualDeckStorageQuickSettingsWidget::getDrawUnusedColorIdentities() const
|
||||
{
|
||||
return drawUnusedColorIdentitiesCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool VisualDeckStorageQuickSettingsWidget::getShowBannerCardComboBox() const
|
||||
{
|
||||
return showBannerCardComboBoxCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool VisualDeckStorageQuickSettingsWidget::getShowTagFilter() const
|
||||
{
|
||||
return showTagFilterCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool VisualDeckStorageQuickSettingsWidget::getShowTagsOnDeckPreviews() const
|
||||
{
|
||||
return showTagsOnDeckPreviewsCheckBox->isChecked();
|
||||
}
|
||||
|
||||
int VisualDeckStorageQuickSettingsWidget::getUnusedColorIdentitiesOpacity() const
|
||||
{
|
||||
return unusedColorIdentitiesOpacitySpinBox->value();
|
||||
}
|
||||
|
||||
VisualDeckStorageQuickSettingsWidget::TooltipType VisualDeckStorageQuickSettingsWidget::getDeckPreviewTooltip() const
|
||||
{
|
||||
return deckPreviewTooltipComboBox->currentData().value<TooltipType>();
|
||||
}
|
||||
|
||||
int VisualDeckStorageQuickSettingsWidget::getCardSize() const
|
||||
{
|
||||
return cardSizeWidget->getSlider()->value();
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef VISUAL_DECK_STORAGE_QUICK_SETTINGS_WIDGET_H
|
||||
#define VISUAL_DECK_STORAGE_QUICK_SETTINGS_WIDGET_H
|
||||
|
||||
#include "../quick_settings/settings_button_widget.h"
|
||||
|
||||
class CardSizeWidget;
|
||||
class QLabel;
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
|
||||
/**
|
||||
* The VDS's quick settings menu.
|
||||
* Manages the widgets in the quick settings menu dropdown, as well as syncing their values with SettingsCache.
|
||||
* The current values of the settings are exposed through getters and signals.
|
||||
*/
|
||||
class VisualDeckStorageQuickSettingsWidget : public SettingsButtonWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QCheckBox *showFoldersCheckBox;
|
||||
QCheckBox *drawUnusedColorIdentitiesCheckBox;
|
||||
QCheckBox *showBannerCardComboBoxCheckBox;
|
||||
QCheckBox *showTagFilterCheckBox;
|
||||
QCheckBox *showTagsOnDeckPreviewsCheckBox;
|
||||
QLabel *unusedColorIdentitiesOpacityLabel;
|
||||
QSpinBox *unusedColorIdentitiesOpacitySpinBox;
|
||||
QLabel *deckPreviewTooltipLabel;
|
||||
QComboBox *deckPreviewTooltipComboBox;
|
||||
CardSizeWidget *cardSizeWidget;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The info to display in the deck preview's banner card tooltip.
|
||||
*/
|
||||
enum TooltipType
|
||||
{
|
||||
None,
|
||||
Filepath
|
||||
};
|
||||
Q_ENUM(TooltipType)
|
||||
|
||||
explicit VisualDeckStorageQuickSettingsWidget(QWidget *parent = nullptr);
|
||||
|
||||
void retranslateUi();
|
||||
|
||||
bool getShowFolders() const;
|
||||
bool getDrawUnusedColorIdentities() const;
|
||||
bool getShowBannerCardComboBox() const;
|
||||
bool getShowTagFilter() const;
|
||||
bool getShowTagsOnDeckPreviews() const;
|
||||
int getUnusedColorIdentitiesOpacity() const;
|
||||
TooltipType getDeckPreviewTooltip() const;
|
||||
int getCardSize() const;
|
||||
|
||||
signals:
|
||||
void showFoldersChanged(bool enabled);
|
||||
void drawUnusedColorIdentitiesChanged(bool enabled);
|
||||
void showBannerCardComboBoxChanged(bool enabled);
|
||||
void showTagFilterChanged(bool enabled);
|
||||
void showTagsOnDeckPreviewsChanged(bool enabled);
|
||||
void unusedColorIdentitiesOpacityChanged(int opacity);
|
||||
void deckPreviewTooltipChanged(TooltipType tooltip);
|
||||
void cardSizeChanged(int scale);
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_QUICK_SETTINGS_WIDGET_H
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include "visual_deck_storage_search_widget.h"
|
||||
|
||||
#include "../../../filters/deck_filter_string.h"
|
||||
#include "../../../filters/syntax_help.h"
|
||||
#include "../../../settings/cache_settings.h"
|
||||
#include "../../pixel_map_generator.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
/**
|
||||
* @brief Constructs a PrintingSelectorCardSearchWidget for searching cards by set name or set code.
|
||||
*
|
||||
* This widget provides a search bar that allows users to search for cards by either their set name
|
||||
* or set code. It uses a debounced timer to trigger the search action after the user stops typing.
|
||||
*
|
||||
* @param parent The parent PrintingSelector widget that will handle the search results.
|
||||
*/
|
||||
VisualDeckStorageSearchWidget::VisualDeckStorageSearchWidget(VisualDeckStorageWidget *parent) : parent(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
|
||||
searchBar = new QLineEdit(this);
|
||||
searchBar->setPlaceholderText(tr("Search by filename (or search expression)"));
|
||||
searchBar->setClearButtonEnabled(true);
|
||||
searchBar->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||
|
||||
auto help = searchBar->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
||||
connect(help, &QAction::triggered, this, [this] { createDeckSearchSyntaxHelpWindow(searchBar); });
|
||||
|
||||
layout->addWidget(searchBar);
|
||||
|
||||
// Add a debounce timer for the search bar to limit frequent updates
|
||||
searchDebounceTimer = new QTimer(this);
|
||||
searchDebounceTimer->setSingleShot(true);
|
||||
connect(searchBar, &QLineEdit::textChanged, this, [this]() {
|
||||
searchDebounceTimer->start(300); // 300ms debounce
|
||||
});
|
||||
|
||||
connect(searchDebounceTimer, &QTimer::timeout, parent, &VisualDeckStorageWidget::updateSearchFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the current text in the search bar.
|
||||
*
|
||||
* @return The text entered by the user in the search bar.
|
||||
*/
|
||||
QString VisualDeckStorageSearchWidget::getSearchText()
|
||||
{
|
||||
return searchBar->text();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the filepath into a relative filepath starting from the deck folder.
|
||||
* If the file isn't in the deck folder, then this will just return the filename.
|
||||
*
|
||||
* @param filePath The filepath to convert into a relative filepath
|
||||
*/
|
||||
static QString toRelativeFilepath(const QString &filePath)
|
||||
{
|
||||
QString deckPath = SettingsCache::instance().getDeckPath();
|
||||
if (filePath.startsWith(deckPath)) {
|
||||
return filePath.mid(deckPath.length());
|
||||
}
|
||||
|
||||
QFileInfo fileInfo(filePath);
|
||||
QString fileName = fileInfo.fileName();
|
||||
return fileName;
|
||||
}
|
||||
|
||||
void VisualDeckStorageSearchWidget::filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText)
|
||||
{
|
||||
auto filterString = DeckFilterString(searchText);
|
||||
|
||||
for (auto widget : widgets) {
|
||||
QString relativeFilePath = toRelativeFilepath(widget->filePath);
|
||||
widget->filteredBySearch = !filterString.check(widget, {relativeFilePath});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef VISUAL_DECK_STORAGE_SEARCH_WIDGET_H
|
||||
#define VISUAL_DECK_STORAGE_SEARCH_WIDGET_H
|
||||
|
||||
#include "visual_deck_storage_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDeckStorageWidget;
|
||||
class VisualDeckStorageSearchWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VisualDeckStorageSearchWidget(VisualDeckStorageWidget *parent);
|
||||
QString getSearchText();
|
||||
void filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText);
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
VisualDeckStorageWidget *parent;
|
||||
QLineEdit *searchBar;
|
||||
QTimer *searchDebounceTimer;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_SEARCH_WIDGET_H
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
#include "visual_deck_storage_sort_widget.h"
|
||||
|
||||
#include "../../../settings/cache_settings.h"
|
||||
|
||||
/**
|
||||
* @brief Constructs a PrintingSelectorCardSortWidget for searching cards by set name or set code.
|
||||
*
|
||||
* This widget provides a search bar that allows users to search for cards by either their set name
|
||||
* or set code. It uses a debounced timer to trigger the search action after the user stops typing.
|
||||
*
|
||||
* @param parent The parent PrintingSelector widget that will handle the search results.
|
||||
*/
|
||||
VisualDeckStorageSortWidget::VisualDeckStorageSortWidget(VisualDeckStorageWidget *parent)
|
||||
: parent(parent), sortOrder(Alphabetical)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
|
||||
// Initialize the ComboBox
|
||||
sortComboBox = new QComboBox(this);
|
||||
layout->addWidget(sortComboBox);
|
||||
|
||||
// Need to retranslateUi first so that the sortComboBox actually has entries and doesn't get its currentIndex
|
||||
// immediately capped to 0 when we try to set it
|
||||
retranslateUi();
|
||||
|
||||
// Set the current sort order
|
||||
sortComboBox->setCurrentIndex(SettingsCache::instance().getVisualDeckStorageSortingOrder());
|
||||
sortOrder = static_cast<SortOrder>(sortComboBox->currentIndex());
|
||||
|
||||
// Connect sorting change signal to refresh the file list
|
||||
connect(sortComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&VisualDeckStorageSortWidget::updateSortOrder);
|
||||
connect(this, &VisualDeckStorageSortWidget::sortOrderChanged, parent, &VisualDeckStorageWidget::updateSortOrder);
|
||||
}
|
||||
|
||||
void VisualDeckStorageSortWidget::retranslateUi()
|
||||
{
|
||||
// Block signals to avoid triggering unnecessary updates while changing text
|
||||
sortComboBox->blockSignals(true);
|
||||
|
||||
int oldIndex = sortComboBox->currentIndex();
|
||||
|
||||
// Clear and repopulate the ComboBox with translated items
|
||||
sortComboBox->clear();
|
||||
sortComboBox->addItem(tr("Sort Alphabetically (Deck Name)"), ByName);
|
||||
sortComboBox->addItem(tr("Sort Alphabetically (Filename)"), Alphabetical);
|
||||
sortComboBox->addItem(tr("Sort by Last Modified"), ByLastModified);
|
||||
sortComboBox->addItem(tr("Sort by Last Loaded"), ByLastLoaded);
|
||||
|
||||
// Restore the current index
|
||||
sortComboBox->setCurrentIndex(oldIndex);
|
||||
|
||||
// Re-enable signals
|
||||
sortComboBox->blockSignals(false);
|
||||
}
|
||||
|
||||
void VisualDeckStorageSortWidget::updateSortOrder()
|
||||
{
|
||||
sortOrder = static_cast<SortOrder>(sortComboBox->currentIndex());
|
||||
SettingsCache::instance().setVisualDeckStorageSortingOrder(sortComboBox->currentIndex());
|
||||
emit sortOrderChanged();
|
||||
}
|
||||
|
||||
void VisualDeckStorageSortWidget::sortFolder(VisualDeckStorageFolderDisplayWidget *folderWidget)
|
||||
{
|
||||
auto children =
|
||||
folderWidget->getFlowWidget()->findChildren<QWidget *>(QString(), Qt::FindChildOption::FindDirectChildrenOnly);
|
||||
for (auto widget : children) {
|
||||
auto deckPreviewWidgets =
|
||||
widget->findChildren<DeckPreviewWidget *>(QString(), Qt::FindChildOption::FindDirectChildrenOnly);
|
||||
auto newOrder = filterFiles(deckPreviewWidgets);
|
||||
for (DeckPreviewWidget *previewWidget : newOrder) {
|
||||
folderWidget->getFlowWidget()->removeWidget(previewWidget);
|
||||
}
|
||||
for (DeckPreviewWidget *previewWidget : newOrder) {
|
||||
folderWidget->getFlowWidget()->addWidget(previewWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<DeckPreviewWidget *> VisualDeckStorageSortWidget::filterFiles(QList<DeckPreviewWidget *> widgets)
|
||||
{
|
||||
// Sort the widgets list based on the current sort order
|
||||
std::sort(widgets.begin(), widgets.end(), [this](DeckPreviewWidget *widget1, DeckPreviewWidget *widget2) {
|
||||
if (!widget1 || !widget2) {
|
||||
return false; // Handle null pointers gracefully
|
||||
}
|
||||
|
||||
QFileInfo info1(widget1->filePath);
|
||||
QFileInfo info2(widget2->filePath);
|
||||
|
||||
switch (sortOrder) {
|
||||
case ByName:
|
||||
return widget1->deckLoader->getName() < widget2->deckLoader->getName();
|
||||
case Alphabetical:
|
||||
return QString::localeAwareCompare(info1.fileName(), info2.fileName()) <= 0;
|
||||
case ByLastModified:
|
||||
return info1.lastModified() > info2.lastModified();
|
||||
case ByLastLoaded: {
|
||||
QDateTime time1 = QDateTime::fromString(widget1->deckLoader->getLastLoadedTimestamp());
|
||||
QDateTime time2 = QDateTime::fromString(widget2->deckLoader->getLastLoadedTimestamp());
|
||||
return time1 > time2;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Default case, no sorting applied
|
||||
});
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef VISUAL_DECK_STORAGE_SORT_WIDGET_H
|
||||
#define VISUAL_DECK_STORAGE_SORT_WIDGET_H
|
||||
|
||||
#include "visual_deck_storage_widget.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDeckStorageWidget;
|
||||
class VisualDeckStorageFolderDisplayWidget;
|
||||
class VisualDeckStorageSortWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VisualDeckStorageSortWidget(VisualDeckStorageWidget *parent);
|
||||
void retranslateUi();
|
||||
void updateSortOrder();
|
||||
void sortFolder(VisualDeckStorageFolderDisplayWidget *folderWidget);
|
||||
QString getSearchText();
|
||||
QList<DeckPreviewWidget *> filterFiles(QList<DeckPreviewWidget *> widgets);
|
||||
|
||||
signals:
|
||||
void sortOrderChanged();
|
||||
|
||||
private:
|
||||
enum SortOrder
|
||||
{
|
||||
ByName,
|
||||
Alphabetical,
|
||||
ByLastModified,
|
||||
ByLastLoaded,
|
||||
};
|
||||
QHBoxLayout *layout;
|
||||
VisualDeckStorageWidget *parent;
|
||||
SortOrder sortOrder; // Current sorting option
|
||||
QComboBox *sortComboBox;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_SORT_WIDGET_H
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
#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, 0, 5, 0);
|
||||
|
||||
setFixedHeight(100);
|
||||
|
||||
auto *flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
||||
layout->addWidget(flowWidget);
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
refreshTags();
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const
|
||||
{
|
||||
QStringList selectedTags;
|
||||
QStringList excludedTags;
|
||||
|
||||
// Collect selected and excluded tags
|
||||
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||
switch (tagWidget->getState()) {
|
||||
case TagState::Selected:
|
||||
selectedTags.append(tagWidget->getTagName());
|
||||
break;
|
||||
case TagState::Excluded:
|
||||
excludedTags.append(tagWidget->getTagName());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no tags are selected or excluded, show all
|
||||
if (selectedTags.isEmpty() && excludedTags.isEmpty()) {
|
||||
for (DeckPreviewWidget *deckPreview : deckPreviews) {
|
||||
deckPreview->filteredByTags = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (DeckPreviewWidget *deckPreview : deckPreviews) {
|
||||
QStringList deckTags = deckPreview->deckLoader->getTags();
|
||||
|
||||
bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(),
|
||||
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
|
||||
|
||||
bool hasAnyExcluded = std::any_of(excludedTags.begin(), excludedTags.end(),
|
||||
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
|
||||
|
||||
// Filter out if any excluded tag is present or if any selected tag is missing
|
||||
deckPreview->filteredByTags = !(hasAllSelected && !hasAnyExcluded);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::refreshTags()
|
||||
{
|
||||
QSet<QString> allTags = gatherAllTags();
|
||||
removeTagsNotInList(allTags);
|
||||
addTagsIfNotPresent(allTags);
|
||||
sortTags();
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::removeTagsNotInList(const QSet<QString> &tags)
|
||||
{
|
||||
auto *flowWidget = findChild<FlowWidget *>();
|
||||
|
||||
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||
const QString &tagName = tagWidget->getTagName();
|
||||
|
||||
// Keep the tag widget if it is either selected or excluded
|
||||
if (!tags.contains(tagName) && tagWidget->getState() == TagState::NotSelected) {
|
||||
flowWidget->removeWidget(tagWidget);
|
||||
tagWidget->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::addTagsIfNotPresent(const QSet<QString> &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;
|
||||
for (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::updateTagFilter);
|
||||
connect(newTagWidget, &DeckPreviewTagDisplayWidget::tagClicked, this,
|
||||
&VisualDeckStorageTagFilterWidget::refreshTags);
|
||||
auto *flowWidget = findChild<FlowWidget *>();
|
||||
flowWidget->addWidget(newTagWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageTagFilterWidget::sortTags()
|
||||
{
|
||||
auto *flowWidget = findChild<FlowWidget *>();
|
||||
if (!flowWidget)
|
||||
return;
|
||||
|
||||
// Get all tag widgets
|
||||
QList<DeckPreviewTagDisplayWidget *> tagWidgets = findChildren<DeckPreviewTagDisplayWidget *>();
|
||||
|
||||
// Sort widgets by tag name
|
||||
std::sort(tagWidgets.begin(), tagWidgets.end(), [](DeckPreviewTagDisplayWidget *a, DeckPreviewTagDisplayWidget *b) {
|
||||
return a->getTagName().toLower() < b->getTagName().toLower();
|
||||
});
|
||||
|
||||
// Clear and re-add widgets in sorted order
|
||||
for (DeckPreviewTagDisplayWidget *tagWidget : tagWidgets) {
|
||||
flowWidget->removeWidget(tagWidget);
|
||||
}
|
||||
for (DeckPreviewTagDisplayWidget *tagWidget : tagWidgets) {
|
||||
flowWidget->addWidget(tagWidget);
|
||||
}
|
||||
}
|
||||
|
||||
QSet<QString> VisualDeckStorageTagFilterWidget::gatherAllTags() const
|
||||
{
|
||||
QSet<QString> allTags;
|
||||
QList<DeckPreviewWidget *> deckWidgets = parent->findChildren<DeckPreviewWidget *>();
|
||||
|
||||
for (DeckPreviewWidget *widget : deckWidgets) {
|
||||
if (widget->checkVisibility()) {
|
||||
for (const QString &tag : widget->deckLoader->getTags()) {
|
||||
allTags.insert(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return allTags;
|
||||
}
|
||||
|
||||
QStringList VisualDeckStorageTagFilterWidget::getAllKnownTags() const
|
||||
{
|
||||
QStringList allTags;
|
||||
|
||||
for (DeckPreviewTagDisplayWidget *tagWidget : findChildren<DeckPreviewTagDisplayWidget *>()) {
|
||||
allTags.append(tagWidget->getTagName());
|
||||
}
|
||||
|
||||
// Remove duplicates by calling 'removeDuplicates'
|
||||
allTags.removeDuplicates();
|
||||
|
||||
return allTags;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#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
|
||||
|
||||
VisualDeckStorageWidget *parent;
|
||||
|
||||
QSet<QString> gatherAllTags() const;
|
||||
void removeTagsNotInList(const QSet<QString> &tags);
|
||||
void addTagsIfNotPresent(const QSet<QString> &tags);
|
||||
void addTagIfNotPresent(const QString &tag);
|
||||
void sortTags();
|
||||
|
||||
public:
|
||||
explicit VisualDeckStorageTagFilterWidget(VisualDeckStorageWidget *_parent);
|
||||
QStringList getAllKnownTags() const;
|
||||
void filterDecksBySelectedTags(const QList<DeckPreviewWidget *> &deckPreviews) const;
|
||||
|
||||
public slots:
|
||||
void refreshTags();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_TAG_FILTER_WIDGET_H
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
#include "visual_deck_storage_widget.h"
|
||||
|
||||
#include "../../../database/card_database_manager.h"
|
||||
#include "../../../settings/cache_settings.h"
|
||||
#include "../quick_settings/settings_button_widget.h"
|
||||
#include "deck_preview/deck_preview_widget.h"
|
||||
#include "visual_deck_storage_folder_display_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>
|
||||
#include <QMouseEvent>
|
||||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(parent), folderWidget(nullptr)
|
||||
{
|
||||
deckListModel = new DeckListModel(this);
|
||||
deckListModel->setObjectName("visualDeckModel");
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(9, 0, 9, 5);
|
||||
setLayout(layout);
|
||||
|
||||
// search bar row
|
||||
searchAndSortContainer = new QWidget(this);
|
||||
searchAndSortLayout = new QHBoxLayout(searchAndSortContainer);
|
||||
searchAndSortLayout->setSpacing(3);
|
||||
searchAndSortLayout->setContentsMargins(9, 0, 9, 0);
|
||||
searchAndSortContainer->setLayout(searchAndSortLayout);
|
||||
|
||||
deckPreviewColorIdentityFilterWidget = new DeckPreviewColorIdentityFilterWidget(this);
|
||||
sortWidget = new VisualDeckStorageSortWidget(this);
|
||||
searchWidget = new VisualDeckStorageSearchWidget(this);
|
||||
|
||||
refreshButton = new QToolButton(this);
|
||||
refreshButton->setIcon(QPixmap("theme:icons/reload"));
|
||||
refreshButton->setFixedSize(32, 32);
|
||||
connect(refreshButton, &QPushButton::clicked, this, &VisualDeckStorageWidget::refreshIfPossible);
|
||||
|
||||
quickSettingsWidget = new VisualDeckStorageQuickSettingsWidget(this);
|
||||
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showFoldersChanged, this,
|
||||
&VisualDeckStorageWidget::updateShowFolders);
|
||||
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showTagFilterChanged, this,
|
||||
&VisualDeckStorageWidget::updateTagsVisibility);
|
||||
|
||||
searchAndSortLayout->addWidget(deckPreviewColorIdentityFilterWidget);
|
||||
searchAndSortLayout->addWidget(sortWidget);
|
||||
searchAndSortLayout->addWidget(searchWidget);
|
||||
searchAndSortLayout->addWidget(refreshButton);
|
||||
searchAndSortLayout->addWidget(quickSettingsWidget);
|
||||
|
||||
// tag filter box
|
||||
tagFilterWidget = new VisualDeckStorageTagFilterWidget(this);
|
||||
updateTagsVisibility(SettingsCache::instance().getVisualDeckStorageShowTagFilter());
|
||||
|
||||
deckPreviewSelectionAnimationEnabled = SettingsCache::instance().getVisualDeckStorageSelectionAnimation();
|
||||
connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageSelectionAnimationChanged, this,
|
||||
&VisualDeckStorageWidget::updateSelectionAnimationEnabled);
|
||||
|
||||
// deck area
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
|
||||
// putting everything together
|
||||
layout->addWidget(searchAndSortContainer);
|
||||
layout->addWidget(tagFilterWidget);
|
||||
layout->addWidget(scrollArea);
|
||||
|
||||
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
|
||||
&VisualDeckStorageWidget::createRootFolderWidget);
|
||||
|
||||
databaseLoadIndicator = new QLabel(this);
|
||||
databaseLoadIndicator->setAlignment(Qt::AlignCenter);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
// Don't waste time processing the cards if they're going to get refreshed anyway once the db finishes loading
|
||||
if (CardDatabaseManager::getInstance()->getLoadStatus() == LoadStatus::Ok) {
|
||||
createRootFolderWidget();
|
||||
databaseLoadIndicator->setVisible(false);
|
||||
} else {
|
||||
scrollArea->setWidget(databaseLoadIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::refreshIfPossible()
|
||||
{
|
||||
if (scrollArea->widget() != databaseLoadIndicator) {
|
||||
createRootFolderWidget();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
if (scrollArea->widget() == folderWidget) {
|
||||
scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width());
|
||||
scrollArea->widget()->adjustSize();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
if (scrollArea->widget() == folderWidget) {
|
||||
scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width());
|
||||
scrollArea->widget()->adjustSize();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::retranslateUi()
|
||||
{
|
||||
databaseLoadIndicator->setText(tr("Loading database ..."));
|
||||
|
||||
refreshButton->setToolTip(tr("Refresh loaded files"));
|
||||
quickSettingsWidget->setToolTip(tr("Visual Deck Storage Settings"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a const pointer to the quick settings so that the values can be accessed.
|
||||
*/
|
||||
const VisualDeckStorageQuickSettingsWidget *VisualDeckStorageWidget::settings() const
|
||||
{
|
||||
return quickSettingsWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reapplies all sort and filter options by calling the appropriate update methods.
|
||||
*/
|
||||
void VisualDeckStorageWidget::reapplySortAndFilters()
|
||||
{
|
||||
updateSortOrder();
|
||||
updateTagFilter();
|
||||
updateColorFilter();
|
||||
updateSearchFilter();
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::createRootFolderWidget()
|
||||
{
|
||||
folderWidget = new VisualDeckStorageFolderDisplayWidget(this, this, SettingsCache::instance().getDeckPath(), false,
|
||||
quickSettingsWidget->getShowFolders());
|
||||
|
||||
scrollArea->setWidget(folderWidget); // this automatically destroys the old folderWidget
|
||||
scrollArea->widget()->setMaximumWidth(scrollArea->viewport()->width());
|
||||
scrollArea->widget()->adjustSize();
|
||||
|
||||
/* We have to schedule a QTimer here so that the sorting logic doesn't try to access widgets that haven't been
|
||||
* processed by the event loop yet. Otherwise, deck sorting will intermittently segfault on some systems.
|
||||
*/
|
||||
QTimer::singleShot(0, this, &VisualDeckStorageWidget::reapplySortAndFilters);
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateShowFolders(bool enabled)
|
||||
{
|
||||
if (folderWidget) {
|
||||
folderWidget->updateShowFolders(enabled);
|
||||
QTimer::singleShot(0, this, &VisualDeckStorageWidget::reapplySortAndFilters);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateSortOrder()
|
||||
{
|
||||
if (folderWidget) {
|
||||
sortWidget->sortFolder(folderWidget);
|
||||
for (VisualDeckStorageFolderDisplayWidget *subFolderWidget :
|
||||
folderWidget->findChildren<VisualDeckStorageFolderDisplayWidget *>()) {
|
||||
sortWidget->sortFolder(subFolderWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateTagFilter()
|
||||
{
|
||||
if (folderWidget) {
|
||||
tagFilterWidget->filterDecksBySelectedTags(folderWidget->findChildren<DeckPreviewWidget *>());
|
||||
folderWidget->updateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateColorFilter()
|
||||
{
|
||||
if (folderWidget) {
|
||||
deckPreviewColorIdentityFilterWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>());
|
||||
folderWidget->updateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateSearchFilter()
|
||||
{
|
||||
if (folderWidget) {
|
||||
searchWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>(), searchWidget->getSearchText());
|
||||
folderWidget->updateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateTagsVisibility(const bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
tagFilterWidget->setVisible(true);
|
||||
|
||||
} else {
|
||||
tagFilterWidget->setHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDeckStorageWidget::updateSelectionAnimationEnabled(const bool enabled)
|
||||
{
|
||||
deckPreviewSelectionAnimationEnabled = enabled;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef VISUAL_DECK_STORAGE_WIDGET_H
|
||||
#define VISUAL_DECK_STORAGE_WIDGET_H
|
||||
|
||||
#include "../../../deck/deck_list_model.h"
|
||||
#include "../cards/card_size_widget.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
#include "../quick_settings/settings_button_widget.h"
|
||||
#include "deck_preview/deck_preview_color_identity_filter_widget.h"
|
||||
#include "deck_preview/deck_preview_widget.h"
|
||||
#include "visual_deck_storage_folder_display_widget.h"
|
||||
#include "visual_deck_storage_quick_settings_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 <QCheckBox>
|
||||
#include <QFileSystemModel>
|
||||
|
||||
class QSpinBox;
|
||||
class VisualDeckStorageSearchWidget;
|
||||
class VisualDeckStorageSortWidget;
|
||||
class VisualDeckStorageTagFilterWidget;
|
||||
class VisualDeckStorageFolderDisplayWidget;
|
||||
class DeckPreviewColorIdentityFilterWidget;
|
||||
class VisualDeckStorageWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDeckStorageWidget(QWidget *parent);
|
||||
void refreshIfPossible();
|
||||
void retranslateUi();
|
||||
|
||||
VisualDeckStorageTagFilterWidget *tagFilterWidget;
|
||||
bool deckPreviewSelectionAnimationEnabled;
|
||||
|
||||
const VisualDeckStorageQuickSettingsWidget *settings() const;
|
||||
|
||||
public slots:
|
||||
void createRootFolderWidget(); // Refresh the display of cards based on the current sorting option
|
||||
void updateShowFolders(bool enabled);
|
||||
void updateTagFilter();
|
||||
void updateColorFilter();
|
||||
void updateSearchFilter();
|
||||
void updateTagsVisibility(bool visible);
|
||||
void updateSelectionAnimationEnabled(bool enabled);
|
||||
void updateSortOrder();
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
signals:
|
||||
void bannerCardsRefreshed();
|
||||
void deckLoadRequested(const QString &filePath);
|
||||
void openDeckEditor(const DeckLoader *deck);
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
QWidget *searchAndSortContainer;
|
||||
QHBoxLayout *searchAndSortLayout;
|
||||
DeckListModel *deckListModel;
|
||||
QLabel *databaseLoadIndicator;
|
||||
VisualDeckStorageSortWidget *sortWidget;
|
||||
VisualDeckStorageSearchWidget *searchWidget;
|
||||
DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget;
|
||||
QToolButton *refreshButton;
|
||||
VisualDeckStorageQuickSettingsWidget *quickSettingsWidget;
|
||||
QScrollArea *scrollArea;
|
||||
VisualDeckStorageFolderDisplayWidget *folderWidget;
|
||||
|
||||
void reapplySortAndFilters();
|
||||
};
|
||||
|
||||
#endif // VISUAL_DECK_STORAGE_WIDGET_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue