mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-27 00:14:40 -07:00
Compare commits
2 commits
12299abcc8
...
7a2492ac67
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a2492ac67 | ||
|
|
ef68a7bdcc |
28 changed files with 505 additions and 74 deletions
|
|
@ -43,6 +43,12 @@ 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.
|
||||
thread_local CardSearchLanguage deckSearchLanguageContext;
|
||||
|
||||
static void setupParserRules()
|
||||
{
|
||||
// plumbing
|
||||
|
|
@ -116,7 +122,7 @@ static void setupParserRules()
|
|||
|
||||
// actual functionality
|
||||
search["DeckContentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
|
||||
auto cardFilter = FilterString(std::any_cast<QString>(sv[0]));
|
||||
auto cardFilter = FilterString(std::any_cast<QString>(sv[0]), deckSearchLanguageContext);
|
||||
auto numberMatcher = sv.size() > 1 ? std::any_cast<NumberMatcher>(sv[1]) : [](int count) { return count > 0; };
|
||||
|
||||
return [=](const DeckSearchData &data) -> bool {
|
||||
|
|
@ -186,7 +192,7 @@ DeckFilterString::DeckFilterString()
|
|||
_error = "Not initialized";
|
||||
}
|
||||
|
||||
DeckFilterString::DeckFilterString(const QString &expr)
|
||||
DeckFilterString::DeckFilterString(const QString &expr, const CardSearchLanguage &searchLanguage)
|
||||
{
|
||||
QByteArray ba = expr.simplified().toUtf8();
|
||||
|
||||
|
|
@ -199,6 +205,8 @@ DeckFilterString::DeckFilterString(const QString &expr)
|
|||
return;
|
||||
}
|
||||
|
||||
deckSearchLanguageContext = searchLanguage;
|
||||
|
||||
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));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,7 @@ class DeckFilterString
|
|||
{
|
||||
public:
|
||||
DeckFilterString();
|
||||
explicit DeckFilterString(const QString &expr);
|
||||
explicit DeckFilterString(const QString &expr, const CardSearchLanguage &searchLanguage = {});
|
||||
bool check(const DeckSearchData &data) const
|
||||
{
|
||||
return filter(data);
|
||||
|
|
|
|||
|
|
@ -16,11 +16,13 @@
|
|||
#include <QLineEdit>
|
||||
#include <QRadioButton>
|
||||
#include <QTreeView>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/models/database/card_database_model.h>
|
||||
#include <libcockatrice/models/database/token/token_display_model.h>
|
||||
#include <libcockatrice/settings/card_override_settings.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/layouts_settings.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
|
|
@ -88,6 +90,17 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
|||
cardDatabaseDisplayModel = new TokenDisplayModel(this);
|
||||
cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel);
|
||||
|
||||
const auto applyCardSearchLanguage = [this]() {
|
||||
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
|
||||
cardDatabaseDisplayModel->setSearchLanguage(CardSearchLanguage{
|
||||
cardsDisplay.getCardLang(), static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
||||
};
|
||||
applyCardSearchLanguage();
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
|
||||
chooseTokenFromAllRadioButton = new QRadioButton(tr("Show &all tokens"));
|
||||
connect(chooseTokenFromAllRadioButton, &QRadioButton::toggled, this, &DlgCreateToken::actChooseTokenFromAll);
|
||||
chooseTokenFromDeckRadioButton = new QRadioButton(tr("Show tokens from this &deck"));
|
||||
|
|
|
|||
|
|
@ -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,10 @@ 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, CardSearchLanguage{cardsDisplay.getCardLang(),
|
||||
static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
||||
reorganizeCards();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,12 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player,
|
|||
}
|
||||
|
||||
connect(&searchEdit, &QLineEdit::textChanged, zone, &ZoneViewZone::setFilterString);
|
||||
|
||||
const auto applyCardSearchLanguage = [this] { zone->setFilterString(searchEdit.text()); };
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
}
|
||||
|
||||
setLayout(vbox);
|
||||
|
|
|
|||
|
|
@ -94,8 +94,7 @@ void CardPictureToLoad::populateSetUrls()
|
|||
}
|
||||
}
|
||||
|
||||
const QStringList orderedTemplates = urlTemplates;
|
||||
for (const QString &urlTemplate : orderedTemplates) {
|
||||
for (const QString &urlTemplate : urlTemplates) {
|
||||
QString transformedUrl = transformUrl(urlTemplate);
|
||||
|
||||
if (!transformedUrl.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
#include <QHeaderView>
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
|
||||
DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent, CardDatabaseModel *databaseModel)
|
||||
: QWidget(parent)
|
||||
|
|
@ -40,6 +42,17 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent
|
|||
databaseDisplayModel->setSourceModel(databaseModel);
|
||||
databaseDisplayModel->setFilterKeyColumn(0);
|
||||
|
||||
const auto applyCardSearchLanguage = [this]() {
|
||||
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
|
||||
databaseDisplayModel->setSearchLanguage(CardSearchLanguage{
|
||||
cardsDisplay.getCardLang(), static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
||||
};
|
||||
applyCardSearchLanguage();
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
|
||||
databaseView = new CardDatabaseView(this, databaseDisplayModel);
|
||||
databaseView->setObjectName("databaseView");
|
||||
databaseView->setFocusProxy(searchEdit);
|
||||
|
|
|
|||
|
|
@ -340,16 +340,27 @@ void ChatView::appendMessage(QString message,
|
|||
pos.relativePosition = match.captured(0).length(); // set message start
|
||||
auto before = match.captured(1);
|
||||
auto sentBy = match.captured(2);
|
||||
|
||||
// The user level is not carried in the room chat history, so history
|
||||
// entries used to render as fixed-level user tags. Resolve online users
|
||||
// against the user list to turn their history entries into full user
|
||||
// tags (correct level, name casing and moderation context menu).
|
||||
QString displayName = sentBy;
|
||||
// Offline users have no known level; render them as zero-level tags.
|
||||
QString levelMarker = "0";
|
||||
if (const ServerInfo_User *onlineUser = userListProxy->getOnlineUser(sentBy)) {
|
||||
displayName = QString::fromStdString(onlineUser->name());
|
||||
levelMarker = QString::number(onlineUser->user_level());
|
||||
}
|
||||
|
||||
cursor.insertText(before); // add message timestamp
|
||||
QTextCharFormat senderFormat(defaultFormat);
|
||||
senderFormat.setAnchor(true);
|
||||
// this underscore is important, it is used to add the user level, but in this case the level is
|
||||
// unknown, if the name contains an underscore it would split up the name
|
||||
senderFormat.setAnchorHref("user://_" + sentBy);
|
||||
senderFormat.setAnchorHref("user://" + levelMarker + "_" + displayName);
|
||||
cursor.setCharFormat(senderFormat);
|
||||
cursor.insertText(sentBy); // add username with href so it shows the menu
|
||||
userMessagePositions[sentBy].append(pos); // save message position
|
||||
message.remove(0, pos.relativePosition - 2); // do not remove semicolon
|
||||
cursor.insertText(displayName); // add username with href so it shows the menu
|
||||
userMessagePositions[displayName].append(pos); // save message position
|
||||
message.remove(0, pos.relativePosition - 2); // do not remove semicolon
|
||||
}
|
||||
} else {
|
||||
//! \todo Remove hardcoded color.
|
||||
|
|
|
|||
|
|
@ -63,13 +63,26 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
connect(&cardLanguageBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&GeneralSettingsPage::cardLanguageBoxChanged);
|
||||
|
||||
// card search language, independent of the card display language
|
||||
cardSearchLanguageBox.addItem(""); // texts set in retranslateUi
|
||||
cardSearchLanguageBox.addItem("");
|
||||
cardSearchLanguageBox.addItem("");
|
||||
const int cardSearchLanguageIndex = SettingsCache::instance().cardsDisplay().getCardSearchLanguage();
|
||||
cardSearchLanguageBox.setCurrentIndex(cardSearchLanguageIndex < 0 ? static_cast<int>(SearchLanguageMode::English)
|
||||
: cardSearchLanguageIndex);
|
||||
|
||||
connect(&cardSearchLanguageBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&GeneralSettingsPage::cardSearchLanguageBoxChanged);
|
||||
|
||||
auto *languageGrid = new QGridLayout;
|
||||
languageGrid->addWidget(&languageLabel, 0, 0);
|
||||
languageGrid->addWidget(&languageBox, 0, 1);
|
||||
languageGrid->addWidget(&cardLanguageLabel, 1, 0);
|
||||
languageGrid->addWidget(&cardLanguageBox, 1, 1);
|
||||
languageGrid->addWidget(&cardLanguageNoteLabel, 2, 1);
|
||||
languageGrid->addWidget(&advertiseTranslationPageLabel, 3, 1, Qt::AlignRight);
|
||||
languageGrid->addWidget(&cardSearchLanguageLabel, 3, 0);
|
||||
languageGrid->addWidget(&cardSearchLanguageBox, 3, 1);
|
||||
languageGrid->addWidget(&advertiseTranslationPageLabel, 4, 1, Qt::AlignRight);
|
||||
|
||||
cardLanguageNoteLabel.setWordWrap(true);
|
||||
cardLanguageNoteLabel.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
|
|
@ -481,6 +494,11 @@ void GeneralSettingsPage::cardLanguageBoxChanged(int index)
|
|||
}
|
||||
}
|
||||
|
||||
void GeneralSettingsPage::cardSearchLanguageBoxChanged(int index)
|
||||
{
|
||||
SettingsCache::instance().cardsDisplay().setCardSearchLanguage(index);
|
||||
}
|
||||
|
||||
void GeneralSettingsPage::updateStartupServerControlsVisibility()
|
||||
{
|
||||
const int index = startupTabSelector.currentIndex();
|
||||
|
|
@ -502,6 +520,12 @@ void GeneralSettingsPage::retranslateUi()
|
|||
cardLanguageLabel.setText(tr("Card text & images language:"));
|
||||
cardLanguageNoteLabel.setText(
|
||||
tr("Foreign card names, text and art apply after you update the card database (Oracle)."));
|
||||
cardSearchLanguageLabel.setText(tr("Language used in card search:"));
|
||||
cardSearchLanguageBox.setItemText(static_cast<int>(SearchLanguageMode::English), tr("English"));
|
||||
cardSearchLanguageBox.setItemText(static_cast<int>(SearchLanguageMode::Selected),
|
||||
tr("Selected card language (untranslated cards still match in English)"));
|
||||
cardSearchLanguageBox.setItemText(static_cast<int>(SearchLanguageMode::Both),
|
||||
tr("English and selected card language"));
|
||||
advertiseTranslationPageLabel.setText(
|
||||
QString("<a href='%1'>%2</a>").arg(WIKI_TRANSLATION_FAQ).arg(tr("How to help with translations")));
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ private slots:
|
|||
void resetAllPathsClicked();
|
||||
void languageBoxChanged(int index);
|
||||
void cardLanguageBoxChanged(int index);
|
||||
void cardSearchLanguageBoxChanged(int index);
|
||||
void updateStartupServerControlsVisibility();
|
||||
|
||||
private:
|
||||
|
|
@ -55,6 +56,9 @@ private:
|
|||
QComboBox cardLanguageBox;
|
||||
QLabel cardLanguageNoteLabel;
|
||||
|
||||
QLabel cardSearchLanguageLabel;
|
||||
QComboBox cardSearchLanguageBox;
|
||||
|
||||
QLabel updateReleaseChannelLabel;
|
||||
QComboBox updateReleaseChannelBox;
|
||||
QCheckBox startupUpdateCheckCheckBox;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "completer_utils.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "card_completer_styler.h"
|
||||
|
||||
#include <QCompleter>
|
||||
|
|
@ -7,13 +8,26 @@
|
|||
#include <QObject>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringListModel>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/models/database/card/card_completer_proxy_model.h>
|
||||
#include <libcockatrice/models/database/card/card_search_model.h>
|
||||
#include <libcockatrice/models/database/card_database_display_model.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
void applyCardSearchLanguage(CardSearchModel *searchModel)
|
||||
{
|
||||
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
|
||||
searchModel->setSearchLanguage(CardSearchLanguage{
|
||||
cardsDisplay.getCardLang(), static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CardCompleterSetup createCardCompleter(CardDatabaseDisplayModel *displayModel, QObject *parent, int maxVisibleItems)
|
||||
{
|
||||
auto *searchModel = new CardSearchModel(displayModel, parent);
|
||||
applyCardSearchLanguage(searchModel);
|
||||
|
||||
auto *proxyModel = new CardCompleterProxyModel(parent);
|
||||
proxyModel->setSourceModel(searchModel);
|
||||
|
|
@ -27,6 +41,12 @@ CardCompleterSetup createCardCompleter(CardDatabaseDisplayModel *displayModel, Q
|
|||
completer->setMaxVisibleItems(maxVisibleItems);
|
||||
CardCompleterStyler::apply(completer);
|
||||
|
||||
auto *cardsDisplay = &SettingsCache::instance().cardsDisplay();
|
||||
QObject::connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, searchModel,
|
||||
[searchModel] { applyCardSearchLanguage(searchModel); });
|
||||
QObject::connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, searchModel,
|
||||
[searchModel] { applyCardSearchLanguage(searchModel); });
|
||||
|
||||
return {searchModel, proxyModel, completer};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,17 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
|||
databaseDisplayModel->setSourceModel(database_model);
|
||||
databaseDisplayModel->setFilterKeyColumn(0);
|
||||
|
||||
const auto applyCardSearchLanguage = [this]() {
|
||||
const CardsDisplaySettings &cardsDisplay = SettingsCache::instance().cardsDisplay();
|
||||
databaseDisplayModel->setSearchLanguage(CardSearchLanguage{
|
||||
cardsDisplay.getCardLang(), static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
||||
};
|
||||
applyCardSearchLanguage();
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardSearchLanguageChanged, this,
|
||||
applyCardSearchLanguage);
|
||||
|
||||
cards = new QList<ExactCard>;
|
||||
connect(databaseDisplayModel, &CardDatabaseDisplayModel::modelDirty, this,
|
||||
&VisualDatabaseDisplayWidget::modelDirty);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -215,7 +218,10 @@ void VisualDeckStorageSortFilterProxyModel::updateSearchMatches()
|
|||
return;
|
||||
}
|
||||
|
||||
DeckFilterString filterString(searchText);
|
||||
const auto &cardsDisplay = SettingsCache::instance().cardsDisplay();
|
||||
DeckFilterString filterString(
|
||||
searchText, CardSearchLanguage{cardsDisplay.getCardLang(),
|
||||
static_cast<SearchLanguageMode>(cardsDisplay.getCardSearchLanguage())});
|
||||
for (int row = 0; row < count; ++row) {
|
||||
const DeckPreviewData &data = source->dataForRow(row);
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
@ -118,6 +119,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);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,42 @@
|
|||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
* @brief The card languages card search should run against.
|
||||
*/
|
||||
enum class SearchLanguageMode
|
||||
{
|
||||
English, ///< Only search the English card names and texts.
|
||||
Selected, ///< Search the selected card language (untranslated cards still match in English).
|
||||
Both ///< Search both the English and the selected card language names and texts.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The card language and matching mode searches run against.
|
||||
*
|
||||
* Bundles the card language code configured in the settings with the
|
||||
* SearchLanguageMode, so entry points take one value instead of two related
|
||||
* parameters.
|
||||
*/
|
||||
struct CardSearchLanguage
|
||||
{
|
||||
QString language; ///< Card language code (e.g. "de"); empty means the English fallback.
|
||||
SearchLanguageMode mode = SearchLanguageMode::English; ///< How the language participates in the search.
|
||||
|
||||
/**
|
||||
* @brief Whether only the English card data is searched.
|
||||
*
|
||||
* @return True when no card language is selected or English itself is selected.
|
||||
*/
|
||||
[[nodiscard]] bool isEnglishOnly() const
|
||||
{
|
||||
return language.isEmpty() || language == QLatin1String("en");
|
||||
}
|
||||
|
||||
bool operator==(const CardSearchLanguage &) const = default;
|
||||
bool operator!=(const CardSearchLanguage &) const = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @namespace CardLocalization
|
||||
* @ingroup Cards
|
||||
|
|
|
|||
|
|
@ -74,6 +74,36 @@ NumericValue <- [0-9]+
|
|||
|
||||
static std::once_flag init;
|
||||
|
||||
// The peglib parser rules (and therefore their rule actions) are set up once per
|
||||
// process, so a rule action cannot capture per-instance state. The card language
|
||||
// plain-text name and text queries search in is therefore handed to the GenericQuery
|
||||
// and OracleQuery rule actions through this thread-local context, which is live only
|
||||
// while a FilterString is being parsed. The rule actions copy it into the filter
|
||||
// closures they produce, so card evaluation never reads process-global state.
|
||||
thread_local CardSearchLanguage searchLanguageContext;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool matchesInSearchLanguage(const QString &english,
|
||||
const QString &localized,
|
||||
const CardSearchLanguage &searchLanguage,
|
||||
const StringMatcher &matcher)
|
||||
{
|
||||
if (searchLanguage.mode == SearchLanguageMode::English) {
|
||||
return matcher(english);
|
||||
}
|
||||
|
||||
if (searchLanguage.mode == SearchLanguageMode::Both) {
|
||||
if (!searchLanguage.isEnglishOnly() && matcher(localized)) {
|
||||
return true;
|
||||
}
|
||||
return matcher(english);
|
||||
}
|
||||
|
||||
return searchLanguage.isEnglishOnly() ? matcher(english) : matcher(localized);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static void setupParserRules()
|
||||
{
|
||||
auto passthru = [](const peg::SemanticValues &sv) -> Filter {
|
||||
|
|
@ -333,7 +363,11 @@ static void setupParserRules()
|
|||
|
||||
search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
const auto matcher = std::any_cast<StringMatcher>(sv[0]);
|
||||
return [=](const CardData &x) { return matcher(x->getText()); };
|
||||
const CardSearchLanguage searchLanguage = searchLanguageContext;
|
||||
return [=](const CardData &x) {
|
||||
return matchesInSearchLanguage(x->getText(), x->getLocalizedText(searchLanguage.language), searchLanguage,
|
||||
matcher);
|
||||
};
|
||||
};
|
||||
|
||||
search["ColorQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
|
|
@ -410,7 +444,11 @@ static void setupParserRules()
|
|||
};
|
||||
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||
const auto matcher = std::any_cast<StringMatcher>(sv[0]);
|
||||
return [=](const CardData &x) { return matcher(x->getName()); };
|
||||
const CardSearchLanguage searchLanguage = searchLanguageContext;
|
||||
return [=](const CardData &x) {
|
||||
return matchesInSearchLanguage(x->getName(), x->getLocalizedName(searchLanguage.language), searchLanguage,
|
||||
matcher);
|
||||
};
|
||||
};
|
||||
|
||||
search["Color"] = [](const peg::SemanticValues &sv) -> char { return "WUBRGU"[sv.choice()]; };
|
||||
|
|
@ -425,7 +463,7 @@ FilterString::FilterString()
|
|||
_error = "Not initialized";
|
||||
}
|
||||
|
||||
FilterString::FilterString(const QString &expr)
|
||||
FilterString::FilterString(const QString &expr, const CardSearchLanguage &searchLanguage)
|
||||
{
|
||||
QByteArray ba = expr.simplified().toUtf8();
|
||||
|
||||
|
|
@ -438,6 +476,8 @@ FilterString::FilterString(const QString &expr)
|
|||
return;
|
||||
}
|
||||
|
||||
searchLanguageContext = searchLanguage;
|
||||
|
||||
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));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <QString>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <utility>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(FilterStringLog, "filter_string");
|
||||
|
|
@ -35,7 +36,7 @@ class FilterString
|
|||
{
|
||||
public:
|
||||
FilterString();
|
||||
explicit FilterString(const QString &exp);
|
||||
explicit FilterString(const QString &exp, const CardSearchLanguage &searchLanguage = {});
|
||||
[[nodiscard]] bool check(const CardData &card) const
|
||||
{
|
||||
if (card.isNull()) {
|
||||
|
|
|
|||
|
|
@ -100,14 +100,16 @@ FilterTreeNode *FilterItemList::termNode(const QString &term)
|
|||
return childNodes.at(i);
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeAnd(const CardInfoPtr info, CardFilter::Attr attr) const
|
||||
bool FilterItemList::testTypeAnd(const CardInfoPtr info,
|
||||
CardFilter::Attr attr,
|
||||
const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) {
|
||||
if (!(*i)->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(*i)->acceptCardAttr(info, attr)) {
|
||||
if (!(*i)->acceptCardAttr(info, attr, searchLanguage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -115,13 +117,17 @@ bool FilterItemList::testTypeAnd(const CardInfoPtr info, CardFilter::Attr attr)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeAndNot(const CardInfoPtr info, CardFilter::Attr attr) const
|
||||
bool FilterItemList::testTypeAndNot(const CardInfoPtr info,
|
||||
CardFilter::Attr attr,
|
||||
const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
// if any one in the list is true, return false
|
||||
return !testTypeOr(info, attr);
|
||||
return !testTypeOr(info, attr, searchLanguage);
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) const
|
||||
bool FilterItemList::testTypeOr(const CardInfoPtr info,
|
||||
CardFilter::Attr attr,
|
||||
const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
bool noChildEnabledChild = true;
|
||||
|
||||
|
|
@ -134,7 +140,7 @@ bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) c
|
|||
noChildEnabledChild = false;
|
||||
}
|
||||
|
||||
if ((*i)->acceptCardAttr(info, attr)) {
|
||||
if ((*i)->acceptCardAttr(info, attr, searchLanguage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,20 +148,58 @@ bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) c
|
|||
return noChildEnabledChild;
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeOrNot(const CardInfoPtr info, CardFilter::Attr attr) const
|
||||
bool FilterItemList::testTypeOrNot(const CardInfoPtr info,
|
||||
CardFilter::Attr attr,
|
||||
const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
// if any one in the list is false, return true
|
||||
return !testTypeAnd(info, attr);
|
||||
return !testTypeAnd(info, attr, searchLanguage);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptName(const CardInfoPtr info) const
|
||||
bool FilterItem::acceptName(const CardInfoPtr info, const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
return info->getName().contains(term, Qt::CaseInsensitive);
|
||||
const QString &englishName = info->getName();
|
||||
const QString &localizedName = info->getLocalizedName(searchLanguage.language);
|
||||
|
||||
switch (searchLanguage.mode) {
|
||||
case SearchLanguageMode::English:
|
||||
return englishName.contains(term, Qt::CaseInsensitive);
|
||||
case SearchLanguageMode::Both:
|
||||
if (englishName.contains(term, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
return !searchLanguage.isEnglishOnly() && localizedName.contains(term, Qt::CaseInsensitive);
|
||||
case SearchLanguageMode::Selected:
|
||||
if (searchLanguage.isEnglishOnly()) {
|
||||
return englishName.contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
return localizedName.contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptNameExact(const CardInfoPtr info) const
|
||||
bool FilterItem::acceptNameExact(const CardInfoPtr info, const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
return info->getName() == term;
|
||||
const QString &englishName = info->getName();
|
||||
const QString &localizedName = info->getLocalizedName(searchLanguage.language);
|
||||
|
||||
switch (searchLanguage.mode) {
|
||||
case SearchLanguageMode::English:
|
||||
return englishName == term;
|
||||
case SearchLanguageMode::Both:
|
||||
if (englishName == term) {
|
||||
return true;
|
||||
}
|
||||
return !searchLanguage.isEnglishOnly() && localizedName == term;
|
||||
case SearchLanguageMode::Selected:
|
||||
if (searchLanguage.isEnglishOnly()) {
|
||||
return englishName == term;
|
||||
}
|
||||
return localizedName == term;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptType(const CardInfoPtr info) const
|
||||
|
|
@ -213,9 +257,27 @@ bool FilterItem::acceptColor(const CardInfoPtr info) const
|
|||
return match_count == converted_term.length();
|
||||
}
|
||||
|
||||
bool FilterItem::acceptText(const CardInfoPtr info) const
|
||||
bool FilterItem::acceptText(const CardInfoPtr info, const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
return info->getText().contains(term, Qt::CaseInsensitive);
|
||||
const QString &englishText = info->getText();
|
||||
const QString &localizedText = info->getLocalizedText(searchLanguage.language);
|
||||
|
||||
switch (searchLanguage.mode) {
|
||||
case SearchLanguageMode::English:
|
||||
return englishText.contains(term, Qt::CaseInsensitive);
|
||||
case SearchLanguageMode::Both:
|
||||
if (englishText.contains(term, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
return !searchLanguage.isEnglishOnly() && localizedText.contains(term, Qt::CaseInsensitive);
|
||||
case SearchLanguageMode::Selected:
|
||||
if (searchLanguage.isEnglishOnly()) {
|
||||
return englishText.contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
return localizedText.contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptSet(const CardInfoPtr info) const
|
||||
|
|
@ -402,19 +464,21 @@ bool FilterItem::relationCheck(int cardInfo) const
|
|||
return result;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptCardAttr(const CardInfoPtr info, CardFilter::Attr attr) const
|
||||
bool FilterItem::acceptCardAttr(const CardInfoPtr info,
|
||||
CardFilter::Attr attr,
|
||||
const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
switch (attr) {
|
||||
case CardFilter::AttrName:
|
||||
return acceptName(info);
|
||||
return acceptName(info, searchLanguage);
|
||||
case CardFilter::AttrNameExact:
|
||||
return acceptNameExact(info);
|
||||
return acceptNameExact(info, searchLanguage);
|
||||
case CardFilter::AttrType:
|
||||
return acceptType(info);
|
||||
case CardFilter::AttrColor:
|
||||
return acceptColor(info);
|
||||
case CardFilter::AttrText:
|
||||
return acceptText(info);
|
||||
return acceptText(info, searchLanguage);
|
||||
case CardFilter::AttrSet:
|
||||
return acceptSet(info);
|
||||
case CardFilter::AttrManaCost:
|
||||
|
|
@ -484,18 +548,18 @@ FilterTreeNode *FilterTree::termNode(const CardFilter *f)
|
|||
return termNode(f->attr(), f->type(), f->term());
|
||||
}
|
||||
|
||||
bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const
|
||||
bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm, const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
const FilterItemList *fil;
|
||||
bool status = true;
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeAnd);
|
||||
if (fil && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr)) {
|
||||
if (fil && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr, searchLanguage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeAndNot);
|
||||
if (fil && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr)) {
|
||||
if (fil && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr, searchLanguage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -504,23 +568,23 @@ bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const
|
|||
status = false;
|
||||
|
||||
// if this is true we can return because it is OR'd with the OrNot list
|
||||
if (fil->testTypeOr(info, lm->attr)) {
|
||||
if (fil->testTypeOr(info, lm->attr, searchLanguage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeOrNot);
|
||||
if (fil && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr)) {
|
||||
if (fil && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr, searchLanguage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool FilterTree::acceptsCard(const CardInfoPtr info) const
|
||||
bool FilterTree::acceptsCard(const CardInfoPtr info, const CardSearchLanguage &searchLanguage) const
|
||||
{
|
||||
for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) {
|
||||
if ((*i)->isEnabled() && !testAttr(info, *i)) {
|
||||
if ((*i)->isEnabled() && !testAttr(info, *i, searchLanguage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -167,10 +168,14 @@ public:
|
|||
return CardFilter::typeName(type);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool testTypeAnd(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||
[[nodiscard]] bool testTypeAndNot(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||
[[nodiscard]] bool testTypeOr(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||
[[nodiscard]] bool testTypeOrNot(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||
[[nodiscard]] bool
|
||||
testTypeAnd(CardInfoPtr info, CardFilter::Attr attr, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool
|
||||
testTypeAndNot(CardInfoPtr info, CardFilter::Attr attr, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool
|
||||
testTypeOr(CardInfoPtr info, CardFilter::Attr attr, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool
|
||||
testTypeOrNot(CardInfoPtr info, CardFilter::Attr attr, const CardSearchLanguage &searchLanguage) const;
|
||||
};
|
||||
|
||||
class FilterItem : public FilterTreeNode
|
||||
|
|
@ -207,20 +212,21 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool acceptName(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptNameExact(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptName(CardInfoPtr info, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool acceptNameExact(CardInfoPtr info, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool acceptType(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptMainType(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptSubType(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptColor(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptText(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptText(CardInfoPtr info, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool acceptSet(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptManaCost(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptCmc(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptPowerToughness(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||
[[nodiscard]] bool acceptLoyalty(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptRarity(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||
[[nodiscard]] bool
|
||||
acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool acceptFormat(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool relationCheck(int cardInfo) const;
|
||||
};
|
||||
|
|
@ -240,7 +246,7 @@ private:
|
|||
LogicMap *attrLogicMap(CardFilter::Attr attr);
|
||||
FilterItemList *attrTypeList(CardFilter::Attr attr, CardFilter::Type type);
|
||||
|
||||
bool testAttr(CardInfoPtr info, const LogicMap *lm) const;
|
||||
bool testAttr(CardInfoPtr info, const LogicMap *lm, const CardSearchLanguage &searchLanguage) const;
|
||||
|
||||
void nodeChanged() const override
|
||||
{
|
||||
|
|
@ -279,7 +285,7 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool acceptsCard(CardInfoPtr info) const;
|
||||
[[nodiscard]] bool acceptsCard(CardInfoPtr info, const CardSearchLanguage &searchLanguage) const;
|
||||
void removeFiltersByAttr(CardFilter::Attr filterType);
|
||||
void removeFilter(const CardFilter *toRemove);
|
||||
void clear();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public:
|
|||
[[nodiscard]] virtual int getArchidektPreviewSize() const = 0;
|
||||
[[nodiscard]] virtual int getSampleHandSize() const = 0;
|
||||
[[nodiscard]] virtual QString getCardLang() const = 0;
|
||||
[[nodiscard]] virtual int getCardSearchLanguage() const = 0;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_INTERFACE_CARDS_DISPLAY_SETTINGS_PROVIDER_H
|
||||
|
|
|
|||
|
|
@ -65,25 +65,31 @@ void CardSearchModel::updateSearchResults(const QString &query)
|
|||
continue;
|
||||
}
|
||||
|
||||
const QString lowerName = card->getName().toLower();
|
||||
if (!lowerName.contains(lowerQuery)) {
|
||||
continue;
|
||||
}
|
||||
// The completer suggestions match against the same languages the card
|
||||
// search uses, so typing a localized name finds the card. In Both mode
|
||||
// either language can match.
|
||||
for (const QString &matchName : searchableNames(card)) {
|
||||
const QString lowerName = matchName.toLower();
|
||||
if (!lowerName.contains(lowerQuery)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int distance = levenshteinDistance(lowerQuery, lowerName);
|
||||
const int distance = levenshteinDistance(lowerQuery, lowerName);
|
||||
|
||||
if (lowerName.startsWith(lowerQuery)) {
|
||||
prefixMatches.append({card, distance});
|
||||
} else {
|
||||
containsMatches.append({card, distance});
|
||||
if (lowerName.startsWith(lowerQuery)) {
|
||||
prefixMatches.append({card, distance});
|
||||
} else {
|
||||
containsMatches.append({card, distance});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto sortByDistanceThenLength = [](const SearchResult &a, const SearchResult &b) {
|
||||
auto sortByDistanceThenLength = [this](const SearchResult &a, const SearchResult &b) {
|
||||
if (a.distance != b.distance) {
|
||||
return a.distance < b.distance;
|
||||
}
|
||||
return a.card->getName().size() < b.card->getName().size();
|
||||
return sortableName(a.card).size() < sortableName(b.card).size();
|
||||
};
|
||||
|
||||
std::sort(prefixMatches.begin(), prefixMatches.end(), sortByDistanceThenLength);
|
||||
|
|
@ -101,3 +107,25 @@ void CardSearchModel::updateSearchResults(const QString &query)
|
|||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QStringList CardSearchModel::searchableNames(const CardInfoPtr &card) const
|
||||
{
|
||||
if (searchLanguage.isEnglishOnly()) {
|
||||
return {card->getName()};
|
||||
}
|
||||
|
||||
const QString localizedName = card->getLocalizedName(searchLanguage.language);
|
||||
if (searchLanguage.mode == SearchLanguageMode::Selected) {
|
||||
return {localizedName};
|
||||
}
|
||||
|
||||
return {card->getName(), localizedName};
|
||||
}
|
||||
|
||||
QString CardSearchModel::sortableName(const CardInfoPtr &card) const
|
||||
{
|
||||
if (searchLanguage.isEnglishOnly()) {
|
||||
return card->getName();
|
||||
}
|
||||
return card->getLocalizedName(searchLanguage.language);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,14 @@ public:
|
|||
|
||||
void updateSearchResults(const QString &query); // Update results based on input
|
||||
|
||||
void setSearchLanguage(const CardSearchLanguage &searchLang)
|
||||
{
|
||||
if (searchLanguage == searchLang) {
|
||||
return;
|
||||
}
|
||||
searchLanguage = searchLang;
|
||||
}
|
||||
|
||||
private:
|
||||
struct SearchResult
|
||||
{
|
||||
|
|
@ -34,8 +42,15 @@ private:
|
|||
int distance;
|
||||
};
|
||||
|
||||
/** @brief The names a card is searched by with the current search language. */
|
||||
[[nodiscard]] QStringList searchableNames(const CardInfoPtr &card) const;
|
||||
|
||||
/** @brief The name used to break distance ties when sorting suggestions. */
|
||||
[[nodiscard]] QString sortableName(const CardInfoPtr &card) const;
|
||||
|
||||
CardDatabaseDisplayModel *sourceModel;
|
||||
QList<SearchResult> searchResults;
|
||||
CardSearchLanguage searchLanguage;
|
||||
};
|
||||
|
||||
#endif // CARD_SEARCH_MODEL_H
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
|||
}
|
||||
|
||||
if (filterString != nullptr) {
|
||||
if (filterTree != nullptr && !filterTree->acceptsCard(info)) {
|
||||
if (filterTree != nullptr && !filterTree->acceptsCard(info, searchLanguage)) {
|
||||
return false;
|
||||
}
|
||||
return filterString->check(info);
|
||||
|
|
@ -190,8 +190,14 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
|||
|
||||
bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const
|
||||
{
|
||||
if (!cardName.isEmpty() && !info->getName().contains(cardName, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
if (!cardName.isEmpty()) {
|
||||
const bool matchesEnglish = info->getName().contains(cardName, Qt::CaseInsensitive);
|
||||
const bool matchesLocalized =
|
||||
!searchLanguage.isEnglishOnly() &&
|
||||
info->getLocalizedName(searchLanguage.language).contains(cardName, Qt::CaseInsensitive);
|
||||
if (!matchesEnglish && !matchesLocalized) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cardNameSet.isEmpty() && !cardNameSet.contains(info->getName())) {
|
||||
|
|
@ -199,7 +205,7 @@ bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const
|
|||
}
|
||||
|
||||
if (filterTree != nullptr) {
|
||||
return filterTree->acceptsCard(info);
|
||||
return filterTree->acceptsCard(info, searchLanguage);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -235,6 +241,28 @@ void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree)
|
|||
invalidate();
|
||||
}
|
||||
|
||||
void CardDatabaseDisplayModel::setStringFilter(const QString &_src)
|
||||
{
|
||||
searchText = _src;
|
||||
delete filterString;
|
||||
filterString = new FilterString(_src, searchLanguage);
|
||||
dirty();
|
||||
}
|
||||
|
||||
void CardDatabaseDisplayModel::setSearchLanguage(const CardSearchLanguage &searchLang)
|
||||
{
|
||||
if (searchLanguage == searchLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
searchLanguage = searchLang;
|
||||
|
||||
if (filterString != nullptr) {
|
||||
setStringFilter(searchText);
|
||||
}
|
||||
dirty();
|
||||
}
|
||||
|
||||
void CardDatabaseDisplayModel::filterTreeChanged()
|
||||
{
|
||||
invalidate();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QTimer>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/filters/filter_string.h>
|
||||
|
||||
class FilterTree;
|
||||
|
|
@ -32,6 +33,8 @@ private:
|
|||
FilterString *filterString;
|
||||
int loadedRowCount;
|
||||
QTimer dirtyTimer;
|
||||
CardSearchLanguage searchLanguage;
|
||||
QString searchText;
|
||||
|
||||
/** The translation table that will be used for sanitizeCardName. */
|
||||
static QMap<wchar_t, wchar_t> characterTranslation;
|
||||
|
|
@ -55,17 +58,13 @@ public:
|
|||
cardName = sanitizeCardName(_cardName, characterTranslation);
|
||||
dirty();
|
||||
}
|
||||
void setStringFilter(const QString &_src)
|
||||
{
|
||||
delete filterString;
|
||||
filterString = new FilterString(_src);
|
||||
dirty();
|
||||
}
|
||||
void setStringFilter(const QString &_src);
|
||||
void setCardNameSet(const QSet<QString> &_cardNameSet)
|
||||
{
|
||||
cardNameSet = _cardNameSet;
|
||||
dirty();
|
||||
}
|
||||
void setSearchLanguage(const CardSearchLanguage &searchLang);
|
||||
|
||||
void dirty()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@ QString CardsDisplaySettings::getCardLang() const
|
|||
return getValue("cardLang", QString(), QString(), "en").toString();
|
||||
}
|
||||
|
||||
int CardsDisplaySettings::getCardSearchLanguage() const
|
||||
{
|
||||
return getValue("cardSearchLanguage", QString(), QString(), static_cast<int>(SearchLanguageMode::English)).toInt();
|
||||
}
|
||||
|
||||
void CardsDisplaySettings::setDisplayCardNames(bool _displayCardNames)
|
||||
{
|
||||
setValue(_displayCardNames, "displayCardNames");
|
||||
|
|
@ -242,3 +247,12 @@ void CardsDisplaySettings::setCardLang(const QString &_cardLang)
|
|||
sync();
|
||||
emit cardLangChanged(_cardLang);
|
||||
}
|
||||
|
||||
void CardsDisplaySettings::setCardSearchLanguage(int _cardSearchLanguage)
|
||||
{
|
||||
if (_cardSearchLanguage == getCardSearchLanguage()) {
|
||||
return;
|
||||
}
|
||||
setValue(_cardSearchLanguage, "cardSearchLanguage");
|
||||
emit cardSearchLanguageChanged(_cardSearchLanguage);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "settings_manager.h"
|
||||
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/interfaces/interface_cards_display_settings_provider.h>
|
||||
|
||||
class CardsDisplaySettings : public SettingsManager, public ICardsDisplaySettingsProvider
|
||||
|
|
@ -32,6 +33,7 @@ public:
|
|||
[[nodiscard]] int getArchidektPreviewSize() const override;
|
||||
[[nodiscard]] int getSampleHandSize() const override;
|
||||
[[nodiscard]] QString getCardLang() const override;
|
||||
[[nodiscard]] int getCardSearchLanguage() const override;
|
||||
|
||||
void setDisplayCardNames(bool _displayCardNames);
|
||||
void setRoundCardCorners(bool _roundCardCorners);
|
||||
|
|
@ -54,6 +56,7 @@ public:
|
|||
void setArchidektPreviewCardSize(int _archidektPreviewCardSize);
|
||||
void setSampleHandSize(int _sampleHandSize);
|
||||
void setCardLang(const QString &_cardLang);
|
||||
void setCardSearchLanguage(int _cardSearchLanguage);
|
||||
|
||||
signals:
|
||||
void displayCardNamesChanged();
|
||||
|
|
@ -71,6 +74,7 @@ signals:
|
|||
void archidektPreviewSizeChanged();
|
||||
void sampleHandSizeChanged(int amount);
|
||||
void cardLangChanged(const QString &lang);
|
||||
void cardSearchLanguageChanged(int cardSearchLanguage);
|
||||
|
||||
public:
|
||||
explicit CardsDisplaySettings(const QString &settingPath, QObject *parent = nullptr);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,57 @@ QUERY(Color4, cat, "c!gw", false)
|
|||
|
||||
QUERY(BracketNextToUnquotedString, cat, "(o:woof OR o:meow)", true)
|
||||
|
||||
CardInfoPtr localizedCat()
|
||||
{
|
||||
CardInfoPtr localized = CardInfo::newInstance("Cat", "Meow!", false, {}, {}, {}, {}, {});
|
||||
localized->setLocalizedName("de", "Kater");
|
||||
localized->setLocalizedText("de", "miaut");
|
||||
return localized;
|
||||
}
|
||||
|
||||
TEST_F(CardQuery, SearchLanguageEnglishMatchesOnlyEnglish)
|
||||
{
|
||||
const CardData localized = localizedCat();
|
||||
ASSERT_TRUE(FilterString("Cat", CardSearchLanguage{"de", SearchLanguageMode::English}).check(localized));
|
||||
ASSERT_FALSE(FilterString("Kater", CardSearchLanguage{"de", SearchLanguageMode::English}).check(localized));
|
||||
}
|
||||
|
||||
TEST_F(CardQuery, SearchLanguageSelectedMatchesLocalizedNameAndText)
|
||||
{
|
||||
const CardData localized = localizedCat();
|
||||
ASSERT_TRUE(FilterString("Kater", CardSearchLanguage{"de", SearchLanguageMode::Selected}).check(localized));
|
||||
ASSERT_TRUE(FilterString("o:miaut", CardSearchLanguage{"de", SearchLanguageMode::Selected}).check(localized));
|
||||
ASSERT_FALSE(FilterString("Cat", CardSearchLanguage{"de", SearchLanguageMode::Selected}).check(localized));
|
||||
}
|
||||
|
||||
TEST_F(CardQuery, SearchLanguageSelectedFallsBackToEnglishForUntranslatedCards)
|
||||
{
|
||||
const CardData localized = localizedCat();
|
||||
ASSERT_TRUE(FilterString("Cat", CardSearchLanguage{"fr", SearchLanguageMode::Selected}).check(localized));
|
||||
ASSERT_FALSE(FilterString("Kater", CardSearchLanguage{"fr", SearchLanguageMode::Selected}).check(localized));
|
||||
}
|
||||
|
||||
TEST_F(CardQuery, SearchLanguageBothMatchesEitherLanguage)
|
||||
{
|
||||
const CardData localized = localizedCat();
|
||||
ASSERT_TRUE(FilterString("Cat", CardSearchLanguage{"de", SearchLanguageMode::Both}).check(localized));
|
||||
ASSERT_TRUE(FilterString("Kater", CardSearchLanguage{"de", SearchLanguageMode::Both}).check(localized));
|
||||
}
|
||||
|
||||
TEST_F(CardQuery, SearchLanguageIsBoundPerInstance)
|
||||
{
|
||||
const CardData localized = localizedCat();
|
||||
|
||||
FilterString germanQuery("Kater", CardSearchLanguage{"de", SearchLanguageMode::Selected});
|
||||
ASSERT_TRUE(germanQuery.check(localized));
|
||||
|
||||
// Constructing an English-bound instance afterwards must not change the
|
||||
// language the earlier instance searches in.
|
||||
FilterString englishQuery("Kater", CardSearchLanguage{"", SearchLanguageMode::English});
|
||||
ASSERT_FALSE(englishQuery.check(localized));
|
||||
ASSERT_TRUE(germanQuery.check(localized));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
|||
|
|
@ -575,6 +575,19 @@ TEST_F(SettingsDefaultsTest, CardsDisplay_CardLang_SetAndGet)
|
|||
ASSERT_EQ(s.getCardLang(), QString("de"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, CardsDisplay_CardSearchLanguage_Default)
|
||||
{
|
||||
CardsDisplaySettings s(settingsPath, nullptr);
|
||||
ASSERT_EQ(s.getCardSearchLanguage(), static_cast<int>(SearchLanguageMode::English));
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, CardsDisplay_CardSearchLanguage_SetAndGet)
|
||||
{
|
||||
CardsDisplaySettings s(settingsPath, nullptr);
|
||||
s.setCardSearchLanguage(static_cast<int>(SearchLanguageMode::Selected));
|
||||
ASSERT_EQ(s.getCardSearchLanguage(), static_cast<int>(SearchLanguageMode::Selected));
|
||||
}
|
||||
|
||||
// --- VisualDeckStorageSettings ---
|
||||
|
||||
TEST_F(SettingsDefaultsTest, VisualDeckStorage_SortingOrder_Default)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue