[Refactor] Decouple DeckFilterString from DeckPreviewWidget (#7104)

* [Refactor] Decouple DeckFilterString from DeckPreviewWidget

Took 19 minutes

* Use designated initializer

Took 24 seconds

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-13 19:04:16 +02:00 • committed by GitHub
parent 26fb8622e3
commit 12a5b34e42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 44 deletions

View file

@ -52,18 +52,14 @@ static void setupParserRules()
search["Start"] = passthru; search["Start"] = passthru;
search["QueryPartList"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["QueryPartList"] = [](const peg::SemanticValues &sv) -> DeckFilter {
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) { return [=](const DeckSearchData &data) {
auto matchesFilter = [&deck, &info](const std::any &query) { auto matchesFilter = [&data](const std::any &query) { return std::any_cast<DeckFilter>(query)(data); };
return std::any_cast<DeckFilter>(query)(deck, info);
};
return std::all_of(sv.begin(), sv.end(), matchesFilter); return std::all_of(sv.begin(), sv.end(), matchesFilter);
}; };
}; };
search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> DeckFilter {
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) { return [=](const DeckSearchData &data) {
auto matchesFilter = [&deck, &info](const std::any &query) { auto matchesFilter = [&data](const std::any &query) { return std::any_cast<DeckFilter>(query)(data); };
return std::any_cast<DeckFilter>(query)(deck, info);
};
return std::any_of(sv.begin(), sv.end(), matchesFilter); return std::any_of(sv.begin(), sv.end(), matchesFilter);
}; };
}; };
@ -71,9 +67,7 @@ static void setupParserRules()
search["QueryPart"] = passthru; search["QueryPart"] = passthru;
search["NotQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["NotQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
const auto dependent = std::any_cast<DeckFilter>(sv[0]); const auto dependent = std::any_cast<DeckFilter>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) -> bool { return [=](const DeckSearchData &data) -> bool { return !dependent(data); };
return !dependent(deck, info);
};
}; };
search["String"] = [](const peg::SemanticValues &sv) -> QString { search["String"] = [](const peg::SemanticValues &sv) -> QString {
@ -125,9 +119,9 @@ static void setupParserRules()
auto cardFilter = FilterString(std::any_cast<QString>(sv[0])); auto cardFilter = FilterString(std::any_cast<QString>(sv[0]));
auto numberMatcher = sv.size() > 1 ? std::any_cast<NumberMatcher>(sv[1]) : [](int count) { return count > 0; }; auto numberMatcher = sv.size() > 1 ? std::any_cast<NumberMatcher>(sv[1]) : [](int count) { return count > 0; };
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool { return [=](const DeckSearchData &data) -> bool {
int count = 0; int count = 0;
auto cardNodes = deck->deckLoader->getDeck().deckList.getCardNodes(); auto cardNodes = data.deck->deckList.getCardNodes();
for (auto node : cardNodes) { for (auto node : cardNodes) {
auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName()); auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) { if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) {
@ -146,53 +140,49 @@ static void setupParserRules()
search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]); auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { return [=](const DeckSearchData &data) {
return deck->deckLoader->getDeck().deckList.getName().contains(name, Qt::CaseInsensitive); return data.deck->deckList.getName().contains(name, Qt::CaseInsensitive);
}; };
}; };
search["FileNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["FileNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]); auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { return [=](const DeckSearchData &data) {
auto filename = QFileInfo(deck->filePath).fileName(); auto filename = QFileInfo(data.filePath).fileName();
return filename.contains(name, Qt::CaseInsensitive); return filename.contains(name, Qt::CaseInsensitive);
}; };
}; };
search["PathQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["PathQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]); auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *, const ExtraDeckSearchInfo &info) { return [=](const DeckSearchData &data) { return data.relativeFilePath.contains(name, Qt::CaseInsensitive); };
return info.relativeFilePath.contains(name, Qt::CaseInsensitive);
};
}; };
search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto format = std::any_cast<QString>(sv[0]); auto format = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { return [=](const DeckSearchData &data) {
auto gameFormat = deck->deckLoader->getDeck().deckList.getGameFormat(); auto gameFormat = data.deck->deckList.getGameFormat();
return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0; return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0;
}; };
}; };
search["CommentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["CommentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto value = std::any_cast<QString>(sv[0]); auto value = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { return [=](const DeckSearchData &data) {
auto comments = deck->deckLoader->getDeck().deckList.getComments(); auto comments = data.deck->deckList.getComments();
return comments.contains(value, Qt::CaseInsensitive); return comments.contains(value, Qt::CaseInsensitive);
}; };
}; };
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]); auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { return [=](const DeckSearchData &data) { return data.displayName.contains(name, Qt::CaseInsensitive); };
return deck->getDisplayName().contains(name, Qt::CaseInsensitive);
};
}; };
} }
DeckFilterString::DeckFilterString() DeckFilterString::DeckFilterString()
{ {
filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return false; }; filter = [](const DeckSearchData &) { return false; };
_error = "Not initialized"; _error = "Not initialized";
} }
@ -205,7 +195,7 @@ DeckFilterString::DeckFilterString(const QString &expr)
_error = QString(); _error = QString();
if (ba.isEmpty()) { if (ba.isEmpty()) {
filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return true; }; filter = [](const DeckSearchData &) { return true; };
return; return;
} }
@ -215,6 +205,6 @@ DeckFilterString::DeckFilterString(const QString &expr)
if (!search.parse(ba.data(), filter)) { if (!search.parse(ba.data(), filter)) {
qCInfo(DeckFilterStringLog).nospace() << "DeckFilterString error for " << expr << "; " << qPrintable(_error); qCInfo(DeckFilterStringLog).nospace() << "DeckFilterString error for " << expr << "; " << qPrintable(_error);
filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return false; }; filter = [](const DeckSearchData &) { return false; };
} }
} }

