mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-13 05:20:00 -08:00
Add MSE gender quick filter to Continuumatrice card browser
This commit is contained in:
parent
00a7feff25
commit
009137d538
10 changed files with 224 additions and 1 deletions
|
|
@ -295,6 +295,7 @@ set(cockatrice_SOURCES
|
|||
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
|
||||
src/interface/widgets/visual_database_display/visual_database_display_filter_toolbar_widget.cpp
|
||||
src/interface/widgets/visual_database_display/visual_database_display_format_legality_filter_widget.cpp
|
||||
src/interface/widgets/visual_database_display/visual_database_display_gender_filter_widget.cpp
|
||||
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
|
||||
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
|
||||
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "card_database_display_model.h"
|
||||
#include "visual_database_display_filter_save_load_widget.h"
|
||||
#include "visual_database_display_format_legality_filter_widget.h"
|
||||
#include "visual_database_display_gender_filter_widget.h"
|
||||
#include "visual_database_display_main_type_filter_widget.h"
|
||||
#include "visual_database_display_name_filter_widget.h"
|
||||
#include "visual_database_display_set_filter_widget.h"
|
||||
|
|
@ -103,6 +104,9 @@ VisualDatabaseDisplayFilterToolbarWidget::VisualDatabaseDisplayFilterToolbarWidg
|
|||
quickFilterSetWidget = new SettingsButtonWidget(this);
|
||||
quickFilterSetWidget->setButtonIcon(themePixmap(QStringLiteral("icons/scroll")));
|
||||
|
||||
quickFilterGenderWidget = new SettingsButtonWidget(this);
|
||||
quickFilterGenderWidget->setButtonIcon(themePixmap(QStringLiteral("icons/filter")));
|
||||
|
||||
quickFilterFormatLegalityWidget = new SettingsButtonWidget(this);
|
||||
quickFilterFormatLegalityWidget->setButtonIcon(themePixmap(QStringLiteral("icons/scale_balanced")));
|
||||
|
||||
|
|
@ -126,12 +130,14 @@ void VisualDatabaseDisplayFilterToolbarWidget::initialize()
|
|||
formatLegalityWidget = new VisualDatabaseDisplayFormatLegalityFilterWidget(this, filterModel);
|
||||
subTypeFilterWidget = new VisualDatabaseDisplaySubTypeFilterWidget(this, filterModel);
|
||||
setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel);
|
||||
genderFilterWidget = new VisualDatabaseDisplayGenderFilterWidget(this, filterModel);
|
||||
|
||||
quickFilterSaveLoadWidget->addSettingsWidget(saveLoadWidget);
|
||||
quickFilterNameWidget->addSettingsWidget(nameFilterWidget);
|
||||
quickFilterMainTypeWidget->addSettingsWidget(mainTypeFilterWidget);
|
||||
quickFilterSubTypeWidget->addSettingsWidget(subTypeFilterWidget);
|
||||
quickFilterSetWidget->addSettingsWidget(setFilterWidget);
|
||||
quickFilterGenderWidget->addSettingsWidget(genderFilterWidget);
|
||||
quickFilterFormatLegalityWidget->addSettingsWidget(formatLegalityWidget);
|
||||
|
||||
// fill groupbox layouts
|
||||
|
|
@ -144,6 +150,7 @@ void VisualDatabaseDisplayFilterToolbarWidget::initialize()
|
|||
filterLayout->addWidget(quickFilterMainTypeWidget);
|
||||
filterLayout->addWidget(quickFilterSubTypeWidget);
|
||||
filterLayout->addWidget(quickFilterSetWidget);
|
||||
filterLayout->addWidget(quickFilterGenderWidget);
|
||||
filterLayout->addWidget(quickFilterFormatLegalityWidget);
|
||||
|
||||
// put everything into main layout
|
||||
|
|
@ -171,6 +178,7 @@ void VisualDatabaseDisplayFilterToolbarWidget::retranslateUi()
|
|||
quickFilterMainTypeWidget->setToolTip(tr("Filter by card main-type"));
|
||||
quickFilterSubTypeWidget->setToolTip(tr("Filter by card sub-type"));
|
||||
quickFilterSetWidget->setToolTip(tr("Filter by set"));
|
||||
quickFilterGenderWidget->setToolTip(tr("Filter by card gender"));
|
||||
quickFilterFormatLegalityWidget->setToolTip(tr("Filter by format legality"));
|
||||
|
||||
quickFilterSaveLoadWidget->setButtonText(tr("Save/Load"));
|
||||
|
|
@ -178,6 +186,7 @@ void VisualDatabaseDisplayFilterToolbarWidget::retranslateUi()
|
|||
quickFilterMainTypeWidget->setButtonText(tr("Main Type"));
|
||||
quickFilterSubTypeWidget->setButtonText(tr("Sub Type"));
|
||||
quickFilterSetWidget->setButtonText(tr("Sets"));
|
||||
quickFilterGenderWidget->setButtonText(tr("Gender"));
|
||||
quickFilterFormatLegalityWidget->setButtonText(tr("Formats"));
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +202,8 @@ void VisualDatabaseDisplayFilterToolbarWidget::updateCompactMode(int availableWi
|
|||
|
||||
const QList<SettingsButtonWidget *> filterButtons = {
|
||||
quickFilterSaveLoadWidget, quickFilterNameWidget, quickFilterMainTypeWidget,
|
||||
quickFilterSubTypeWidget, quickFilterSetWidget, quickFilterFormatLegalityWidget,
|
||||
quickFilterSubTypeWidget, quickFilterSetWidget, quickFilterGenderWidget,
|
||||
quickFilterFormatLegalityWidget,
|
||||
};
|
||||
|
||||
for (auto *btn : filterButtons) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class QLabel;
|
|||
class SettingsButtonWidget;
|
||||
class VisualDatabaseDisplayFilterSaveLoadWidget;
|
||||
class VisualDatabaseDisplayFormatLegalityFilterWidget;
|
||||
class VisualDatabaseDisplayGenderFilterWidget;
|
||||
class VisualDatabaseDisplayMainTypeFilterWidget;
|
||||
class VisualDatabaseDisplayNameFilterWidget;
|
||||
class VisualDatabaseDisplaySetFilterWidget;
|
||||
|
|
@ -52,6 +53,8 @@ private:
|
|||
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
|
||||
SettingsButtonWidget *quickFilterSetWidget;
|
||||
VisualDatabaseDisplaySetFilterWidget *setFilterWidget;
|
||||
SettingsButtonWidget *quickFilterGenderWidget;
|
||||
VisualDatabaseDisplayGenderFilterWidget *genderFilterWidget;
|
||||
SettingsButtonWidget *quickFilterFormatLegalityWidget;
|
||||
VisualDatabaseDisplayFormatLegalityFilterWidget *formatLegalityWidget;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
#include "visual_database_display_gender_filter_widget.h"
|
||||
|
||||
#include "../../../filters/filter_tree_model.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
#include "libcockatrice/filters/filter_card.h"
|
||||
#include "libcockatrice/filters/filter_tree.h"
|
||||
#include "visual_database_display_filter_button.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QPushButton>
|
||||
#include <QSet>
|
||||
#include <QSignalBlocker>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <qnamespace.h>
|
||||
|
||||
VisualDatabaseDisplayGenderFilterWidget::VisualDatabaseDisplayGenderFilterWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel)
|
||||
{
|
||||
setObjectName("genderFilterWidget");
|
||||
setMinimumWidth(300);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(4, 4, 4, 4);
|
||||
|
||||
auto *flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
flowWidget->setMaximumHeight(300);
|
||||
layout->addWidget(flowWidget);
|
||||
|
||||
// Authoritative source: continuum-modules.mse-include/genders/choice_names
|
||||
// The MSE "group horde" option is intentionally omitted.
|
||||
const QList<QPair<QString, QString>> genders = {
|
||||
{QStringLiteral("none"), tr("None")},
|
||||
{QStringLiteral("sex female"), tr("Female")},
|
||||
{QStringLiteral("sex male"), tr("Male")},
|
||||
{QStringLiteral("sex neuter"), tr("Neuter")},
|
||||
{QStringLiteral("sex hermaphrodite"), tr("Hermaphrodite")},
|
||||
{QStringLiteral("sex null"), tr("Sex Null")},
|
||||
{QStringLiteral("artificial artifice"), tr("Artifice")},
|
||||
{QStringLiteral("artificial masculine"), tr("Masculine (Artificial)")},
|
||||
{QStringLiteral("artificial feminine"), tr("Feminine (Artificial)")},
|
||||
{QStringLiteral("duos couple"), tr("Couple")},
|
||||
{QStringLiteral("duos brothers"), tr("Brothers")},
|
||||
{QStringLiteral("duos sisters"), tr("Sisters")},
|
||||
{QStringLiteral("duos siblings"), tr("Siblings")},
|
||||
{QStringLiteral("special not applicable"), tr("Not Applicable")},
|
||||
{QStringLiteral("custom gender one"), tr("Custom One")},
|
||||
{QStringLiteral("custom gender two"), tr("Custom Two")},
|
||||
{QStringLiteral("custom gender three"), tr("Custom Three")},
|
||||
};
|
||||
|
||||
for (const auto &gender : genders) {
|
||||
auto *button = new QPushButton(gender.second, flowWidget);
|
||||
button->setObjectName(QStringLiteral("genderFilter_") + gender.first);
|
||||
button->setToolTip(gender.first);
|
||||
button->setCheckable(true);
|
||||
button->setStyleSheet(visualDatabaseDisplayFilterButtonStyle);
|
||||
flowWidget->addWidget(button);
|
||||
genderButtons.insert(gender.first, button);
|
||||
connect(button, &QPushButton::toggled, this, [this](bool) { updateGenderFilter(); });
|
||||
}
|
||||
|
||||
auto *clearButton = new QPushButton(tr("Clear gender filter"), this);
|
||||
layout->addWidget(clearButton);
|
||||
connect(clearButton, &QPushButton::clicked, this, [this]() {
|
||||
for (auto *button : genderButtons) {
|
||||
const QSignalBlocker blocker(button);
|
||||
button->setChecked(false);
|
||||
}
|
||||
updateGenderFilter();
|
||||
});
|
||||
|
||||
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() { syncWithFilterModel(); });
|
||||
syncWithFilterModel();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayGenderFilterWidget::updateGenderFilter()
|
||||
{
|
||||
// Selected genders are ORed together; other filter attributes still use AND.
|
||||
// No selections means there is no gender restriction.
|
||||
{
|
||||
const QSignalBlocker modelBlocker(filterModel);
|
||||
const QSignalBlocker treeBlocker(filterModel->filterTree());
|
||||
filterModel->clearFiltersOfType(CardFilter::AttrGender);
|
||||
|
||||
for (auto it = genderButtons.cbegin(); it != genderButtons.cend(); ++it) {
|
||||
if (it.value()->isChecked()) {
|
||||
QString gender = it.key();
|
||||
filterModel->addFilter(new CardFilter(gender, CardFilter::TypeOr, CardFilter::AttrGender));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayGenderFilterWidget::syncWithFilterModel()
|
||||
{
|
||||
QSet<QString> selected;
|
||||
const auto filters = filterModel->getFiltersOfType(CardFilter::AttrGender);
|
||||
for (const CardFilter *filter : filters) {
|
||||
if (filter->type() == CardFilter::TypeOr || filter->type() == CardFilter::TypeAnd) {
|
||||
selected.insert(filter->term());
|
||||
}
|
||||
delete filter; // FilterTreeModel::getFiltersOfType returns heap copies.
|
||||
}
|
||||
|
||||
for (auto it = genderButtons.cbegin(); it != genderButtons.cend(); ++it) {
|
||||
const QSignalBlocker blocker(it.value());
|
||||
it.value()->setChecked(selected.contains(it.key()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef COCKATRICE_VISUAL_DATABASE_DISPLAY_GENDER_FILTER_WIDGET_H
|
||||
#define COCKATRICE_VISUAL_DATABASE_DISPLAY_GENDER_FILTER_WIDGET_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class FilterTreeModel;
|
||||
class FlowWidget;
|
||||
class QPushButton;
|
||||
|
||||
// Uses the literal MSE gender option values, not labels or filename suffixes.
|
||||
class VisualDatabaseDisplayGenderFilterWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
explicit VisualDatabaseDisplayGenderFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||
|
||||
private:
|
||||
FilterTreeModel *filterModel;
|
||||
QMap<QString, QPushButton *> genderButtons;
|
||||
|
||||
void updateGenderFilter();
|
||||
void syncWithFilterModel();
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_VISUAL_DATABASE_DISPLAY_GENDER_FILTER_WIDGET_H
|
||||
|
|
@ -91,6 +91,8 @@ const QString CardFilter::attrName(Attr a)
|
|||
return tr("Main Type");
|
||||
case AttrSubType:
|
||||
return tr("Sub Type");
|
||||
case AttrGender:
|
||||
return tr("Gender");
|
||||
default:
|
||||
return QString("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public:
|
|||
AttrMainType,
|
||||
AttrSubType,
|
||||
AttrFormat,
|
||||
AttrGender,
|
||||
AttrEnd,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -351,6 +351,13 @@ bool FilterItem::acceptSubType(const CardInfoPtr &info) const
|
|||
return subType.contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptGender(const CardInfoPtr &info) const
|
||||
{
|
||||
// MSE exports the full option value (e.g. "sex male", "duos siblings").
|
||||
// Do not substring-match "male" against "artificial masculine".
|
||||
return info->getProperty(QStringLiteral("gender")).trimmed().compare(term.trimmed(), Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptColor(const CardInfoPtr &info) const
|
||||
{
|
||||
if (convertedColorTerm.isEmpty()) {
|
||||
|
|
@ -545,6 +552,8 @@ bool FilterItem::acceptCardAttr(const CardInfoPtr &info,
|
|||
return acceptMainType(info);
|
||||
case CardFilter::AttrSubType:
|
||||
return acceptSubType(info);
|
||||
case CardFilter::AttrGender:
|
||||
return acceptGender(info);
|
||||
default:
|
||||
return true; /* ignore this attribute */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ public:
|
|||
[[nodiscard]] bool acceptType(const CardInfoPtr &info) const;
|
||||
[[nodiscard]] bool acceptMainType(const CardInfoPtr &info) const;
|
||||
[[nodiscard]] bool acceptSubType(const CardInfoPtr &info) const;
|
||||
[[nodiscard]] bool acceptGender(const CardInfoPtr &info) const;
|
||||
[[nodiscard]] bool acceptColor(const CardInfoPtr &info) const;
|
||||
[[nodiscard]] bool acceptText(const CardInfoPtr &info, const CardSearchLanguage &searchLanguage) const;
|
||||
[[nodiscard]] bool acceptSet(const CardInfoPtr &info) const;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/printing/printing_info.h>
|
||||
#include <libcockatrice/filters/filter_string.h>
|
||||
#include <libcockatrice/filters/filter_card.h>
|
||||
#include <libcockatrice/filters/filter_tree.h>
|
||||
#include <libcockatrice/interfaces/noop_card_preference_provider.h>
|
||||
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
|
||||
#include <string>
|
||||
|
|
@ -186,6 +188,58 @@ TEST_F(CardQuery, SearchLanguageIsBoundPerInstance)
|
|||
|
||||
} // namespace
|
||||
|
||||
TEST(GenderFilter, MatchesMseValuesExactlyAndCaseInsensitively)
|
||||
{
|
||||
auto male = CardInfo::newInstance("Guardian-m");
|
||||
male->setProperty("gender", "sex male");
|
||||
auto female = CardInfo::newInstance("Guardian-f");
|
||||
female->setProperty("gender", "sex female");
|
||||
auto artificial = CardInfo::newInstance("Automaton");
|
||||
artificial->setProperty("gender", "artificial masculine");
|
||||
auto missing = CardInfo::newInstance("Unknown");
|
||||
|
||||
FilterTree tree;
|
||||
QString term = "SEX MALE";
|
||||
CardFilter genderFilter(term, CardFilter::TypeOr, CardFilter::AttrGender);
|
||||
tree.termNode(&genderFilter);
|
||||
|
||||
EXPECT_TRUE(tree.acceptsCard(male, CardSearchLanguage{}));
|
||||
EXPECT_FALSE(tree.acceptsCard(female, CardSearchLanguage{}));
|
||||
EXPECT_FALSE(tree.acceptsCard(artificial, CardSearchLanguage{}));
|
||||
EXPECT_FALSE(tree.acceptsCard(missing, CardSearchLanguage{}));
|
||||
}
|
||||
|
||||
TEST(GenderFilter, MultipleSelectionsUseOrAndClearingRestoresAllCards)
|
||||
{
|
||||
auto male = CardInfo::newInstance("Guardian-m");
|
||||
male->setProperty("gender", "sex male");
|
||||
auto female = CardInfo::newInstance("Guardian-f");
|
||||
female->setProperty("gender", "sex female");
|
||||
auto neuter = CardInfo::newInstance("Guardian");
|
||||
neuter->setProperty("gender", "sex neuter");
|
||||
auto duo = CardInfo::newInstance("Sibling Heroes");
|
||||
duo->setProperty("gender", "duos siblings");
|
||||
|
||||
FilterTree tree;
|
||||
QString maleTerm = "sex male";
|
||||
QString femaleTerm = "sex female";
|
||||
CardFilter maleFilter(maleTerm, CardFilter::TypeOr, CardFilter::AttrGender);
|
||||
CardFilter femaleFilter(femaleTerm, CardFilter::TypeOr, CardFilter::AttrGender);
|
||||
tree.termNode(&maleFilter);
|
||||
tree.termNode(&femaleFilter);
|
||||
|
||||
EXPECT_TRUE(tree.acceptsCard(male, CardSearchLanguage{}));
|
||||
EXPECT_TRUE(tree.acceptsCard(female, CardSearchLanguage{}));
|
||||
EXPECT_FALSE(tree.acceptsCard(neuter, CardSearchLanguage{}));
|
||||
EXPECT_FALSE(tree.acceptsCard(duo, CardSearchLanguage{}));
|
||||
|
||||
tree.removeFiltersByAttr(CardFilter::AttrGender);
|
||||
EXPECT_TRUE(tree.acceptsCard(male, CardSearchLanguage{}));
|
||||
EXPECT_TRUE(tree.acceptsCard(female, CardSearchLanguage{}));
|
||||
EXPECT_TRUE(tree.acceptsCard(neuter, CardSearchLanguage{}));
|
||||
EXPECT_TRUE(tree.acceptsCard(duo, CardSearchLanguage{}));
|
||||
}
|
||||
|
||||
class SetQuery : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue