[Card] Pass the card search language to deck and zone card searches

Wire the two remaining FilterString consumers to the configured card search
language so card-name matches respect it everywhere:

- DeckFilterString now takes the search language and mode, exposes them to its
  [[card name]] rule action via a thread-local parse context (same pattern as
  FilterString), and the engine's card database uses them for content search.
- ZoneViewZone reads the card language from CardsDisplaySettings when applying
  its search filter, and the reveal-zone widget re-applies the active search
  when the language setting changes.
- The deck-storage search re-runs its filter against the current card language
  setting, including live re-application when the setting changes.

Game-action targeting (DlgMoveTopCardsUntil) intentionally keeps evaluating
against English card names.
This commit is contained in:
Lukas Brübach 2026-09-19 10:53:18 +02:00
parent 14cf4ee2a9
commit 04f77bd9f7
6 changed files with 49 additions and 5 deletions

View file

@ -43,6 +43,17 @@ NumericValue <- [0-9]+
static std::once_flag init;
// The peglib parser is a single permanent object, so the rule actions below cannot see
// per-instance state. The card language that the nested [[card name]] search matches
// against is passed through this thread-local context, which is live only while a
// DeckFilterString is being parsed, and copied into the nested FilterString closures.
struct DeckSearchLanguageContext
{
QString searchLanguage;
CardSearchLanguage searchLanguageMode = CardSearchLanguage::English;
};
thread_local DeckSearchLanguageContext deckSearchLanguageContext;
static void setupParserRules()
{
// plumbing
@ -116,7 +127,9 @@ static void setupParserRules()
// actual functionality
search["DeckContentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto cardFilter = FilterString(std::any_cast<QString>(sv[0]));
const QString searchLanguage = deckSearchLanguageContext.searchLanguage;
const CardSearchLanguage searchLanguageMode = deckSearchLanguageContext.searchLanguageMode;
auto cardFilter = FilterString(std::any_cast<QString>(sv[0]), searchLanguage, searchLanguageMode);
auto numberMatcher = sv.size() > 1 ? std::any_cast<NumberMatcher>(sv[1]) : [](int count) { return count > 0; };
return [=](const DeckSearchData &data) -> bool {
@ -186,7 +199,9 @@ DeckFilterString::DeckFilterString()
_error = "Not initialized";
}
DeckFilterString::DeckFilterString(const QString &expr)
DeckFilterString::DeckFilterString(const QString &expr,
const QString &searchLanguage,
CardSearchLanguage searchLanguageMode)
{
QByteArray ba = expr.simplified().toUtf8();
@ -199,6 +214,8 @@ DeckFilterString::DeckFilterString(const QString &expr)
return;
}
deckSearchLanguageContext = DeckSearchLanguageContext{searchLanguage, searchLanguageMode};
search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) {
_error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg));
});

View file

@ -12,6 +12,7 @@
#include <QLoggingCategory>
#include <QString>
#include <functional>
#include <libcockatrice/card/card_localization.h>
inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string");
@ -35,7 +36,9 @@ class DeckFilterString
{
public:
DeckFilterString();
explicit DeckFilterString(const QString &expr);
explicit DeckFilterString(const QString &expr,
const QString &searchLanguage = QString(),
CardSearchLanguage searchLanguageMode = CardSearchLanguage::English);
bool check(const DeckSearchData &data) const
{
return filter(data);

View file

@ -1,5 +1,6 @@
#include "view_zone.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/view_zone_logic.h"
@ -11,11 +12,13 @@
#include <QGraphicsSceneWheelEvent>
#include <QPainter>
#include <QtMath>
#include <libcockatrice/card/card_localization.h>
#include <libcockatrice/protocol/pb/command_dump_zone.pb.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
#include <libcockatrice/protocol/pb/response_dump_zone.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/settings/cards_display_settings.h>
/**
* @param parent the parent QGraphicsWidget containing the reveal zone
@ -253,7 +256,9 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
void ZoneViewZone::setFilterString(const QString &_filterString)
{
filterString = FilterString(_filterString);
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
filterString = FilterString(_filterString, cardsDisplay.getCardLang(),
static_cast<CardSearchLanguage>(cardsDisplay.getCardSearchLanguage()));
reorganizeCards();
}

View file

@ -21,6 +21,7 @@
#include <QStyle>
#include <QStyleOption>
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/settings/interface_settings.h>
namespace
@ -168,6 +169,11 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player,
}
connect(&searchEdit, &QLineEdit::textChanged, zone, &ZoneViewZone::setFilterString);
CardsDisplaySettings *cardsDisplay = &SettingsCache::instance().cardsDisplay();
const auto applyCardSearchLanguage = [this] { zone->setFilterString(searchEdit.text()); };
connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, this, applyCardSearchLanguage);
connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, this, applyCardSearchLanguage);
}
setLayout(vbox);

View file

@ -1,9 +1,12 @@
#include "visual_deck_storage_sort_filter_proxy_model.h"
#include "../../../client/settings/cache_settings.h"
#include "../../filters/deck_filter_string.h"
#include <QFileInfo>
#include <algorithm>
#include <libcockatrice/card/card_localization.h>
#include <libcockatrice/settings/cards_display_settings.h>
VisualDeckStorageSortFilterProxyModel::VisualDeckStorageSortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
@ -187,7 +190,9 @@ void VisualDeckStorageSortFilterProxyModel::updateSearchMatches()
return;
}
DeckFilterString filterString(searchText);
const auto &cardsDisplay = SettingsCache::instance().cardsDisplay();
DeckFilterString filterString(searchText, cardsDisplay.getCardLang(),
static_cast<CardSearchLanguage>(cardsDisplay.getCardSearchLanguage()));
for (int row = 0; row < count; ++row) {
const DeckPreviewData &data = source->dataForRow(row);

View file

@ -15,6 +15,7 @@
#include <QTimer>
#include <QVBoxLayout>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/settings/paths_settings.h>
#include <libcockatrice/settings/visual_deck_storage_settings.h>
@ -108,6 +109,13 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
connect(searchWidget, &VisualDeckStorageSearchWidget::searchTextChanged, this,
&VisualDeckStorageWidget::updateSearchFilter);
// The deck content search matches card names in the configured card language;
// re-run it whenever that setting changes so active searches follow immediately.
CardsDisplaySettings *cardsDisplay = &SettingsCache::instance().cardsDisplay();
const auto reapplySearchForLanguage = [this] { storageProxyModel->reapplyFilters(); };
connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, this, reapplySearchForLanguage);
connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, this, reapplySearchForLanguage);
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&VisualDeckStorageWidget::createRootFolderWidget);