Rederive the mode from saved filters, added Custom mode

This commit is contained in:
Jurriaan van den Berg 2026-11-02 14:20:04 +01:00
parent e85e81ce85
commit f2e4aa3c34
2 changed files with 130 additions and 83 deletions

View file

@ -15,6 +15,39 @@
#include <QtGlobal>
#include <libcockatrice/filters/filter_tree.h>
/**
* @brief Information about the filter mode.
*/
struct FilterModeInfo
{
CardFilter::Type includeType;
CardFilter::Type excludeType;
CardFilter::Attr attr;
};
/**
* @brief Return information about the filter mode.
*/
static FilterModeInfo getFilterModeInfo(FilterMode filterMode)
{
switch (filterMode) {
case FilterMode::Includes:
return {CardFilter::TypeOr, CardFilter::TypeEnd, CardFilter::AttrColor};
case FilterMode::Excludes:
return {CardFilter::TypeAndNot, CardFilter::TypeEnd, CardFilter::AttrColor};
case FilterMode::IncludeExclude:
return {CardFilter::TypeOr, CardFilter::TypeAndNot, CardFilter::AttrColor};
case FilterMode::ExactMatch:
return {CardFilter::TypeAnd, CardFilter::TypeAndNot, CardFilter::AttrColor};
case FilterMode::ColorIdentity:
return {CardFilter::TypeEnd, CardFilter::TypeAndNot, CardFilter::AttrColorIdentity};
case FilterMode::Custom:
case FilterMode::End:
break;
}
return {CardFilter::TypeEnd, CardFilter::TypeEnd, CardFilter::AttrEnd};
}
/**
* This widget provides a graphical control element for the CardFilter::Attr::AttrColor filters applied to the filter
* model.
@ -59,16 +92,17 @@ void VisualDatabaseDisplayColorFilterWidget::retranslateUi()
modeComboBox->blockSignals(true);
modeComboBox->clear();
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("Exact match"), QVariant::fromValue(FilterMode::ExactMatch));
modeComboBox->addItem(tr("Color identity"), QVariant::fromValue(FilterMode::ColorIdentity));
modeComboBox->setItemData(0, tr("Cards being exactly of the selected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(1, tr("Cards containing any of the selected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(2, tr("Cards containing none of the selected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(3, tr("Cards containing any of the selected colors and none of the unselected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(0, tr("Cards containing any of the selected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(1, tr("Cards containing none of the selected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(2, tr("Cards containing any of the selected colors and none of the unselected colors"),
Qt::ToolTipRole);
modeComboBox->setItemData(3, tr("Cards being exactly of the selected colors"), Qt::ToolTipRole);
modeComboBox->setItemData(4, tr("Cards whose color identity fits within the selected colors"), Qt::ToolTipRole);
modeComboBox->setToolTip(tr("How selected and unselected colors are combined in the filter"));
@ -91,6 +125,7 @@ void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, boo
}
}
/** @brief TODO */
static void removeFilters(FilterTreeModel *filterModel, QChar color, CardFilter::Attr attr)
{
for (const CardFilter *filter : filterModel->getFiltersOfType(attr)) {
@ -102,59 +137,24 @@ static void removeFilters(FilterTreeModel *filterModel, QChar color, CardFilter:
void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color)
{
switch (currentMode) {
case FilterMode::ExactMatch:
removeFilters(filterModel, color, CardFilter::Attr::AttrColor);
filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
break;
FilterModeInfo info = getFilterModeInfo(currentMode);
case FilterMode::Includes:
removeFilters(filterModel, color, CardFilter::Attr::AttrColor);
filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
removeFilters(filterModel, color, info.attr);
case FilterMode::Excludes:
removeFilters(filterModel, color, CardFilter::Attr::AttrColor);
filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
break;
case FilterMode::IncludeExclude:
removeFilters(filterModel, color, CardFilter::Attr::AttrColor);
filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
case FilterMode::ColorIdentity:
removeFilters(filterModel, color, CardFilter::Attr::AttrColorIdentity);
break;
if (info.includeType != CardFilter::TypeEnd) {
filterModel->addFilter(new CardFilter(color, info.includeType, info.attr));
}
}
void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color)
{
// Add exclusion filters if the mode demands it
switch (currentMode) {
case FilterMode::ExactMatch:
removeFilters(filterModel, color, CardFilter::Attr::AttrColor);
filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
break;
FilterModeInfo info = getFilterModeInfo(currentMode);
case FilterMode::Includes:
removeFilters(filterModel, color, CardFilter::Attr::AttrColor);
break;
removeFilters(filterModel, color, info.attr);
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::ColorIdentity:
removeFilters(filterModel, color, CardFilter::Attr::AttrColorIdentity);
filterModel->addFilter(new CardFilter(color, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColorIdentity));
break;
// Add exclusion filters if the mode demands it.
if (info.excludeType != CardFilter::TypeEnd) {
filterModel->addFilter(new CardFilter(color, info.excludeType, info.attr));
}
}
@ -214,42 +214,86 @@ QList<QChar> VisualDatabaseDisplayColorFilterWidget::getActiveColors()
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
{
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(
currentMode == FilterMode::ColorIdentity
? CardFilter::Attr::AttrColorIdentity
: CardFilter::Attr::AttrColor);
// Determine the correct currentMode from the filter model by finding which modes
// are not possible due to the model containing filters that that mode cannot add.
constexpr int modeCount = static_cast<int>(FilterMode::End);
bool modeImpossible[modeCount] = {};
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
QList<const CardFilter *> filters;
filters.append(filterModel->getFiltersOfType(CardFilter::AttrColor));
filters.append(filterModel->getFiltersOfType(CardFilter::AttrColorIdentity));
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
QChar symbol = manaSymbolWidget->getSymbolChar();
bool found = currentMode == FilterMode::ColorIdentity;
for (const CardFilter *filter : allColorFilters) {
if (symbol == filter->term()) {
switch (currentMode) {
case FilterMode::ExactMatch:
if (filter->type() == CardFilter::Type::TypeAnd)
found = true;
break;
case FilterMode::Includes:
case FilterMode::IncludeExclude:
if (filter->type() == CardFilter::Type::TypeOr)
found = true;
break;
case FilterMode::Excludes:
if (filter->type() == CardFilter::Type::TypeAndNot)
found = true;
break;
case FilterMode::ColorIdentity:
if (filter->type() == CardFilter::Type::TypeAndNot)
found = false;
break;
if (filters.empty()) {
// Remove the modes that should always contain filters.
for (int i = 0; i < modeCount; ++i) {
FilterModeInfo modeInfo = getFilterModeInfo(static_cast<FilterMode>(i));
if (modeInfo.includeType != CardFilter::TypeEnd && modeInfo.excludeType != CardFilter::TypeEnd) {
modeImpossible[i] = true;
}
}
} else {
// Remove the modes that cannot create one of the filters.
for (const CardFilter *filter : filters) {
for (int i = 0; i < modeCount; ++i) {
FilterModeInfo modeInfo = getFilterModeInfo(static_cast<FilterMode>(i));
if (filter->attr() != modeInfo.attr ||
(filter->type() != modeInfo.includeType && filter->type() != modeInfo.excludeType)) {
modeImpossible[i] = true;
}
if (found)
break;
}
}
}
// If the current mode is not possible with the current filters, set it to the first
// one that is. Switching to and from the custom mode is always possible.
if (modeImpossible[static_cast<int>(currentMode)] || currentMode == FilterMode::Custom) {
for (int i = 0; i < modeCount; ++i) {
if (!modeImpossible[i] || i == static_cast<int>(FilterMode::Custom)) {
currentMode = static_cast<FilterMode>(i);
break;
}
}
}
// Update the combobox. Add a Custom item only if we have a custom filter mode.
int customIndex = modeComboBox->findData(QVariant::fromValue(FilterMode::Custom));
modeComboBox->blockSignals(true);
if (currentMode == FilterMode::Custom) {
if (customIndex < 0) {
customIndex = modeComboBox->count();
modeComboBox->addItem(tr("Custom"), QVariant::fromValue(FilterMode::Custom));
modeComboBox->setItemData(customIndex, tr("Cards matching an unknown combination of color filters"),
Qt::ToolTipRole);
}
modeComboBox->setCurrentIndex(customIndex);
} else {
if (customIndex >= 0) {
modeComboBox->removeItem(customIndex);
}
int index = modeComboBox->findData(QVariant::fromValue(currentMode));
if (index >= 0) {
modeComboBox->setCurrentIndex(index);
}
}
modeComboBox->blockSignals(false);
// Update the mana symbol widgets by looking for the current mode's include or exclude filters.
const FilterModeInfo modeInfo = getFilterModeInfo(currentMode);
const bool useExclude = modeInfo.includeType == CardFilter::TypeEnd && modeInfo.excludeType != CardFilter::TypeEnd;
const CardFilter::Type filterType = useExclude ? modeInfo.excludeType : modeInfo.includeType;
filters = filterModel->getFiltersOfType(modeInfo.attr);
for (ManaSymbolWidget *manaSymbolWidget : findChildren<ManaSymbolWidget *>()) {
bool found = false;
for (const CardFilter *filter : filters) {
if (filter->type() == filterType && manaSymbolWidget->getSymbolChar() == filter->term()) {
found = true;
break;
}
}
setManaSymbolActive(manaSymbolWidget->getSymbolChar(), found);
manaSymbolWidget->setColorActive(useExclude ? !found : found);
}
}

View file

@ -40,11 +40,14 @@ private:
enum class FilterMode
{
ExactMatch, // Only selected colors are included, all others are excluded.
Includes, // Include selected colors (OR condition).
Includes = 0, // Include selected colors (OR condition).
Excludes, // Exclude selected colors (AND NOT).
IncludeExclude, // Include selected colors (OR) and exclude unselected colors (AND NOT).
ColorIdentity // Keep cards whose color identity is a subset of the selected colors (stored as ANDNOT for each unselected color).
ExactMatch, // Only selected colors are included, all others are excluded.
ColorIdentity, // Keep cards whose color identity is a subset of the selected colors (stored as ANDNOT for each
// unselected color).
Custom, // The filters do not match any of the modes. Not included in the combo box.
End
};
Q_DECLARE_METATYPE(FilterMode)