From 4906c9dc9402d4db14bd56adb26e62f80832dbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Sun, 16 Mar 2025 12:16:09 +0100 Subject: [PATCH] Add a new dialog that allows editing the default suggested tags. --- .../deck_preview/deck_preview_tag_dialog.cpp | 120 +++++--------- .../deck_preview/deck_preview_tag_dialog.h | 5 +- .../src/dialogs/dlg_default_tags_editor.cpp | 155 ++++++++++++++++++ .../src/dialogs/dlg_default_tags_editor.h | 32 ++++ cockatrice/src/settings/cache_settings.cpp | 9 + cockatrice/src/settings/cache_settings.h | 87 ++++++++++ dbconverter/src/mocks.cpp | 3 + tests/carddatabase/mocks.cpp | 3 + 8 files changed, 331 insertions(+), 83 deletions(-) create mode 100644 cockatrice/src/dialogs/dlg_default_tags_editor.cpp create mode 100644 cockatrice/src/dialogs/dlg_default_tags_editor.h diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp index 4d844f6f8..09cd1444c 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp @@ -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 @@ -11,89 +13,11 @@ #include DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags, const QStringList &activeTags, QWidget *parent) - : QDialog(parent), activeTags(activeTags) + : 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 +52,17 @@ 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 defaultTagsEditor = new DlgDefaultTagsEditor(nullptr); + defaultTagsEditor->exec(); + }); addTagLayout->addWidget(newTagInput); addTagLayout->addWidget(addTagButton); + addTagLayout->addWidget(editButton); mainLayout->addLayout(addTagLayout); connect(addTagButton, &QPushButton::clicked, this, &DeckPreviewTagDialog::addTag); @@ -144,6 +73,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 +81,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 +96,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; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.h index 9d6b5792b..52e51c2d1 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.h @@ -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 activeTags_; + QStringList knownTags_; }; #endif // DECK_PREVIEW_TAG_DIALOG_H diff --git a/cockatrice/src/dialogs/dlg_default_tags_editor.cpp b/cockatrice/src/dialogs/dlg_default_tags_editor.cpp new file mode 100644 index 000000000..e5826c05c --- /dev/null +++ b/cockatrice/src/dialogs/dlg_default_tags_editor.cpp @@ -0,0 +1,155 @@ +#include "dlg_default_tags_editor.h" + +#include "../settings/cache_settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +DlgDefaultTagsEditor::DlgDefaultTagsEditor(QWidget *parent) : QDialog(parent) +{ + setWindowModality(Qt::WindowModal); // Steals focus from parent dialog + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + // Input field + Add button (horizontal layout) + QHBoxLayout *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) + QHBoxLayout *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(listWidget); + QWidget *widget = new QWidget(this); + QHBoxLayout *layout = new QHBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + + // Editable tag label + QLineEdit *lineEdit = new QLineEdit(tag, this); + lineEdit->setFrame(false); + lineEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + layout->addWidget(lineEdit); + + // Delete button + QPushButton *deleteButton = new QPushButton(tr("✖"), this); + deleteButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + deleteButton->setFixedWidth(25); + layout->addSpacing(5); // Adds spacing before the delete button + layout->addWidget(deleteButton); + + widget->setLayout(layout); + listWidget->setItemWidget(item, widget); + + // Delete button signal + 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(); + 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(); + if (lineEdit) { + updatedList.append(lineEdit->text()); + } + } + SettingsCache::instance().setVisualDeckStorageDefaultTagsList(updatedList); + accept(); // Close dialog and confirm changes +} diff --git a/cockatrice/src/dialogs/dlg_default_tags_editor.h b/cockatrice/src/dialogs/dlg_default_tags_editor.h new file mode 100644 index 000000000..2307122fe --- /dev/null +++ b/cockatrice/src/dialogs/dlg_default_tags_editor.h @@ -0,0 +1,32 @@ +#ifndef DLG_DEFAULT_TAGS_EDITOR_H +#define DLG_DEFAULT_TAGS_EDITOR_H + +#include +#include +#include +#include +#include + +class DlgDefaultTagsEditor : public QDialog { + Q_OBJECT + +public: + explicit DlgDefaultTagsEditor(QWidget *parent = nullptr); + + private slots: + void addItem(); + void deleteItem(QListWidgetItem *item); + void confirmChanges(); + +private: + QListWidget *listWidget; + QLineEdit *inputField; + QPushButton *addButton; + QPushButton *confirmButton; + QPushButton *cancelButton; + + void loadStringList(); + void retranslateUi(); +}; + +#endif //DLG_DEFAULT_TAGS_EDITOR_H diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index acd8cd581..b2bfbf9f8 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -271,6 +271,8 @@ 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(); @@ -731,6 +733,13 @@ 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; diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index f0861af58..3fa04c094 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -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 SettingsCache : public QObject @@ -64,6 +144,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(); @@ -146,6 +227,7 @@ private: bool visualDeckStorageShowBannerCardComboBox; bool visualDeckStorageShowTagsOnDeckPreviews; bool visualDeckStorageShowTagFilter; + QStringList visualDeckStorageDefaultTagsList; bool visualDeckStorageSearchFolderNames; int visualDeckStorageCardSize; bool visualDeckStorageDrawUnusedColorIdentities; @@ -453,6 +535,10 @@ public: { return visualDeckStorageShowTagFilter; } + QStringList getVisualDeckStorageDefaultTagsList() const + { + return visualDeckStorageDefaultTagsList; + } bool getVisualDeckStorageSearchFolderNames() const { return visualDeckStorageSearchFolderNames; @@ -851,6 +937,7 @@ 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); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index acd199759..0eebe7db6 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -227,6 +227,9 @@ 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 */) { } diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index 8ac925c0b..f3a12e733 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -231,6 +231,9 @@ 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 */) { }