View file

@ -7,7 +7,7 @@
#ifndef DECK_FILTER_STRING_H #ifndef DECK_FILTER_STRING_H
#define DECK_FILTER_STRING_H #define DECK_FILTER_STRING_H
#include "../interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h" #include "../interface/deck_loader/loaded_deck.h"
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QString> #include <QString>
@ -16,26 +16,29 @@
inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string"); inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string");
/** /**
* Extra info relevant to filtering that isn't present in the DeckPreviewWidget * The data a deck search expression is evaluated against.
*
* This is a data view rather than a widget pointer, so the same filter
* expression can be evaluated against a model or a live widget.
*/ */
struct ExtraDeckSearchInfo struct DeckSearchData
{ {
/** const LoadedDeck *deck = nullptr; ///< The loaded deck. Must not be null.
* The relative filepath starting from the deck folder QString filePath; ///< Absolute path of the deck file.
*/ QString displayName; ///< Deck name, or the file name if the deck has no name.
QString relativeFilePath; QString relativeFilePath; ///< File path relative to the deck folder.
}; };
typedef std::function<bool(const DeckPreviewWidget *, const ExtraDeckSearchInfo &)> DeckFilter; typedef std::function<bool(const DeckSearchData &data)> DeckFilter;
class DeckFilterString class DeckFilterString
{ {
public: public:
DeckFilterString(); DeckFilterString();
explicit DeckFilterString(const QString &expr); explicit DeckFilterString(const QString &expr);
bool check(const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) const bool check(const DeckSearchData &data) const
{ {
return filter(deck, info); return filter(data);
} }
[[nodiscard]] bool valid() const [[nodiscard]] bool valid() const

View file

@ -73,10 +73,14 @@ static QString toRelativeFilepath(const QString &filePath)
void VisualDeckStorageSearchWidget::filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText) void VisualDeckStorageSearchWidget::filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText)
{ {
auto filterString = DeckFilterString(searchText); const auto filterString = DeckFilterString(searchText);
for (auto widget : widgets) { for (auto widget : widgets) {
QString relativeFilePath = toRelativeFilepath(widget->filePath); const DeckSearchData searchData{.deck = &widget->deckLoader->getDeck(),
widget->filteredBySearch = !filterString.check(widget, {relativeFilePath}); .filePath = widget->filePath,
.displayName = widget->getDisplayName(),
.relativeFilePath = toRelativeFilepath(widget->filePath)};
widget->filteredBySearch = !filterString.check(searchData);
} }
} }