From 286982350c4632140b52ee516922bf7f4cd390a9 Mon Sep 17 00:00:00 2001 From: Jurriaan van den Berg Date: Mon, 19 Oct 2026 03:15:01 +0200 Subject: [PATCH] Added Exclude and Color Identity filter modes. --- ...l_database_display_color_filter_widget.cpp | 139 ++++++++---------- ...ual_database_display_color_filter_widget.h | 8 +- .../libcockatrice/card/card_info.cpp | 4 + .../libcockatrice/card/card_info.h | 1 + .../libcockatrice/filters/filter_card.cpp | 2 + .../libcockatrice/filters/filter_card.h | 3 +- .../libcockatrice/filters/filter_tree.cpp | 47 +++--- .../libcockatrice/filters/filter_tree.h | 1 + 8 files changed, 107 insertions(+), 98 deletions(-) diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp index cf815ace0..ac6d78a63 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp @@ -62,7 +62,9 @@ void VisualDatabaseDisplayColorFilterWidget::retranslateUi() modeComboBox->addItem(tr("Exact match"), QVariant::fromValue(FilterMode::ExactMatch)); modeComboBox->addItem(tr("Includes"), QVariant::fromValue(FilterMode::Includes)); + modeComboBox->addItem(tr("Excludes"), QVariant::fromValue(FilterMode::Excludes)); modeComboBox->addItem(tr("Include / Exclude"), QVariant::fromValue(FilterMode::IncludeExclude)); + modeComboBox->addItem(tr("Commander"), QVariant::fromValue(FilterMode::Commander)); modeComboBox->setToolTip(tr("How selected and unselected colors are combined in the filter")); @@ -84,75 +86,69 @@ void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, boo } } -void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color) +static void removeFilters(FilterTreeModel *filterModel, QChar color, CardFilter::Attr attr) { - QString colorString = color; - QString typeStr; - - // Remove previous filters - - QList allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); - QList matchingFilters; - - for (const CardFilter *filter : allColorFilters) { + for (const CardFilter *filter : filterModel->getFiltersOfType(attr)) { if (filter->term() == color) { - matchingFilters.append(filter); + filterModel->removeFilter(filter); } } +} - for (const CardFilter *filter : matchingFilters) { - filterModel->removeFilter(filter); - } - - // Add actual filter - +void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color) +{ switch (currentMode) { case FilterMode::ExactMatch: - filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor)); + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor)); break; case FilterMode::Includes: - filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); + break; + + case FilterMode::Excludes: + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); break; case FilterMode::IncludeExclude: - filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); + break; + + case FilterMode::Commander: + removeFilters(filterModel, color, CardFilter::Attr::AttrColorIdentity); break; } } void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color) { - QString colorString = color; - - // Remove inclusion filters - QList allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); - QList matchingFilters; - - for (const CardFilter *filter : allColorFilters) { - if (filter->term() == color) { - matchingFilters.append(filter); - } - } - - for (const CardFilter *filter : matchingFilters) { - filterModel->removeFilter(filter); - } - // Add exclusion filters if the mode demands it switch (currentMode) { case FilterMode::ExactMatch: - filterModel->addFilter( - new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); - break; - - case FilterMode::IncludeExclude: - filterModel->addFilter( - new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); break; case FilterMode::Includes: - // No exclusion in Includes mode + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + break; + + case FilterMode::Excludes: + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + break; + + case FilterMode::IncludeExclude: + removeFilters(filterModel, color, CardFilter::Attr::AttrColor); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); + break; + + case FilterMode::Commander: + removeFilters(filterModel, color, CardFilter::Attr::AttrColorIdentity); + filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColorIdentity)); break; } } @@ -170,6 +166,7 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode() filterModel->filterTree()->blockSignals(true); filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor); + filterModel->clearFiltersOfType(CardFilter::Attr::AttrColorIdentity); const QList manaSymbolWidgets = findChildren(); @@ -212,52 +209,42 @@ QList VisualDatabaseDisplayColorFilterWidget::getActiveColors() void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel() { - QList allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); + QList allColorFilters = filterModel->getFiltersOfType( + currentMode == FilterMode::Commander + ? CardFilter::Attr::AttrColorIdentity + : CardFilter::Attr::AttrColor); QList manaSymbolWidgets = findChildren(); for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) { - bool found = false; + QChar symbol = manaSymbolWidget->getSymbolChar(); + bool found = currentMode == FilterMode::Commander; for (const CardFilter *filter : allColorFilters) { - if (manaSymbolWidget->getSymbolChar() == filter->term()) { + if (symbol == filter->term()) { switch (currentMode) { case FilterMode::ExactMatch: - switch (filter->type()) { - case CardFilter::Type::TypeAnd: - setManaSymbolActive(filter->term().at(0), true); - break; - default: - setManaSymbolActive(filter->term().at(0), false); - break; - } + if (filter->type() == CardFilter::Type::TypeAnd) + found = true; break; case FilterMode::Includes: - switch (filter->type()) { - case CardFilter::Type::TypeOr: - setManaSymbolActive(filter->term().at(0), true); - break; - default: - setManaSymbolActive(filter->term().at(0), false); - break; - } - break; case FilterMode::IncludeExclude: - switch (filter->type()) { - case CardFilter::Type::TypeOr: - setManaSymbolActive(filter->term().at(0), true); - break; - default: - setManaSymbolActive(filter->term().at(0), false); - break; - } + if (filter->type() == CardFilter::Type::TypeOr) + found = true; + break; + case FilterMode::Excludes: + if (filter->type() == CardFilter::Type::TypeAndNot) + found = true; + break; + case FilterMode::Commander: + if (filter->type() == CardFilter::Type::TypeAndNot) + found = false; break; } - found = true; + if (found) + break; } } - if (!found) { - setManaSymbolActive(manaSymbolWidget->getSymbolChar(), false); - } + setManaSymbolActive(manaSymbolWidget->getSymbolChar(), found); } } diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h index 38812b02a..c1c8e173f 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h @@ -39,9 +39,11 @@ private: enum class FilterMode { - ExactMatch, // Only selected colors are included, all others are excluded. - Includes, // Include selected colors (OR condition). - IncludeExclude // Include selected colors (OR) and exclude unselected colors (AND NOT). + ExactMatch, // Only selected colors are included, all others are excluded. + Includes, // Include selected colors (OR condition). + Excludes, // Exclude selected colors (OR). + IncludeExclude, // Include selected colors (OR) and exclude unselected colors (AND NOT). + Commander // Only include colors in the selected color identity. }; Q_DECLARE_METATYPE(FilterMode) diff --git a/libcockatrice_card/libcockatrice/card/card_info.cpp b/libcockatrice_card/libcockatrice/card/card_info.cpp index bd33f18e7..3aaa4c5bc 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.cpp +++ b/libcockatrice_card/libcockatrice/card/card_info.cpp @@ -285,6 +285,10 @@ void CardInfo::setColors(const QString &value) { setProperty(Mtg::Colors, value); } +QString CardInfo::getColorIdentity() const +{ + return getProperty(Mtg::ColorIdentity); +} QString CardInfo::getLoyalty() const { return getProperty(Mtg::Loyalty); diff --git a/libcockatrice_card/libcockatrice/card/card_info.h b/libcockatrice_card/libcockatrice/card/card_info.h index 356da0546..1670e245d 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.h +++ b/libcockatrice_card/libcockatrice/card/card_info.h @@ -454,6 +454,7 @@ public: [[nodiscard]] QString getCmc() const; [[nodiscard]] QString getColors() const; void setColors(const QString &value); + [[nodiscard]] QString getColorIdentity() const; [[nodiscard]] QString getLoyalty() const; [[nodiscard]] QString getMainCardType() const; [[nodiscard]] QString getManaCost() const; diff --git a/libcockatrice_filters/libcockatrice/filters/filter_card.cpp b/libcockatrice_filters/libcockatrice/filters/filter_card.cpp index 26cddd53c..8af39c098 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_card.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_card.cpp @@ -69,6 +69,8 @@ const QString CardFilter::attrName(Attr a) return tr("Type"); case AttrColor: return tr("Color"); + case AttrColorIdentity: + return tr("Color Identity"); case AttrText: return tr("Text"); case AttrSet: diff --git a/libcockatrice_filters/libcockatrice/filters/filter_card.h b/libcockatrice_filters/libcockatrice/filters/filter_card.h index e6a37ee40..7e4efbd96 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_card.h +++ b/libcockatrice_filters/libcockatrice/filters/filter_card.h @@ -32,6 +32,7 @@ public: { AttrCmc = 0, AttrColor, + AttrColorIdentity, AttrLoyalty, AttrManaCost, AttrName, @@ -54,7 +55,7 @@ private: enum Attr a; public: - CardFilter(QString &term, Type type, Attr attr) : trm(term), t(type), a(attr) + CardFilter(QString const &term, Type type, Attr attr) : trm(term), t(type), a(attr) { } diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp index 742b44e97..1448c7ba1 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp @@ -227,27 +227,26 @@ bool FilterItem::acceptSubType(const CardInfoPtr info) const return typeParts.size() > 1 && typeParts[1].contains(term, Qt::CaseInsensitive); } -bool FilterItem::acceptColor(const CardInfoPtr info) const -{ - QString converted_term = term.trimmed(); +static bool matchesColor(QString const &term, QString const &colors) { + QString converted_term = term.trimmed().toLower(); - converted_term.replace("green", "g", Qt::CaseInsensitive); - converted_term.replace("grn", "g", Qt::CaseInsensitive); - converted_term.replace("blue", "u", Qt::CaseInsensitive); - converted_term.replace("blu", "u", Qt::CaseInsensitive); - converted_term.replace("black", "b", Qt::CaseInsensitive); - converted_term.replace("blk", "b", Qt::CaseInsensitive); - converted_term.replace("red", "r", Qt::CaseInsensitive); - converted_term.replace("white", "w", Qt::CaseInsensitive); - converted_term.replace("wht", "w", Qt::CaseInsensitive); - converted_term.replace("colorless", "c", Qt::CaseInsensitive); - converted_term.replace("colourless", "c", Qt::CaseInsensitive); - converted_term.replace("none", "c", Qt::CaseInsensitive); + converted_term.replace("green", "g"); + converted_term.replace("grn", "g"); + converted_term.replace("blue", "u"); + converted_term.replace("blu", "u"); + converted_term.replace("black", "b"); + converted_term.replace("blk", "b"); + converted_term.replace("red", "r"); + converted_term.replace("white", "w"); + converted_term.replace("wht", "w"); + converted_term.replace("colorless", "c"); + converted_term.replace("colourless", "c"); + converted_term.replace("none", "c"); - converted_term.replace(QString(" "), QString(""), Qt::CaseInsensitive); + converted_term.replace(QString(" "), QString("")); // Colorless card filter - if (converted_term.toLower() == "c" && info->getColors().length() < 1) { + if (converted_term == "c" && colors.length() < 1) { return true; } @@ -257,7 +256,7 @@ bool FilterItem::acceptColor(const CardInfoPtr info) const */ int match_count = 0; for (auto &it : converted_term) { - if (info->getColors().contains(it, Qt::CaseInsensitive)) { + if (colors.contains(it, Qt::CaseInsensitive)) { match_count++; } } @@ -265,6 +264,16 @@ bool FilterItem::acceptColor(const CardInfoPtr info) const return match_count == converted_term.length(); } +bool FilterItem::acceptColor(const CardInfoPtr info) const +{ + return matchesColor(term, info->getColors()); +} + +bool FilterItem::acceptColorIdentity(const CardInfoPtr info) const +{ + return matchesColor(term, info->getColorIdentity()); +} + bool FilterItem::acceptText(const CardInfoPtr info, const CardSearchLanguage &searchLanguage) const { const QString &englishText = info->getText(); @@ -485,6 +494,8 @@ bool FilterItem::acceptCardAttr(const CardInfoPtr info, return acceptType(info); case CardFilter::AttrColor: return acceptColor(info); + case CardFilter::AttrColorIdentity: + return acceptColorIdentity(info); case CardFilter::AttrText: return acceptText(info, searchLanguage); case CardFilter::AttrSet: diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.h b/libcockatrice_filters/libcockatrice/filters/filter_tree.h index 0defd466b..e94e0a948 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.h +++ b/libcockatrice_filters/libcockatrice/filters/filter_tree.h @@ -223,6 +223,7 @@ public: [[nodiscard]] bool acceptMainType(CardInfoPtr info) const; [[nodiscard]] bool acceptSubType(CardInfoPtr info) const; [[nodiscard]] bool acceptColor(CardInfoPtr info) const; + [[nodiscard]] bool acceptColorIdentity(CardInfoPtr info) const; [[nodiscard]] bool acceptText(CardInfoPtr info, const CardSearchLanguage &searchLanguage) const; [[nodiscard]] bool acceptSet(CardInfoPtr info) const; [[nodiscard]] bool acceptManaCost(CardInfoPtr info) const;