Add a new dialog that allows editing the default suggested tags. (#5979)

* Add a new dialog that allows editing the default suggested tags.

* Lint.

* Actually hand linting, lol.

* Fix Build

* Add dialog.

* Use show() instead of exec(), properly size hint list item widgets.

* Fix... something to do with the build?

* Cast to abstract tab deck editor instead of regular.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ZeldaZach <zahalpern+github@gmail.com>
This commit is contained in:
BruebachL
2025-06-13 15:16:57 +02:00
committed by GitHub
parent da2488f7d8
commit c388cee1fe
10 changed files with 363 additions and 87 deletions

View File

@@ -153,6 +153,7 @@ set(cockatrice_SOURCES
src/dialogs/dlg_convert_deck_to_cod_format.cpp
src/dialogs/dlg_create_game.cpp
src/dialogs/dlg_create_token.cpp
src/dialogs/dlg_default_tags_editor.cpp
src/dialogs/dlg_edit_avatar.cpp
src/dialogs/dlg_edit_password.cpp
src/dialogs/dlg_edit_tokens.cpp

View File

@@ -154,16 +154,17 @@ void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
}
}
} else if (parentWidget()) {
// If we're the child of a TabDeckEditor, we are buried under a ton of childWidgets in the DeckInfoDock.
// 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<TabDeckEditor *>(currentParent)) {
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
break;
}
currentParent = currentParent->parentWidget();
}
if (qobject_cast<TabDeckEditor *>(currentParent)) {
auto *deckEditor = qobject_cast<TabDeckEditor *>(currentParent);
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
auto *deckEditor = qobject_cast<AbstractTabDeckEditor *>(currentParent);
QStringList knownTags;
QStringList allFiles = getAllFiles(SettingsCache::instance().getDeckPath());
DeckLoader loader;

View File

@@ -1,5 +1,7 @@
#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>
@@ -8,92 +10,17 @@
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const QStringList &activeTags, QWidget *parent)
: QDialog(parent), activeTags(activeTags)
DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags,
const QStringList &_activeTags,
QWidget *parent)
: QDialog(parent), activeTags(_activeTags), knownTags_(knownTags)
{
resize(400, 500);
QStringList defaultTags = {
// Strategies
"🏃️ Aggro",
"🧙‍️ Control",
"⚔️ Midrange",
"🌀 Combo",
"🪓 Mill",
"🔒 Stax",
"🗺️ Landfall",
"🛡️ Pillowfort",
"🌱 Ramp",
"⚡ Storm",
"💀 Aristocrats",
"☠️ Reanimator",
"👹 Sacrifice",
"🔥 Burn",
"🌟 Lifegain",
"🔮 Spellslinger",
"👥 Tokens",
"🎭 Blink",
"⏳ Time Manipulation",
"🌍 Domain",
"💫 Proliferate",
"📜 Saga",
"🎲 Chaos",
"🪄 Auras",
"🔫 Pingers",
// Themes
"👑 Monarch",
"🚀 Vehicles",
"💉 Infect",
"🩸 Madness",
"🌀 Morph",
// Card Types
"⚔️ Creature",
"💎 Artifact",
"🌔 Enchantment",
"📖 Sorcery",
"⚡ Instant",
"🌌 Planeswalker",
"🌏 Land",
"🪄 Aura",
// Kindred Types
"🐉 Kindred",
"🧙 Humans",
"⚔️ Soldiers",
"🛡️ Knights",
"🎻 Bards",
"🧝 Elves",
"🌲 Dryads",
"😇 Angels",
"🎩 Wizards",
"🧛 Vampires",
"🦴 Skeletons",
"💀 Zombies",
"👹 Demons",
"👾 Eldrazi",
"🐉 Dragons",
"🐠 Merfolk",
"🦁 Cats",
"🐺 Wolves",
"🐺 Werewolves",
"🦇 Bats",
"🐀 Rats",
"🦅 Birds",
"🦗 Insects",
"🍄 Fungus",
"🐚 Sea Creatures",
"🐗 Boars",
"🦊 Foxes",
"🦄 Unicorns",
"🐘 Elephants",
"🐻 Bears",
"🦏 Rhinos",
"🦂 Scorpions",
};
QStringList defaultTags = SettingsCache::instance().getVisualDeckStorageDefaultTagsList();
// Merge knownTags with defaultTags, ensuring no duplicates
QStringList combinedTags = defaultTags + knownTags + activeTags;
@@ -128,12 +55,23 @@ DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const Q
}
// Add tag input layout
addTagLayout = new QHBoxLayout(this);
auto *addTagLayout = new QHBoxLayout();
newTagInput = new QLineEdit(this);
addTagButton = new QPushButton(this);
addTagButton->setEnabled(false);
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);
@@ -144,6 +82,7 @@ DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const Q
buttonLayout = new QHBoxLayout(this);
okButton = new QPushButton(this);
cancelButton = new QPushButton(this);
buttonLayout->addStretch();
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
@@ -151,6 +90,10 @@ DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const Q
connect(okButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::accept);
connect(cancelButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::reject);
connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageDefaultTagsListChanged, this,
&DeckPreviewTagDialog::refreshTagList);
retranslateUi();
}
@@ -162,9 +105,31 @@ void DeckPreviewTagDialog::retranslateUi()
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;

