From 1b65818cabf43dd1a79963ce6b920a55afdddf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Wed, 26 Feb 2025 09:07:20 +0100 Subject: [PATCH] Allow filters to be saved and restored. --- .../tab_deck_editor_visual.cpp | 8 ++ .../tab_deck_editor_visual.h | 1 + .../tab_deck_editor_visual_tab_widget.cpp | 3 +- ...l_database_display_color_filter_widget.cpp | 1 + ...tabase_display_filter_save_load_widget.cpp | 124 ++++++++++++++++++ ...database_display_filter_save_load_widget.h | 42 ++++++ ...al_database_display_name_filter_widget.cpp | 51 ++++++- ...sual_database_display_name_filter_widget.h | 10 +- .../visual_database_display_widget.cpp | 11 +- .../visual_database_display_widget.h | 3 + 10 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp create mode 100644 cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.h diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index 26422d702..44d7bc246 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -368,6 +368,14 @@ void TabDeckEditorVisual::actLoadDeck() deckDockWidget->updateBannerCardComboBox(); } +void TabDeckEditorVisual::actLoadDeckFromClipboard() +{ + TabGenericDeckEditor::actLoadDeckFromClipboard(); + tabContainer->visualDeckView->updateDisplay(); + tabContainer->deckAnalytics->refreshDisplays(deckDockWidget->deckModel); + tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel); +} + void TabDeckEditorVisual::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation) { DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName); diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h index 8ed8a085e..67c54d660 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h @@ -12,6 +12,7 @@ class TabDeckEditorVisual : public TabGenericDeckEditor protected slots: void actNewDeck() override; void actLoadDeck() override; + void actLoadDeckFromClipboard() override; void loadLayout() override; void restartLayout() override; diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp index 5fb68cac9..ee0898301 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp @@ -27,7 +27,8 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, connect(visualDeckView, SIGNAL(sideboardCardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, SLOT(onSideboardCardClickedDeckEditor(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *))); - visualDatabaseDisplay = new VisualDatabaseDisplayWidget(this, cardDatabaseModel, cardDatabaseDisplayModel); + visualDatabaseDisplay = + new VisualDatabaseDisplayWidget(this, deckEditor, cardDatabaseModel, cardDatabaseDisplayModel); visualDatabaseDisplay->setObjectName("visualDatabaseView"); connect(visualDatabaseDisplay, SIGNAL(cardHoveredDatabaseDisplay(CardInfoPtr)), this, SLOT(onCardChangedDatabaseDisplay(CardInfoPtr))); diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp index fe5efbfa7..358ba1b90 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp @@ -3,6 +3,7 @@ #include "../../../../game/filters/filter_card.h" #include "../../../../game/filters/filter_tree_model.h" +#include #include #include diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp new file mode 100644 index 000000000..62c2efc4a --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp @@ -0,0 +1,124 @@ +#include "visual_database_display_filter_save_load_widget.h" + +#include "../../../../game/filters/filter_tree.h" + +#include +#include +#include + +VisualDatabaseDisplayFilterSaveLoadWidget::VisualDatabaseDisplayFilterSaveLoadWidget(QWidget *parent, + FilterTreeModel *_filterModel, + const QString &savePath) + : QWidget(parent), filterModel(_filterModel), saveDirectory(savePath) +{ + setMinimumWidth(300); + setMaximumHeight(300); + + layout = new QVBoxLayout(this); + setLayout(layout); + + // Input for filter filename + filenameInput = new QLineEdit(this); + filenameInput->setPlaceholderText(tr("Enter filename...")); + layout->addWidget(filenameInput); + + // Save button + saveButton = new QPushButton(tr("Save Filter"), this); + layout->addWidget(saveButton); + connect(saveButton, &QPushButton::clicked, this, &VisualDatabaseDisplayFilterSaveLoadWidget::saveFilter); + + // File list container + fileListWidget = new FlowWidget(this, Qt::Vertical, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); + layout->addWidget(fileListWidget); + + refreshFilterList(); // Populate the file list on startup +} + +void VisualDatabaseDisplayFilterSaveLoadWidget::saveFilter() +{ + QString filename = filenameInput->text().trimmed(); + if (filename.isEmpty()) + return; + + QString filePath = saveDirectory + "/" + filename + ".json"; + + // Serialize the filter model to JSON + QJsonArray filtersArray; + QList cardFilters = filterModel->allFilters(); + + for (const CardFilter *filter : cardFilters) { + filtersArray.append(filter->toJson()); // Assuming toJson() returns a QJsonObject + } + + QJsonObject root; + root["filters"] = filtersArray; + + QFile file(filePath); + if (file.open(QIODevice::WriteOnly)) { + file.write(QJsonDocument(root).toJson(QJsonDocument::Indented)); + file.close(); + } + + filenameInput->clear(); + refreshFilterList(); // Update the file list +} + +void VisualDatabaseDisplayFilterSaveLoadWidget::loadFilter(const QString &filename) +{ + QString filePath = saveDirectory + "/" + filename; + + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) + return; + + QByteArray jsonData = file.readAll(); + file.close(); + + QJsonDocument doc = QJsonDocument::fromJson(jsonData); + if (!doc.isObject()) + return; + + QJsonObject root = doc.object(); + if (!root.contains("filters") || !root["filters"].isArray()) + return; + + QJsonArray filtersArray = root["filters"].toArray(); + + filterModel->clear(); + + filterModel->blockSignals(true); + filterModel->filterTree()->blockSignals(true); + for (const QJsonValue &value : filtersArray) { + if (value.isObject()) { + QJsonObject filterObj = value.toObject(); + CardFilter *filter = CardFilter::fromJson(filterObj); + if (filter) + filterModel->addFilter(filter); + } + } + filterModel->blockSignals(false); + filterModel->filterTree()->blockSignals(false); + + filterModel->filterTree()->nodeAt(0)->nodeChanged(); + emit filterModel->layoutChanged(); +} + +void VisualDatabaseDisplayFilterSaveLoadWidget::refreshFilterList() +{ + // Clear existing file buttons + for (auto button : fileButtons) { + delete button; + } + fileButtons.clear(); + + QDir dir(saveDirectory); + QStringList filterFiles = dir.entryList(QStringList() << "*.json", QDir::Files, QDir::Time); + + for (const QString &filename : filterFiles) { + auto *button = new QPushButton(filename, fileListWidget); + fileListWidget->addWidget(button); + fileButtons[filename] = button; + + connect(button, &QPushButton::clicked, this, [this, filename]() { loadFilter(filename); }); + } +} diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.h new file mode 100644 index 000000000..d76602252 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.h @@ -0,0 +1,42 @@ +#ifndef VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H +#define VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H + +#include "../../../../game/filters/filter_tree_model.h" +#include "../general/layout_containers/flow_widget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class VisualDatabaseDisplayFilterSaveLoadWidget : public QWidget +{ + Q_OBJECT +public: + explicit VisualDatabaseDisplayFilterSaveLoadWidget(QWidget *parent, + FilterTreeModel *filterModel, + const QString &savePath); + + void saveFilter(); + void loadFilter(const QString &filename); + void refreshFilterList(); + +private: + FilterTreeModel *filterModel; + QString saveDirectory; + + QVBoxLayout *layout; + QLineEdit *filenameInput; + QPushButton *saveButton; + FlowWidget *fileListWidget; + + QMap fileButtons; +}; + +#endif // VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp index 83bd38b42..d4618c3d3 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp @@ -1,10 +1,13 @@ #include "visual_database_display_name_filter_widget.h" +#include "../../../tabs/tab_generic_deck_editor.h" + #include VisualDatabaseDisplayNameFilterWidget::VisualDatabaseDisplayNameFilterWidget(QWidget *parent, + TabGenericDeckEditor *_deckEditor, FilterTreeModel *_filterModel) - : QWidget(parent), filterModel(_filterModel) + : QWidget(parent), deckEditor(_deckEditor), filterModel(_filterModel) { setMinimumWidth(300); setMaximumHeight(300); @@ -22,12 +25,46 @@ VisualDatabaseDisplayNameFilterWidget::VisualDatabaseDisplayNameFilterWidget(QWi if (!text.isEmpty() && !activeFilters.contains(text)) { createNameFilter(text); searchBox->clear(); + updateFilterModel(); } }); // Create container for active filters flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); layout->addWidget(flowWidget); + + loadFromDeckButton = new QPushButton(this); + loadFromDeckButton->setText(tr("Load from Deck")); + layout->addWidget(loadFromDeckButton); + + connect(loadFromDeckButton, &QPushButton::clicked, this, &VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck); +} + +void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck() +{ + DeckListModel *deck_list_model = deckEditor->deckDockWidget->deckModel; + + if (!deck_list_model) + return; + DeckList *decklist = deck_list_model->getDeckList(); + if (!decklist) + return; + InnerDecklistNode *listRoot = decklist->getRoot(); + if (!listRoot) + return; + + for (int i = 0; i < listRoot->size(); i++) { + InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); + if (!currentZone) + continue; + for (int j = 0; j < currentZone->size(); j++) { + DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); + if (!currentCard) + continue; + createNameFilter(currentCard->getName()); + } + } + updateFilterModel(); } void VisualDatabaseDisplayNameFilterWidget::createNameFilter(const QString &name) @@ -44,8 +81,6 @@ void VisualDatabaseDisplayNameFilterWidget::createNameFilter(const QString &name flowWidget->addWidget(button); activeFilters[name] = button; - - updateFilterModel(); } void VisualDatabaseDisplayNameFilterWidget::removeNameFilter(const QString &name) @@ -60,10 +95,20 @@ void VisualDatabaseDisplayNameFilterWidget::removeNameFilter(const QString &name void VisualDatabaseDisplayNameFilterWidget::updateFilterModel() { // Clear existing name filters + emit filterModel->layoutAboutToBeChanged(); filterModel->clearFiltersOfType(CardFilter::Attr::AttrName); + filterModel->blockSignals(true); + filterModel->filterTree()->blockSignals(true); + for (const auto &name : activeFilters.keys()) { QString nameString = name; filterModel->addFilter(new CardFilter(nameString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrName)); } + + filterModel->blockSignals(false); + filterModel->filterTree()->blockSignals(false); + + filterModel->filterTree()->nodeAt(0)->nodeChanged(); + emit filterModel->layoutChanged(); } diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.h index 6ef632b07..c524fad99 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.h @@ -2,6 +2,7 @@ #define VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H #include "../../../../game/filters/filter_tree_model.h" +#include "../../../tabs/tab_generic_deck_editor.h" #include "../general/layout_containers/flow_widget.h" #include @@ -14,7 +15,9 @@ class VisualDatabaseDisplayNameFilterWidget : public QWidget { Q_OBJECT public: - explicit VisualDatabaseDisplayNameFilterWidget(QWidget *parent, FilterTreeModel *filterModel); + explicit VisualDatabaseDisplayNameFilterWidget(QWidget *parent, + TabGenericDeckEditor *deckEditor, + FilterTreeModel *filterModel); void createNameFilter(const QString &name); void removeNameFilter(const QString &name); @@ -22,12 +25,17 @@ public: void updateFilterModel(); private: + TabGenericDeckEditor *deckEditor; FilterTreeModel *filterModel; QVBoxLayout *layout; QLineEdit *searchBox; FlowWidget *flowWidget; + QPushButton *loadFromDeckButton; QMap activeFilters; // Store active name filter buttons + +private slots: + void actLoadFromDeck(); }; #endif // VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp index 877f25fb6..ea4784527 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp @@ -9,6 +9,7 @@ #include "../cards/card_info_picture_with_text_overlay_widget.h" #include "../quick_settings/settings_button_widget.h" #include "visual_database_display_color_filter_widget.h" +#include "visual_database_display_filter_save_load_widget.h" #include "visual_database_display_main_type_filter_widget.h" #include "visual_database_display_name_filter_widget.h" #include "visual_database_display_set_filter_widget.h" @@ -19,9 +20,11 @@ #include VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, + TabGenericDeckEditor *_deckEditor, CardDatabaseModel *database_model, CardDatabaseDisplayModel *database_display_model) - : QWidget(parent), databaseModel(database_model), databaseDisplayModel(database_display_model) + : QWidget(parent), deckEditor(_deckEditor), databaseModel(database_model), + databaseDisplayModel(database_display_model) { cards = new QList; connect(databaseDisplayModel, &CardDatabaseDisplayModel::modelDirty, this, @@ -53,7 +56,11 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, filterModel->setObjectName("filterModel"); databaseDisplayModel->setFilterTree(filterModel->filterTree()); - auto nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, filterModel); + auto saveLoadWidget = + new VisualDatabaseDisplayFilterSaveLoadWidget(this, filterModel, "/home/ascor/Cockatrice_presets/"); + quickFilterWidget->addSettingsWidget(saveLoadWidget); + + auto nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, deckEditor, filterModel); quickFilterWidget->addSettingsWidget(nameFilterWidget); auto mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel); diff --git a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h index 221ded26a..7375134a4 100644 --- a/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_database_display/visual_database_display_widget.h @@ -7,6 +7,7 @@ #include "../../../../game/cards/card_database_model.h" #include "../../../../game/filters/filter_tree_model.h" #include "../../../game_logic/key_signals.h" +#include "../../../tabs/tab_generic_deck_editor.h" #include "../../layouts/flow_layout.h" #include "../cards/card_info_picture_with_text_overlay_widget.h" #include "../cards/card_size_widget.h" @@ -29,6 +30,7 @@ class VisualDatabaseDisplayWidget : public QWidget public: explicit VisualDatabaseDisplayWidget(QWidget *parent, + TabGenericDeckEditor *deckEditor, CardDatabaseModel *database_model, CardDatabaseDisplayModel *database_display_model); @@ -61,6 +63,7 @@ protected slots: private: KeySignals searchKeySignals; + TabGenericDeckEditor *deckEditor; CardDatabaseModel *databaseModel; CardDatabaseDisplayModel *databaseDisplayModel; QTreeView *databaseView;