[VDD] Saner and more performant color filtering, allow deleting specific filter from filterTree (#5863)

* Saner and more performant color filtering.

* Update visual_database_display_color_filter_widget.cpp

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: Zach H <zahalpern+github@gmail.com>
This commit is contained in:
BruebachL 2025-04-20 06:15:28 +02:00 committed by GitHub
parent 795149e776
commit acd9a163f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 198 additions and 130 deletions

View file

@ -20,12 +20,17 @@ ManaSymbolWidget::ManaSymbolWidget(QWidget *parent, QString _symbol, bool _isAct
&ManaSymbolWidget::updateOpacity);
}
void ManaSymbolWidget::toggleSymbol()
{
setColorActive(!isActive);
emit colorToggled(getSymbolChar(), isActive);
}
void ManaSymbolWidget::setColorActive(bool active)
{
if (isActive != active) {
isActive = active;
updateOpacity();
emit colorToggled(getSymbolChar(), isActive);
}
}
@ -46,9 +51,7 @@ void ManaSymbolWidget::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event);
if (mayBeToggled) {
isActive = !isActive;
updateOpacity();
emit colorToggled(getSymbolChar(), isActive);
toggleSymbol();
}
}

View file

@ -11,9 +11,13 @@ class ManaSymbolWidget : public QLabel
public:
ManaSymbolWidget(QWidget *parent, QString symbol, bool isActive = true, bool mayBeToggled = false);
void toggleSymbol();
void setColorActive(bool active);
void updateOpacity();
bool isColorActive() const;
bool isColorActive() const
{
return isActive;
};
QString getSymbol() const
{
return symbol;

View file

@ -27,9 +27,6 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
layout->addWidget(manaSymbol);
// Initialize the activeColors map
activeColors[color] = false;
// Connect the color toggled signal
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
&VisualDatabaseDisplayColorFilterWidget::handleColorToggled);
@ -41,16 +38,8 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
// Connect the button's toggled signal
connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayColorFilterWidget::updateFilterMode);
connect(this, &VisualDatabaseDisplayColorFilterWidget::activeColorsChanged, this,
&VisualDatabaseDisplayColorFilterWidget::updateColorFilter);
connect(this, &VisualDatabaseDisplayColorFilterWidget::filterModeChanged, this,
&VisualDatabaseDisplayColorFilterWidget::updateColorFilter);
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() {
if (blockSync) {
return; // Skip sync if we're blocking it
}
QTimer::singleShot(100, this, &VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel);
});
connect(filterModel, &FilterTreeModel::layoutChanged, this,
[this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel); });
// Call retranslateUi to set the initial text
retranslateUi();
@ -73,91 +62,88 @@ void VisualDatabaseDisplayColorFilterWidget::retranslateUi()
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active)
{
activeColors[color] = active;
emit activeColorsChanged(); // Notify listeners that the active colors have changed
if (active) {
addFilter(color);
} else {
removeFilter(color);
}
}
void VisualDatabaseDisplayColorFilterWidget::updateColorFilter()
void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color)
{
blockSync = true;
QString colorString = color;
QString typeStr;
// Clear previous filters
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
// Remove previous filters
QSet<QString> selectedColors;
QSet<QString> excludedColors;
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<const CardFilter *> matchingFilters;
// Collect active colors in the selected and excluded sets
for (const auto &color : activeColors.keys()) {
if (activeColors[color]) {
selectedColors.insert(color); // Include this color
} else {
excludedColors.insert(color); // Exclude this color
for (const CardFilter *filter : allColorFilters) {
if (filter->term() == color) {
matchingFilters.append(filter);
}
}
for (const CardFilter *filter : matchingFilters) {
filterModel->removeFilter(filter);
}
// Add actual filter
switch (currentMode) {
case FilterMode::ExactMatch:
// Exact Match Mode: Only selected colors are allowed
if (!selectedColors.isEmpty()) {
// Require all selected colors (TypeAnd)
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
}
// Exclude all other colors
QStringList allPossibleColors = {"W", "U", "B", "R", "G"};
for (const auto &color : allPossibleColors) {
if (!selectedColors.contains(color)) {
QString colorString = color;
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
}
}
}
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
break;
case FilterMode::Includes:
// Includes Mode: Just include selected colors without restrictions
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr,
CardFilter::Attr::AttrColor)); // OR for selected colors
}
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
case FilterMode::IncludeExclude:
// Include/Exclude Mode: Include selected colors and exclude unselected colors
for (const auto &color : selectedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr,
CardFilter::Attr::AttrColor)); // OR for selected colors
}
for (const auto &color : excludedColors) {
QString colorString = color;
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAndNot,
CardFilter::Attr::AttrColor)); // AND NOT for excluded colors
}
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
}
}
filterModel->blockSignals(false);
filterModel->filterTree()->blockSignals(false);
void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color)
{
QString colorString = color;
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
// Remove inclusion filters
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<const CardFilter *> matchingFilters;
blockSync = false;
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));
break;
case FilterMode::Includes:
// No exclusion in Includes mode
break;
}
}
void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
{
blockSync = true;
switch (currentMode) {
case FilterMode::ExactMatch:
currentMode = FilterMode::Includes; // Switch to Includes
@ -170,59 +156,100 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
break;
}
retranslateUi(); // Update button text based on the mode
emit filterModeChanged(currentMode); // Signal mode change
updateColorFilter(); // Reapply the filter based on the new mode
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
blockSync = false;
}
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
{
blockSync = true;
QSet<QString> currentFilters;
// Get current filters of type color
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::Attr::AttrColor)) {
if (filter->type() == CardFilter::Type::TypeAnd || filter->type() == CardFilter::Type::TypeOr) {
currentFilters.insert(filter->term());
}
}
QSet<QString> activeFilterList;
// Iterate over the activeColors map and collect the active colors as strings
for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) {
if (it.value()) { // Only add active colors
activeFilterList.insert(QString(it.key()));
}
}
// Check if the filters in the model match the active filters
if (currentFilters == activeFilterList) {
return;
}
// Remove filters that are in the UI but not in the model
for (const auto &color : activeFilterList) {
if (!currentFilters.contains(color)) {
activeColors[color[0]] = false; // Disable the color
}
}
// Add filters that are in the model but not in the UI
for (const auto &color : currentFilters) {
if (!activeFilterList.contains(color)) {
activeColors[color[0]] = true; // Enable the color
}
}
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
manaSymbolWidget->setColorActive(activeColors[manaSymbolWidget->getSymbolChar()]);
handleColorToggled(manaSymbolWidget->getSymbolChar(), manaSymbolWidget->isColorActive());
}
updateColorFilter();
blockSync = false;
}
filterModel->blockSignals(false);
filterModel->filterTree()->blockSignals(false);
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
retranslateUi(); // Update button text based on the mode
emit filterModeChanged(currentMode); // Signal mode change
}
void VisualDatabaseDisplayColorFilterWidget::setManaSymbolActive(QChar color, bool active)
{
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
if (manaSymbolWidget->getSymbolChar() == color) {
manaSymbolWidget->setColorActive(active);
}
}
}
QList<QChar> VisualDatabaseDisplayColorFilterWidget::getActiveColors()
{
QList<QChar> activeColors;
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
if (manaSymbolWidget->isColorActive()) {
activeColors.append(manaSymbolWidget->getSymbolChar());
}
}
return activeColors;
}
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
{
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
bool found = false;
for (const CardFilter *filter : allColorFilters) {
if (manaSymbolWidget->getSymbolChar() == 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;
}
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;
}
break;
}
found = true;
}
}
if (!found) {
setManaSymbolActive(manaSymbolWidget->getSymbolChar(), false);
}
}
}