View File

@@ -25,6 +25,7 @@ private slots:
void addTag();
void onCheckboxStateChanged();
void retranslateUi();
void refreshTagList();
private:
QVBoxLayout *mainLayout;
@@ -36,8 +37,10 @@ private:
QPushButton *addTagButton;
QHBoxLayout *buttonLayout;
QPushButton *okButton;
QPushButton *editButton;
QPushButton *cancelButton;
QStringList activeTags;
QStringList knownTags_;
};
#endif // DECK_PREVIEW_TAG_DIALOG_H

View File

@@ -0,0 +1,149 @@
#include "dlg_default_tags_editor.h"
#include "../settings/cache_settings.h"
#include <QMessageBox>
#include <QVBoxLayout>
DlgDefaultTagsEditor::DlgDefaultTagsEditor(QWidget *parent) : QDialog(parent)
{
mainLayout = new QVBoxLayout(this);
// Input field + Add button (horizontal layout)
inputLayout = new QHBoxLayout();
inputField = new QLineEdit(this);
addButton = new QPushButton(this);
inputLayout->addWidget(inputField);
inputLayout->addWidget(addButton);
mainLayout->addLayout(inputLayout);
// List widget for tags
listWidget = new QListWidget(this);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
listWidget->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
mainLayout->addWidget(listWidget);
// Button layout (Confirm and Cancel)
buttonLayout = new QHBoxLayout();
confirmButton = new QPushButton(this);
cancelButton = new QPushButton(this);
buttonLayout->addWidget(confirmButton);
buttonLayout->addWidget(cancelButton);
mainLayout->addLayout(buttonLayout);
loadStringList();
retranslateUi();
// Connect signals
connect(addButton, &QPushButton::clicked, this, &DlgDefaultTagsEditor::addItem);
connect(cancelButton, &QPushButton::clicked, this, &DlgDefaultTagsEditor::reject); // Cancel closes dialog
connect(confirmButton, &QPushButton::clicked, this, &DlgDefaultTagsEditor::confirmChanges);
connect(inputField, &QLineEdit::returnPressed, this, &DlgDefaultTagsEditor::addItem);
}
void DlgDefaultTagsEditor::retranslateUi()
{
setWindowTitle(tr("Edit Tags"));
addButton->setText(tr("Add"));
confirmButton->setText(tr("Confirm"));
cancelButton->setText(tr("Cancel"));
inputField->setPlaceholderText(tr("Enter a tag and press Enter"));
}
void DlgDefaultTagsEditor::loadStringList()
{
listWidget->clear();
QStringList tags = SettingsCache::instance().getVisualDeckStorageDefaultTagsList();
for (const QString &tag : tags) {
auto *item = new QListWidgetItem(); // Create item but don't insert yet
QWidget *widget = new QWidget(this);
QHBoxLayout *layout = new QHBoxLayout(widget);
layout->setContentsMargins(2, 2, 2, 2);
layout->setSpacing(5);
QLineEdit *lineEdit = new QLineEdit(tag, this);
lineEdit->setFrame(false);
lineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
layout->addWidget(lineEdit);
QPushButton *deleteButton = new QPushButton(tr(""), this);
deleteButton->setFixedSize(25, 25);
layout->addWidget(deleteButton);
widget->setLayout(layout);
// Set item height explicitly to match the widget (widgets get squished without it)
item->setSizeHint(widget->sizeHint());
listWidget->addItem(item);
listWidget->setItemWidget(item, widget);
connect(deleteButton, &QPushButton::clicked, this, [this, item]() { deleteItem(item); });
}
}
void DlgDefaultTagsEditor::addItem()
{
QString newTag = inputField->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 < listWidget->count(); ++i) {
QWidget *widget = listWidget->itemWidget(listWidget->item(i));
if (!widget)
continue;
QLineEdit *lineEdit = widget->findChild<QLineEdit *>();
if (lineEdit && lineEdit->text() == newTag) {
QMessageBox::warning(this, tr("Duplicate Tag"), tr("This tag already exists."));
return;
}
}
// Add new tag
auto *item = new QListWidgetItem(listWidget);
QWidget *widget = new QWidget(this);
QHBoxLayout *layout = new QHBoxLayout(widget);
layout->setContentsMargins(0, 0, 0, 0);
// Editable tag label
QLineEdit *lineEdit = new QLineEdit(newTag, this);
lineEdit->setFrame(false);
layout->addWidget(lineEdit);
// Delete button
QPushButton *deleteButton = new QPushButton(tr(""), this);
deleteButton->setFixedSize(24, 24);
layout->addWidget(deleteButton);
widget->setLayout(layout);
listWidget->setItemWidget(item, widget);
connect(deleteButton, &QPushButton::clicked, this, [this, item]() { deleteItem(item); });
inputField->clear();
}
void DlgDefaultTagsEditor::deleteItem(QListWidgetItem *item)
{
delete listWidget->takeItem(listWidget->row(item));
}
void DlgDefaultTagsEditor::confirmChanges()
{
QStringList updatedList;
for (int i = 0; i < listWidget->count(); ++i) {
QWidget *widget = listWidget->itemWidget(listWidget->item(i));
if (!widget)
continue;
QLineEdit *lineEdit = widget->findChild<QLineEdit *>();
if (lineEdit) {
updatedList.append(lineEdit->text());
}
}
SettingsCache::instance().setVisualDeckStorageDefaultTagsList(updatedList);
accept(); // Close dialog and confirm changes
}