View file

@ -43,21 +43,21 @@ public:
signals:
void filterModeChanged(FilterMode filterMode);
void activeColorsChanged();
private slots:
void handleColorToggled(QChar color, bool active);
void updateColorFilter();
void addFilter(QChar color);
void removeFilter(QChar color);
void updateFilterMode();
void setManaSymbolActive(QChar color, bool active);
QList<QChar> getActiveColors();
void syncWithFilterModel();
private:
FilterTreeModel *filterModel;
QHBoxLayout *layout;
QPushButton *toggleButton;
QMap<QChar, bool> activeColors;
FilterMode currentMode = FilterMode::Includes; // Default mode
bool blockSync = false;
};
#endif // VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H

View file

@ -531,6 +531,31 @@ void FilterTree::removeFiltersByAttr(CardFilter::Attr filterType)
}
}
void FilterTree::removeFilter(const CardFilter *toRemove)
{
for (int i = childNodes.size() - 1; i >= 0; --i) {
auto *logicMap = dynamic_cast<LogicMap *>(childNodes.at(i));
if (!logicMap || logicMap->attr != toRemove->attr())
continue;
FilterItemList *typeList = logicMap->typeList(toRemove->type());
if (!typeList)
continue;
int termIdx = typeList->termIndex(toRemove->term());
if (termIdx != -1) {
typeList->deleteAt(termIdx);
emit typeList->nodeChanged();
if (typeList->childCount() == 0) {
int logicIndex = logicMap->childIndex(typeList);
if (logicIndex != -1) {
logicMap->deleteAt(logicIndex);
}
}
}
}
}
void FilterTree::clear()
{
while (childCount() > 0) {

View file

@ -270,6 +270,7 @@ public:
bool acceptsCard(CardInfoPtr info) const;
void removeFiltersByAttr(CardFilter::Attr filterType);
void removeFilter(const CardFilter *toRemove);
void clear();
};

View file

@ -78,6 +78,13 @@ void FilterTreeModel::addFilter(const CardFilter *f)
emit layoutChanged();
}
void FilterTreeModel::removeFilter(const CardFilter *f)
{
emit layoutAboutToBeChanged();
fTree->removeFilter(f);
emit layoutChanged();
}
void FilterTreeModel::clearFiltersOfType(CardFilter::Attr filterType)
{
emit layoutAboutToBeChanged();

View file

@ -17,6 +17,7 @@ private:
public slots:
void addFilter(const CardFilter *f);
void removeFilter(const CardFilter *f);
void clearFiltersOfType(CardFilter::Attr filterType);
QList<const CardFilter *> getFiltersOfType(CardFilter::Attr filterType) const;
QList<const CardFilter *> allFilters() const;