View File

@@ -0,0 +1,36 @@
#ifndef DLG_DEFAULT_TAGS_EDITOR_H
#define DLG_DEFAULT_TAGS_EDITOR_H
#include <QDialog>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QVBoxLayout>
class DlgDefaultTagsEditor : public QDialog
{
Q_OBJECT
public:
explicit DlgDefaultTagsEditor(QWidget *parent = nullptr);
private slots:
void addItem();
void deleteItem(QListWidgetItem *item);
void confirmChanges();
private:
QVBoxLayout *mainLayout;
QHBoxLayout *inputLayout;
QListWidget *listWidget;
QHBoxLayout *buttonLayout;
QLineEdit *inputField;
QPushButton *addButton;
QPushButton *confirmButton;
QPushButton *cancelButton;
void loadStringList();
void retranslateUi();
};
#endif // DLG_DEFAULT_TAGS_EDITOR_H

View File

@@ -276,6 +276,9 @@ SettingsCache::SettingsCache()
visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt();
visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool();
visualDeckStorageShowTagFilter = settings->value("interface/visualdeckstorageshowtagfilter", true).toBool();
visualDeckStorageDefaultTagsList =
settings->value("interface/visualdeckstoragedefaulttagslist", defaultTags).toStringList();
visualDeckStorageSearchFolderNames = settings->value("interface/visualdeckstoragesearchfoldernames", true).toBool();
visualDeckStorageShowBannerCardComboBox =
settings->value("interface/visualdeckstorageshowbannercardcombobox", true).toBool();
visualDeckStorageShowTagsOnDeckPreviews =
@@ -742,6 +745,19 @@ void SettingsCache::setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T _showTa
emit visualDeckStorageShowTagFilterChanged(visualDeckStorageShowTagFilter);
}
void SettingsCache::setVisualDeckStorageDefaultTagsList(QStringList _defaultTagsList)
{
visualDeckStorageDefaultTagsList = _defaultTagsList;
settings->setValue("interface/visualdeckstoragedefaulttagslist", visualDeckStorageDefaultTagsList);
emit visualDeckStorageDefaultTagsListChanged();
}
void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T value)
{
visualDeckStorageSearchFolderNames = value;
settings->setValue("interface/visualdeckstoragesearchfoldernames", visualDeckStorageSearchFolderNames);
}
void SettingsCache::setVisualDeckStorageShowBannerCardComboBox(QT_STATE_CHANGED_T _showBannerCardComboBox)
{
visualDeckStorageShowBannerCardComboBox = _showBannerCardComboBox;

View File

@@ -42,6 +42,86 @@ constexpr int NETWORK_CACHE_SIZE_MAX = 1024 * 1024; // 1 TB
#define DEFAULT_FONT_SIZE 12
inline QStringList defaultTags = {
// Strategies
"🏃️ Aggro",
"🧙‍️ Control",
"⚔️ Midrange",
"🌀 Combo",
"🪓 Mill",
"🔒 Stax",
"🗺️ Landfall",
"🛡️ Pillowfort",
"🌱 Ramp",
"⚡ Storm",
"💀 Aristocrats",
"☠️ Reanimator",
"👹 Sacrifice",
"🔥 Burn",
"🌟 Lifegain",
"🔮 Spellslinger",
"👥 Tokens",
"🎭 Blink",
"⏳ Time Manipulation",
"🌍 Domain",
"💫 Proliferate",
"📜 Saga",
"🎲 Chaos",
"🪄 Auras",
"🔫 Pingers",
// Themes
"👑 Monarch",
"🚀 Vehicles",
"💉 Infect",
"🩸 Madness",
"🌀 Morph",
// Card Types
"⚔️ Creature",
"💎 Artifact",
"🌔 Enchantment",
"📖 Sorcery",
"⚡ Instant",
"🌌 Planeswalker",
"🌏 Land",
"🪄 Aura",
// Kindred Types
"🐉 Kindred",
"🧙 Humans",
"⚔️ Soldiers",
"🛡️ Knights",
"🎻 Bards",
"🧝 Elves",
"🌲 Dryads",
"😇 Angels",
"🎩 Wizards",
"🧛 Vampires",
"🦴 Skeletons",
"💀 Zombies",
"👹 Demons",
"👾 Eldrazi",
"🐉 Dragons",
"🐠 Merfolk",
"🦁 Cats",
"🐺 Wolves",
"🐺 Werewolves",
"🦇 Bats",
"🐀 Rats",
"🦅 Birds",
"🦗 Insects",
"🍄 Fungus",
"🐚 Sea Creatures",
"🐗 Boars",
"🦊 Foxes",
"🦄 Unicorns",
"🐘 Elephants",
"🐻 Bears",
"🦏 Rhinos",
"🦂 Scorpions",
};
class QSettings;
class CardCounterSettings;
@@ -66,6 +146,7 @@ signals:
void deckEditorBannerCardComboBoxVisibleChanged(bool _visible);
void deckEditorTagsWidgetVisibleChanged(bool _visible);
void visualDeckStorageShowTagFilterChanged(bool _visible);
void visualDeckStorageDefaultTagsListChanged();
void visualDeckStorageShowBannerCardComboBoxChanged(bool _visible);
void visualDeckStorageShowTagsOnDeckPreviewsChanged(bool _visible);
void visualDeckStorageCardSizeChanged();
@@ -149,6 +230,8 @@ private:
bool visualDeckStorageShowBannerCardComboBox;
bool visualDeckStorageShowTagsOnDeckPreviews;
bool visualDeckStorageShowTagFilter;
QStringList visualDeckStorageDefaultTagsList;
bool visualDeckStorageSearchFolderNames;
int visualDeckStorageCardSize;
bool visualDeckStorageDrawUnusedColorIdentities;
int visualDeckStorageUnusedColorIdentitiesOpacity;
@@ -460,6 +543,14 @@ public:
{
return visualDeckStorageShowTagFilter;
}
QStringList getVisualDeckStorageDefaultTagsList() const
{
return visualDeckStorageDefaultTagsList;
}
bool getVisualDeckStorageSearchFolderNames() const
{
return visualDeckStorageSearchFolderNames;
}
bool getVisualDeckStorageShowBannerCardComboBox() const
{
return visualDeckStorageShowBannerCardComboBox;
@@ -857,6 +948,8 @@ public slots:
void setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder);
void setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T value);
void setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T _showTags);
void setVisualDeckStorageDefaultTagsList(QStringList _defaultTagsList);
void setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T value);
void setVisualDeckStorageShowBannerCardComboBox(QT_STATE_CHANGED_T _showBannerCardComboBox);
void setVisualDeckStorageShowTagsOnDeckPreviews(QT_STATE_CHANGED_T _showTags);
void setVisualDeckStorageCardSize(int _visualDeckStorageCardSize);

View File

@@ -230,6 +230,12 @@ void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T /* value
void SettingsCache::setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T /* _showTags */)
{
}
void SettingsCache::setVisualDeckStorageDefaultTagsList(QStringList /* _defaultTagsList */)
{
}
void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T /* value */)
{
}
void SettingsCache::setVisualDeckStorageShowBannerCardComboBox(QT_STATE_CHANGED_T /* _showBannerCardComboBox */)
{
}

View File

@@ -234,6 +234,12 @@ void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T /* value
void SettingsCache::setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T /* _showTags */)
{
}
void SettingsCache::setVisualDeckStorageDefaultTagsList(QStringList /* _defaultTagsList */)
{
}
void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T /* value */)
{
}
void SettingsCache::setVisualDeckStorageShowBannerCardComboBox(QT_STATE_CHANGED_T /* _showBannerCardComboBox */)
{
}