mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-03-21 17:55:21 -05:00
Visual Database Display Tab. (#5822)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, 12) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, skip, 11) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, 41) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, skip, 40) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, 24.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, skip, 20.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, skip, 22.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (yes, Arch, skip) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (3, 1, macos-14, Apple, 14, Release, 15.4) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (3, 1, macos-15, Apple, 15, Release, 16.2) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (3, macos-15, Apple, 15, Debug, 16.2) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (4, 1, macos-13, Intel, 13, Release, 14.3.1) (push) Blocked by required conditions
Build Desktop / Windows ${{matrix.target}} (msvc2019_64, 5.15.*, 7) (push) Blocked by required conditions
Build Desktop / Windows ${{matrix.target}} (msvc2019_64, qtimageformats qtmultimedia qtwebsockets, 6.6.*, 10) (push) Blocked by required conditions
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, 12) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, skip, 11) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, 41) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, skip, 40) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, 24.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, skip, 20.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, skip, 22.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (yes, Arch, skip) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (3, 1, macos-14, Apple, 14, Release, 15.4) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (3, 1, macos-15, Apple, 15, Release, 16.2) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (3, macos-15, Apple, 15, Debug, 16.2) (push) Blocked by required conditions
Build Desktop / macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (4, 1, macos-13, Intel, 13, Release, 14.3.1) (push) Blocked by required conditions
Build Desktop / Windows ${{matrix.target}} (msvc2019_64, 5.15.*, 7) (push) Blocked by required conditions
Build Desktop / Windows ${{matrix.target}} (msvc2019_64, qtimageformats qtmultimedia qtwebsockets, 6.6.*, 10) (push) Blocked by required conditions
* Visual Database Display Tab. * Address comments. * Readd dropped method. * Update filterTree properly in case the filter is empty after modification. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
ae90b6c93f
commit
c4d0921a15
|
|
@ -43,6 +43,7 @@ set(cockatrice_SOURCES
|
|||
src/client/tabs/tab_room.cpp
|
||||
src/client/tabs/tab_server.cpp
|
||||
src/client/tabs/tab_supervisor.cpp
|
||||
src/client/tabs/tab_visual_database_display.cpp
|
||||
src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
|
||||
src/client/tapped_out_interface.cpp
|
||||
src/client/translate_counter_name.cpp
|
||||
|
|
@ -92,6 +93,14 @@ set(cockatrice_SOURCES
|
|||
src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
|
||||
src/client/ui/widgets/quick_settings/settings_button_widget.cpp
|
||||
src/client/ui/widgets/quick_settings/settings_popup_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
|
||||
src/client/ui/widgets/visual_database_display/visual_database_filter_display_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
|
||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
|
||||
|
|
@ -204,6 +213,7 @@ set(cockatrice_SOURCES
|
|||
src/settings/settings_manager.cpp
|
||||
src/settings/shortcut_treeview.cpp
|
||||
src/settings/shortcuts_settings.cpp
|
||||
src/utility/card_info_comparator.cpp
|
||||
src/utility/logger.cpp
|
||||
src/utility/sequence_edit.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "tab_replays.h"
|
||||
#include "tab_room.h"
|
||||
#include "tab_server.h"
|
||||
#include "tab_visual_database_display.h"
|
||||
#include "visual_deck_storage/tab_deck_storage_visual.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
|
@ -134,6 +135,9 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
|
|||
aTabVisualDeckStorage->setCheckable(true);
|
||||
connect(aTabVisualDeckStorage, &QAction::triggered, this, &TabSupervisor::actTabVisualDeckStorage);
|
||||
|
||||
aTabVisualDatabaseDisplay = new QAction(this);
|
||||
connect(aTabVisualDatabaseDisplay, &QAction::triggered, this, [this] { addVisualDatabaseDisplayTab(); });
|
||||
|
||||
aTabServer = new QAction(this);
|
||||
aTabServer->setCheckable(true);
|
||||
connect(aTabServer, &QAction::triggered, this, &TabSupervisor::actTabServer);
|
||||
|
|
@ -177,6 +181,7 @@ void TabSupervisor::retranslateUi()
|
|||
// tab menu actions
|
||||
aTabDeckEditor->setText(tr("Deck Editor"));
|
||||
aTabVisualDeckStorage->setText(tr("&Visual Deck Storage"));
|
||||
aTabVisualDatabaseDisplay->setText(tr("Visual Database Display"));
|
||||
aTabServer->setText(tr("Server"));
|
||||
aTabAccount->setText(tr("Account"));
|
||||
aTabDeckStorage->setText(tr("Deck Storage"));
|
||||
|
|
@ -372,6 +377,7 @@ void TabSupervisor::resetTabsMenu()
|
|||
tabsMenu->addAction(aTabDeckEditor);
|
||||
tabsMenu->addSeparator();
|
||||
tabsMenu->addAction(aTabVisualDeckStorage);
|
||||
tabsMenu->addAction(aTabVisualDatabaseDisplay);
|
||||
tabsMenu->addAction(aTabDeckStorage);
|
||||
tabsMenu->addAction(aTabReplays);
|
||||
}
|
||||
|
|
@ -804,6 +810,14 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen)
|
|||
return tab;
|
||||
}
|
||||
|
||||
TabVisualDatabaseDisplay *TabSupervisor::addVisualDatabaseDisplayTab()
|
||||
{
|
||||
auto *tab = new TabVisualDatabaseDisplay(this);
|
||||
myAddTab(tab);
|
||||
setCurrentWidget(tab);
|
||||
return tab;
|
||||
}
|
||||
|
||||
TabEdhRec *TabSupervisor::addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander)
|
||||
{
|
||||
auto *tab = new TabEdhRec(this);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../server/user/user_list_proxy.h"
|
||||
#include "abstract_tab_deck_editor.h"
|
||||
#include "api/edhrec/tab_edhrec.h"
|
||||
#include "tab_visual_database_display.h"
|
||||
#include "visual_deck_storage/tab_deck_storage_visual.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
|
|
@ -91,8 +92,8 @@ private:
|
|||
QList<AbstractTabDeckEditor *> deckEditorTabs;
|
||||
bool isLocalGame;
|
||||
|
||||
QAction *aTabDeckEditor, *aTabVisualDeckStorage, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays,
|
||||
*aTabAdmin, *aTabLog;
|
||||
QAction *aTabDeckEditor, *aTabVisualDeckStorage, *aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount,
|
||||
*aTabDeckStorage, *aTabReplays, *aTabAdmin, *aTabLog;
|
||||
|
||||
int myAddTab(Tab *tab, QAction *manager = nullptr);
|
||||
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
|
||||
|
|
@ -149,6 +150,7 @@ signals:
|
|||
|
||||
public slots:
|
||||
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
|
||||
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();
|
||||
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
|
||||
void openReplay(GameReplay *replay);
|
||||
void maximizeMainWindow();
|
||||
|
|
|
|||
20
cockatrice/src/client/tabs/tab_visual_database_display.cpp
Normal file
20
cockatrice/src/client/tabs/tab_visual_database_display.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include "tab_visual_database_display.h"
|
||||
|
||||
#include "tab_deck_editor.h"
|
||||
|
||||
TabVisualDatabaseDisplay::TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
deckEditor = new TabDeckEditor(_tabSupervisor);
|
||||
deckEditor->setHidden(true);
|
||||
visualDatabaseDisplayWidget =
|
||||
new VisualDatabaseDisplayWidget(this, deckEditor, deckEditor->databaseDisplayDockWidget->databaseModel,
|
||||
deckEditor->databaseDisplayDockWidget->databaseDisplayModel);
|
||||
|
||||
setCentralWidget(visualDatabaseDisplayWidget);
|
||||
|
||||
TabVisualDatabaseDisplay::retranslateUi();
|
||||
}
|
||||
|
||||
void TabVisualDatabaseDisplay::retranslateUi()
|
||||
{
|
||||
}
|
||||
26
cockatrice/src/client/tabs/tab_visual_database_display.h
Normal file
26
cockatrice/src/client/tabs/tab_visual_database_display.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef TAB_VISUAL_DATABASE_DISPLAY_H
|
||||
#define TAB_VISUAL_DATABASE_DISPLAY_H
|
||||
|
||||
#include "../ui/widgets/visual_database_display/visual_database_display_widget.h"
|
||||
#include "tab.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class TabVisualDatabaseDisplay : public Tab
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
TabDeckEditor *deckEditor;
|
||||
VisualDatabaseDisplayWidget *visualDatabaseDisplayWidget;
|
||||
|
||||
public:
|
||||
TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor);
|
||||
void retranslateUi() override;
|
||||
QString getTabText() const override
|
||||
{
|
||||
return tr("Visual Database Display");
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TAB_VISUAL_DATABASE_DISPLAY_H
|
||||
|
|
@ -235,6 +235,42 @@ void DeckEditorDatabaseDisplayWidget::saveDbHeaderState()
|
|||
SettingsCache::instance().layouts().setDeckEditorDbHeaderState(databaseView->header()->saveState());
|
||||
}
|
||||
|
||||
void DeckEditorDatabaseDisplayWidget::showSearchSyntaxHelp()
|
||||
{
|
||||
|
||||
QFile file("theme:help/search.md");
|
||||
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString text = in.readAll();
|
||||
file.close();
|
||||
|
||||
// Poor Markdown Converter
|
||||
auto opts = QRegularExpression::MultilineOption;
|
||||
text = text.replace(QRegularExpression("^(###)(.*)", opts), "<h3>\\2</h3>")
|
||||
.replace(QRegularExpression("^(##)(.*)", opts), "<h2>\\2</h2>")
|
||||
.replace(QRegularExpression("^(#)(.*)", opts), "<h1>\\2</h1>")
|
||||
.replace(QRegularExpression("^------*", opts), "<hr />")
|
||||
.replace(QRegularExpression(R"(\[([^[]+)\]\(([^\)]+)\))", opts), R"(<a href='\2'>\1</a>)");
|
||||
|
||||
auto browser = new QTextBrowser;
|
||||
browser->setParent(this, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint |
|
||||
Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint);
|
||||
browser->setWindowTitle("Search Help");
|
||||
browser->setReadOnly(true);
|
||||
browser->setMinimumSize({500, 600});
|
||||
|
||||
QString sheet = QString("a { text-decoration: underline; color: rgb(71,158,252) };");
|
||||
browser->document()->setDefaultStyleSheet(sheet);
|
||||
|
||||
browser->setHtml(text);
|
||||
connect(browser, &QTextBrowser::anchorClicked, [this](const QUrl &link) { searchEdit->setText(link.fragment()); });
|
||||
browser->show();
|
||||
}
|
||||
|
||||
void DeckEditorDatabaseDisplayWidget::setFilterTree(FilterTree *filterTree)
|
||||
{
|
||||
databaseDisplayModel->setFilterTree(filterTree);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public:
|
|||
CardDatabaseDisplayModel *databaseDisplayModel;
|
||||
|
||||
public slots:
|
||||
void showSearchSyntaxHelp();
|
||||
CardInfoPtr currentCardInfo() const;
|
||||
void setFilterTree(FilterTree *filterTree);
|
||||
void clearAllDatabaseFilters();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,228 @@
|
|||
#include "visual_database_display_color_filter_widget.h"
|
||||
|
||||
#include "../../../../game/filters/filter_tree.h"
|
||||
#include "../cards/additional_info/mana_symbol_widget.h"
|
||||
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
/**
|
||||
* This widget provides a graphical control element for the CardFilter::Attr::AttrColor filters applied to the filter
|
||||
* model.
|
||||
* @param parent The Qt Widget that this widget will parent to
|
||||
* @param _filterModel The filter model that this widget will manipulate
|
||||
*/
|
||||
VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel), layout(new QHBoxLayout(this))
|
||||
{
|
||||
setLayout(layout);
|
||||
layout->setSpacing(5);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QString fullColorIdentity = "WUBRG";
|
||||
for (const QChar &color : fullColorIdentity) {
|
||||
auto *manaSymbol = new ManaSymbolWidget(this, color, false, true);
|
||||
manaSymbol->setFixedWidth(25);
|
||||
|
||||
layout->addWidget(manaSymbol);
|
||||
|
||||
// Initialize the activeColors map
|
||||
activeColors[color] = false;
|
||||
|
||||
// Connect the color toggled signal
|
||||
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
|
||||
&VisualDatabaseDisplayColorFilterWidget::handleColorToggled);
|
||||
}
|
||||
|
||||
toggleButton = new QPushButton(this);
|
||||
toggleButton->setCheckable(true);
|
||||
layout->addWidget(toggleButton);
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
// Call retranslateUi to set the initial text
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::retranslateUi()
|
||||
{
|
||||
switch (currentMode) {
|
||||
case FilterMode::ExactMatch:
|
||||
toggleButton->setText(tr("Mode: Exact Match"));
|
||||
break;
|
||||
case FilterMode::Includes:
|
||||
toggleButton->setText(tr("Mode: Includes"));
|
||||
break;
|
||||
case FilterMode::IncludeExclude:
|
||||
toggleButton->setText(tr("Mode: Include/Exclude"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active)
|
||||
{
|
||||
activeColors[color] = active;
|
||||
emit activeColorsChanged(); // Notify listeners that the active colors have changed
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::updateColorFilter()
|
||||
{
|
||||
blockSync = true;
|
||||
|
||||
// Clear previous filters
|
||||
filterModel->blockSignals(true);
|
||||
filterModel->filterTree()->blockSignals(true);
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
|
||||
QSet<QString> selectedColors;
|
||||
QSet<QString> excludedColors;
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
|
||||
blockSync = false;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
|
||||
{
|
||||
blockSync = true;
|
||||
|
||||
switch (currentMode) {
|
||||
case FilterMode::ExactMatch:
|
||||
currentMode = FilterMode::Includes; // Switch to Includes
|
||||
break;
|
||||
case FilterMode::Includes:
|
||||
currentMode = FilterMode::IncludeExclude; // Switch to Include/Exclude
|
||||
break;
|
||||
case FilterMode::IncludeExclude:
|
||||
currentMode = FilterMode::ExactMatch; // Switch to Exact Match
|
||||
break;
|
||||
}
|
||||
|
||||
retranslateUi(); // Update button text based on the mode
|
||||
emit filterModeChanged(currentMode); // Signal mode change
|
||||
updateColorFilter(); // Reapply the filter based on the new mode
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
|
||||
|
||||
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
|
||||
manaSymbolWidget->setColorActive(activeColors[manaSymbolWidget->getSymbolChar()]);
|
||||
}
|
||||
|
||||
updateColorFilter();
|
||||
blockSync = false;
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H
|
||||
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDatabaseDisplayColorFilterCircleWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VisualDatabaseDisplayColorFilterCircleWidget(QChar color, QWidget *parent = nullptr);
|
||||
void setColorActive(bool active);
|
||||
bool isColorActive() const;
|
||||
QChar getColorChar() const;
|
||||
|
||||
signals:
|
||||
void colorToggled(QChar color, bool active);
|
||||
|
||||
private:
|
||||
QChar colorChar;
|
||||
bool isActive;
|
||||
int circleDiameter;
|
||||
};
|
||||
|
||||
enum class FilterMode
|
||||
{
|
||||
ExactMatch, // Only selected colors are included, all others are excluded.
|
||||
Includes, // Include selected colors (OR condition).
|
||||
IncludeExclude // Include selected colors (OR) and exclude unselected colors (AND NOT).
|
||||
};
|
||||
|
||||
class VisualDatabaseDisplayColorFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VisualDatabaseDisplayColorFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||
void retranslateUi();
|
||||
|
||||
signals:
|
||||
void filterModeChanged(FilterMode filterMode);
|
||||
void activeColorsChanged();
|
||||
|
||||
private slots:
|
||||
void handleColorToggled(QChar color, bool active);
|
||||
void updateColorFilter();
|
||||
void updateFilterMode();
|
||||
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
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#include "visual_database_display_filter_save_load_widget.h"
|
||||
|
||||
#include "../../../../game/filters/filter_tree.h"
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "visual_database_filter_display_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QMessageBox>
|
||||
|
||||
VisualDatabaseDisplayFilterSaveLoadWidget::VisualDatabaseDisplayFilterSaveLoadWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel)
|
||||
{
|
||||
setMinimumWidth(300);
|
||||
setMaximumHeight(300);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
// Input for filter filename
|
||||
filenameInput = new QLineEdit(this);
|
||||
layout->addWidget(filenameInput);
|
||||
|
||||
// Save button
|
||||
saveButton = new QPushButton(this);
|
||||
layout->addWidget(saveButton);
|
||||
connect(saveButton, &QPushButton::clicked, this, &VisualDatabaseDisplayFilterSaveLoadWidget::saveFilter);
|
||||
|
||||
// File list container
|
||||
fileListWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
layout->addWidget(fileListWidget);
|
||||
|
||||
refreshFilterList(); // Populate the file list on startup
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::retranslateUi()
|
||||
{
|
||||
saveButton->setText(tr("Save Filter"));
|
||||
filenameInput->setPlaceholderText(tr("Enter filename..."));
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::saveFilter()
|
||||
{
|
||||
QString filename = filenameInput->text().trimmed();
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
QString filePath = SettingsCache::instance().getFiltersPath() + QDir::separator() + filename + ".json";
|
||||
|
||||
// Serialize the filter model to JSON
|
||||
QJsonArray filtersArray;
|
||||
QList<const CardFilter *> cardFilters = filterModel->allFilters();
|
||||
|
||||
for (const CardFilter *filter : cardFilters) {
|
||||
filtersArray.append(filter->toJson());
|
||||
}
|
||||
|
||||
QJsonObject root;
|
||||
root["filters"] = filtersArray;
|
||||
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
file.write(QJsonDocument(root).toJson(QJsonDocument::Indented));
|
||||
file.close();
|
||||
}
|
||||
|
||||
filenameInput->clear();
|
||||
refreshFilterList(); // Update the file list
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::loadFilter(const QString &filename)
|
||||
{
|
||||
QString filePath = SettingsCache::instance().getFiltersPath() + QDir::separator() + filename;
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
|
||||
QByteArray jsonData = file.readAll();
|
||||
file.close();
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData);
|
||||
if (!doc.isObject())
|
||||
return;
|
||||
|
||||
QJsonObject root = doc.object();
|
||||
if (!root.contains("filters") || !root["filters"].isArray())
|
||||
return;
|
||||
|
||||
QJsonArray filtersArray = root["filters"].toArray();
|
||||
|
||||
filterModel->clear();
|
||||
|
||||
filterModel->blockSignals(true);
|
||||
filterModel->filterTree()->blockSignals(true);
|
||||
for (const QJsonValue &value : filtersArray) {
|
||||
if (value.isObject()) {
|
||||
QJsonObject filterObj = value.toObject();
|
||||
CardFilter *filter = CardFilter::fromJson(filterObj);
|
||||
if (filter) {
|
||||
filterModel->addFilter(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::refreshFilterList()
|
||||
{
|
||||
fileListWidget->clearLayout();
|
||||
// Clear existing widgets
|
||||
for (auto buttonPair : fileButtons) {
|
||||
buttonPair.first->deleteLater();
|
||||
buttonPair.second->deleteLater();
|
||||
}
|
||||
fileButtons.clear(); // Clear the list of buttons
|
||||
|
||||
// Refresh the filter file list
|
||||
QDir dir(SettingsCache::instance().getFiltersPath());
|
||||
QStringList filterFiles = dir.entryList(QStringList() << "*.json", QDir::Files, QDir::Time);
|
||||
|
||||
// Loop through the filter files and create widgets for them
|
||||
for (const QString &filename : filterFiles) {
|
||||
bool alreadyAdded = false;
|
||||
|
||||
// Check if the widget for this filter file already exists to avoid duplicates
|
||||
for (const auto &pair : fileButtons) {
|
||||
if (pair.first->text() == filename) {
|
||||
alreadyAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyAdded) {
|
||||
// Create a new custom widget for the filter
|
||||
FilterDisplayWidget *filterWidget = new FilterDisplayWidget(this, filename, filterModel);
|
||||
fileListWidget->addWidget(filterWidget);
|
||||
|
||||
// Connect signals to handle loading and deletion
|
||||
connect(filterWidget, &FilterDisplayWidget::filterLoadRequested, this,
|
||||
&VisualDatabaseDisplayFilterSaveLoadWidget::loadFilter);
|
||||
connect(filterWidget, &FilterDisplayWidget::filterDeleted, this,
|
||||
&VisualDatabaseDisplayFilterSaveLoadWidget::refreshFilterList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H
|
||||
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLineEdit>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDatabaseDisplayFilterSaveLoadWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDatabaseDisplayFilterSaveLoadWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||
|
||||
void saveFilter();
|
||||
void loadFilter(const QString &filename);
|
||||
void refreshFilterList();
|
||||
void deleteFilter(const QString &filename, QPushButton *deleteButton);
|
||||
|
||||
public slots:
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
FilterTreeModel *filterModel;
|
||||
|
||||
QVBoxLayout *layout;
|
||||
QLineEdit *filenameInput;
|
||||
QPushButton *saveButton;
|
||||
FlowWidget *fileListWidget;
|
||||
|
||||
QMap<QString, QPair<QPushButton *, QPushButton *>> fileButtons;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
#include "visual_database_display_main_type_filter_widget.h"
|
||||
|
||||
#include "../../../../game/cards/card_database_manager.h"
|
||||
#include "../../../../game/filters/filter_tree.h"
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QTimer>
|
||||
|
||||
VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel)
|
||||
{
|
||||
allMainCardTypesWithCount = CardDatabaseManager::getInstance()->getAllMainCardTypesWithCount();
|
||||
// Get all main card types with their count
|
||||
|
||||
setMaximumHeight(75);
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
||||
|
||||
layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
layout->setContentsMargins(0, 1, 0, 1);
|
||||
layout->setSpacing(1);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
layout->addWidget(flowWidget);
|
||||
|
||||
// Create the spinbox
|
||||
spinBox = new QSpinBox(this);
|
||||
spinBox->setMinimum(1);
|
||||
spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically
|
||||
spinBox->setValue(150);
|
||||
layout->addWidget(spinBox);
|
||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
&VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility);
|
||||
|
||||
// Create the toggle button for Exact Match/Includes mode
|
||||
toggleButton = new QPushButton(this);
|
||||
toggleButton->setCheckable(true);
|
||||
layout->addWidget(toggleButton);
|
||||
connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplayMainTypeFilterWidget::updateFilterMode);
|
||||
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() {
|
||||
QTimer::singleShot(100, this, &VisualDatabaseDisplayMainTypeFilterWidget::syncWithFilterModel);
|
||||
});
|
||||
|
||||
createMainTypeButtons(); // Populate buttons initially
|
||||
updateFilterMode(false); // Initialize toggle button text
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayMainTypeFilterWidget::createMainTypeButtons()
|
||||
{
|
||||
// Iterate through main types and create buttons
|
||||
for (auto it = allMainCardTypesWithCount.begin(); it != allMainCardTypesWithCount.end(); ++it) {
|
||||
auto *button = new QPushButton(it.key(), flowWidget);
|
||||
button->setCheckable(true);
|
||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
||||
"QPushButton:checked { background-color: green; color: white; }");
|
||||
|
||||
flowWidget->addWidget(button);
|
||||
typeButtons[it.key()] = button;
|
||||
|
||||
// Connect toggle signal
|
||||
connect(button, &QPushButton::toggled, this,
|
||||
[this, mainType = it.key()](bool checked) { handleMainTypeToggled(mainType, checked); });
|
||||
}
|
||||
updateMainTypeButtonsVisibility(); // Ensure visibility is updated initially
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility()
|
||||
{
|
||||
int threshold = spinBox->value(); // Get the current spinbox value
|
||||
|
||||
// Iterate through buttons and hide/disable those below the threshold
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
bool visible = allMainCardTypesWithCount[it.key()] >= threshold;
|
||||
it.value()->setVisible(visible);
|
||||
it.value()->setEnabled(visible);
|
||||
}
|
||||
}
|
||||
|
||||
int VisualDatabaseDisplayMainTypeFilterWidget::getMaxMainTypeCount() const
|
||||
{
|
||||
int maxCount = 1;
|
||||
for (auto it = allMainCardTypesWithCount.begin(); it != allMainCardTypesWithCount.end(); ++it) {
|
||||
maxCount = qMax(maxCount, it.value());
|
||||
}
|
||||
return maxCount;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayMainTypeFilterWidget::handleMainTypeToggled(const QString &mainType, bool active)
|
||||
{
|
||||
activeMainTypes[mainType] = active;
|
||||
|
||||
if (typeButtons.contains(mainType)) {
|
||||
typeButtons[mainType]->setChecked(active);
|
||||
}
|
||||
|
||||
updateMainTypeFilter();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeFilter()
|
||||
{
|
||||
// Clear existing filters related to main type
|
||||
filterModel->blockSignals(true);
|
||||
filterModel->filterTree()->blockSignals(true);
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrMainType);
|
||||
|
||||
if (exactMatchMode) {
|
||||
// Exact Match: Only selected main types are allowed
|
||||
QSet<QString> selectedTypes;
|
||||
for (const auto &type : activeMainTypes.keys()) {
|
||||
if (activeMainTypes[type]) {
|
||||
selectedTypes.insert(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedTypes.isEmpty()) {
|
||||
// Require all selected types (TypeAnd)
|
||||
for (const auto &type : selectedTypes) {
|
||||
QString typeString = type;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrMainType));
|
||||
}
|
||||
|
||||
// Exclude any other types (TypeAndNot)
|
||||
for (const auto &type : typeButtons.keys()) {
|
||||
if (!selectedTypes.contains(type)) {
|
||||
QString typeString = type;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(typeString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrMainType));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default Includes Mode (TypeOr) - match any selected main types
|
||||
for (const auto &type : activeMainTypes.keys()) {
|
||||
if (activeMainTypes[type]) {
|
||||
QString typeString = type;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrMainType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayMainTypeFilterWidget::updateFilterMode(bool checked)
|
||||
{
|
||||
exactMatchMode = checked;
|
||||
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
|
||||
updateMainTypeFilter();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayMainTypeFilterWidget::syncWithFilterModel()
|
||||
{
|
||||
// Temporarily block signals for each button to prevent toggling while updating button states
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
it.value()->blockSignals(true);
|
||||
}
|
||||
|
||||
// Uncheck all buttons
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
it.value()->setChecked(false);
|
||||
}
|
||||
|
||||
// Get active filters for main types
|
||||
QSet<QString> activeTypes;
|
||||
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::AttrMainType)) {
|
||||
if (filter->type() == CardFilter::Type::TypeAnd) {
|
||||
activeTypes.insert(filter->term());
|
||||
}
|
||||
}
|
||||
|
||||
// Check the buttons for active types
|
||||
for (const auto &type : activeTypes) {
|
||||
activeMainTypes[type] = true;
|
||||
if (typeButtons.contains(type)) {
|
||||
typeButtons[type]->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable signal emissions for each button
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
it.value()->blockSignals(false);
|
||||
}
|
||||
|
||||
// Update the visibility of buttons
|
||||
updateMainTypeButtonsVisibility();
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H
|
||||
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDatabaseDisplayMainTypeFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||
void createMainTypeButtons();
|
||||
void updateMainTypeButtonsVisibility();
|
||||
int getMaxMainTypeCount() const;
|
||||
|
||||
void handleMainTypeToggled(const QString &mainType, bool active);
|
||||
void updateMainTypeFilter();
|
||||
void updateFilterMode(bool checked);
|
||||
void syncWithFilterModel();
|
||||
|
||||
private:
|
||||
FilterTreeModel *filterModel;
|
||||
QMap<QString, int> allMainCardTypesWithCount;
|
||||
QSpinBox *spinBox;
|
||||
QHBoxLayout *layout;
|
||||
FlowWidget *flowWidget;
|
||||
QPushButton *toggleButton; // Mode switch button
|
||||
|
||||
QMap<QString, bool> activeMainTypes; // Track active filters
|
||||
QMap<QString, QPushButton *> typeButtons; // Store toggle buttons
|
||||
|
||||
bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes"
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
#include "visual_database_display_name_filter_widget.h"
|
||||
|
||||
#include "../../../tabs/abstract_tab_deck_editor.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
VisualDatabaseDisplayNameFilterWidget::VisualDatabaseDisplayNameFilterWidget(QWidget *parent,
|
||||
AbstractTabDeckEditor *_deckEditor,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), deckEditor(_deckEditor), filterModel(_filterModel)
|
||||
{
|
||||
setMinimumWidth(300);
|
||||
setMaximumHeight(300);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
searchBox = new QLineEdit(this);
|
||||
layout->addWidget(searchBox);
|
||||
|
||||
connect(searchBox, &QLineEdit::returnPressed, this, [this]() {
|
||||
QString text = searchBox->text().trimmed();
|
||||
if (!text.isEmpty() && !activeFilters.contains(text)) {
|
||||
createNameFilter(text);
|
||||
searchBox->clear();
|
||||
updateFilterModel();
|
||||
}
|
||||
});
|
||||
|
||||
// Create container for active filters
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
layout->addWidget(flowWidget);
|
||||
|
||||
loadFromDeckButton = new QPushButton(this);
|
||||
layout->addWidget(loadFromDeckButton);
|
||||
|
||||
connect(loadFromDeckButton, &QPushButton::clicked, this, &VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck);
|
||||
connect(filterModel, &FilterTreeModel::layoutChanged, this,
|
||||
[this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplayNameFilterWidget::syncWithFilterModel); });
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayNameFilterWidget::retranslateUi()
|
||||
{
|
||||
searchBox->setPlaceholderText(tr("Filter by name..."));
|
||||
loadFromDeckButton->setText(tr("Load from Deck"));
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck()
|
||||
{
|
||||
DeckListModel *deckListModel = deckEditor->deckDockWidget->deckModel;
|
||||
|
||||
if (!deckListModel)
|
||||
return;
|
||||
DeckList *decklist = deckListModel->getDeckList();
|
||||
if (!decklist)
|
||||
return;
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
if (!currentZone)
|
||||
continue;
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
createNameFilter(currentCard->getName());
|
||||
}
|
||||
}
|
||||
updateFilterModel();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayNameFilterWidget::createNameFilter(const QString &name)
|
||||
{
|
||||
if (activeFilters.contains(name))
|
||||
return;
|
||||
|
||||
// Create a button for the filter
|
||||
auto *button = new QPushButton(name, flowWidget);
|
||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
||||
"QPushButton:hover { background-color: red; color: white; }");
|
||||
|
||||
connect(button, &QPushButton::clicked, this, [this, name]() { removeNameFilter(name); });
|
||||
|
||||
flowWidget->addWidget(button);
|
||||
activeFilters[name] = button;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayNameFilterWidget::removeNameFilter(const QString &name)
|
||||
{
|
||||
if (activeFilters.contains(name)) {
|
||||
activeFilters[name]->deleteLater(); // Safe deletion
|
||||
activeFilters.remove(name);
|
||||
|
||||
QTimer::singleShot(0, this,
|
||||
&VisualDatabaseDisplayNameFilterWidget::updateFilterModel); // Avoid concurrent modification
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayNameFilterWidget::updateFilterModel()
|
||||
{
|
||||
// Clear existing name filters
|
||||
emit filterModel->layoutAboutToBeChanged();
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrName);
|
||||
|
||||
filterModel->blockSignals(true);
|
||||
filterModel->filterTree()->blockSignals(true);
|
||||
|
||||
for (const auto &name : activeFilters.keys()) {
|
||||
QString nameString = name;
|
||||
filterModel->addFilter(new CardFilter(nameString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrName));
|
||||
}
|
||||
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayNameFilterWidget::syncWithFilterModel()
|
||||
{
|
||||
QStringList currentFilters;
|
||||
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::Attr::AttrName)) {
|
||||
if (filter->type() == CardFilter::Type::TypeOr) {
|
||||
currentFilters.append(filter->term());
|
||||
}
|
||||
}
|
||||
|
||||
QStringList activeFilterList = activeFilters.keys();
|
||||
|
||||
// Sort lists for efficient comparison
|
||||
std::sort(currentFilters.begin(), currentFilters.end());
|
||||
std::sort(activeFilterList.begin(), activeFilterList.end());
|
||||
|
||||
if (currentFilters == activeFilterList) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove filters that are in the UI but not in the model
|
||||
for (const auto &name : activeFilterList) {
|
||||
if (!currentFilters.contains(name)) {
|
||||
removeNameFilter(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add filters that are in the model but not in the UI
|
||||
for (const auto &name : currentFilters) {
|
||||
if (!activeFilters.contains(name)) {
|
||||
createNameFilter(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H
|
||||
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../../../tabs/abstract_tab_deck_editor.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDatabaseDisplayNameFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDatabaseDisplayNameFilterWidget(QWidget *parent,
|
||||
AbstractTabDeckEditor *deckEditor,
|
||||
FilterTreeModel *filterModel);
|
||||
|
||||
void createNameFilter(const QString &name);
|
||||
void removeNameFilter(const QString &name);
|
||||
void updateFilterList();
|
||||
void updateFilterModel();
|
||||
void syncWithFilterModel();
|
||||
|
||||
public slots:
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
AbstractTabDeckEditor *deckEditor;
|
||||
FilterTreeModel *filterModel;
|
||||
QVBoxLayout *layout;
|
||||
QLineEdit *searchBox;
|
||||
FlowWidget *flowWidget;
|
||||
QPushButton *loadFromDeckButton;
|
||||
|
||||
QMap<QString, QPushButton *> activeFilters; // Store active name filter buttons
|
||||
|
||||
private slots:
|
||||
void actLoadFromDeck();
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
#include "visual_database_display_set_filter_widget.h"
|
||||
|
||||
#include "../../../../game/cards/card_database_manager.h"
|
||||
#include "../../../../game/filters/filter_tree.h"
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <algorithm>
|
||||
|
||||
VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel)
|
||||
{
|
||||
setMinimumWidth(300);
|
||||
setMaximumHeight(300);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
searchBox = new QLineEdit(this);
|
||||
searchBox->setPlaceholderText(tr("Search sets..."));
|
||||
layout->addWidget(searchBox);
|
||||
connect(searchBox, &QLineEdit::textChanged, this,
|
||||
&VisualDatabaseDisplaySetFilterWidget::updateSetButtonsVisibility);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
layout->addWidget(flowWidget);
|
||||
|
||||
// Create the toggle button for Exact Match/Includes mode
|
||||
toggleButton = new QPushButton(this);
|
||||
toggleButton->setCheckable(true);
|
||||
layout->addWidget(toggleButton);
|
||||
connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplaySetFilterWidget::updateFilterMode);
|
||||
connect(filterModel, &FilterTreeModel::layoutChanged, this,
|
||||
[this]() { QTimer::singleShot(100, this, &VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel); });
|
||||
|
||||
createSetButtons(); // Populate buttons initially
|
||||
updateFilterMode(false); // Initialize toggle button text
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
|
||||
{
|
||||
SetList shared_pointerses = CardDatabaseManager::getInstance()->getSetList();
|
||||
|
||||
// Sort by release date
|
||||
std::sort(shared_pointerses.begin(), shared_pointerses.end(),
|
||||
[](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); });
|
||||
|
||||
int setsToPreactivate = 10;
|
||||
int setsActivated = 0;
|
||||
|
||||
for (const auto &shared_pointer : shared_pointerses) {
|
||||
QString shortName = shared_pointer->getShortName();
|
||||
QString longName = shared_pointer->getLongName();
|
||||
|
||||
auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget);
|
||||
button->setCheckable(true);
|
||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
||||
"QPushButton:checked { background-color: green; color: white; }");
|
||||
|
||||
flowWidget->addWidget(button);
|
||||
setButtons[shortName] = button;
|
||||
|
||||
// Connect toggle signal
|
||||
connect(button, &QPushButton::toggled, this,
|
||||
[this, shortName](bool checked) { handleSetToggled(shortName, checked); });
|
||||
if (setsActivated < setsToPreactivate) {
|
||||
setsActivated++;
|
||||
activeSets[shortName] = true;
|
||||
button->setChecked(true);
|
||||
}
|
||||
}
|
||||
updateSetFilter();
|
||||
updateSetButtonsVisibility(); // Ensure visibility is updated initially
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySetFilterWidget::updateSetButtonsVisibility()
|
||||
{
|
||||
QString filterText = searchBox->text().trimmed().toLower();
|
||||
|
||||
for (const QString &setName : setButtons.keys()) {
|
||||
QPushButton *buttonForSet = setButtons[setName];
|
||||
QString shortName = setName.toLower();
|
||||
QString longName = buttonForSet->text().toLower();
|
||||
bool alwaysVisible = activeSets.contains(setName) && activeSets[setName];
|
||||
bool visible =
|
||||
alwaysVisible || filterText.isEmpty() || shortName.contains(filterText) || longName.contains(filterText);
|
||||
buttonForSet->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySetFilterWidget::handleSetToggled(const QString &setShortName, bool active)
|
||||
{
|
||||
activeSets[setShortName] = active;
|
||||
|
||||
if (setButtons.contains(setShortName)) {
|
||||
setButtons[setShortName]->setChecked(active);
|
||||
}
|
||||
|
||||
updateSetFilter();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySetFilterWidget::updateSetFilter()
|
||||
{
|
||||
// Clear existing filters related to sets
|
||||
filterModel->blockSignals(true);
|
||||
filterModel->filterTree()->blockSignals(true);
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrSet);
|
||||
|
||||
if (exactMatchMode) {
|
||||
// Exact Match: Only selected sets are allowed
|
||||
QSet<QString> selectedSets;
|
||||
for (const auto &set : activeSets.keys()) {
|
||||
if (activeSets[set]) {
|
||||
selectedSets.insert(set);
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedSets.isEmpty()) {
|
||||
// Require all selected sets (TypeAnd)
|
||||
for (const auto &set : selectedSets) {
|
||||
QString setString = set;
|
||||
filterModel->addFilter(new CardFilter(setString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrSet));
|
||||
}
|
||||
|
||||
// Exclude any other sets (TypeAndNot)
|
||||
for (const auto &set : setButtons.keys()) {
|
||||
if (!selectedSets.contains(set)) {
|
||||
QString setString = set;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(setString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default Includes Mode (TypeOr) - match any selected sets
|
||||
for (const auto &set : activeSets.keys()) {
|
||||
if (activeSets[set]) {
|
||||
QString setString = set;
|
||||
filterModel->addFilter(new CardFilter(setString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySetFilterWidget::syncWithFilterModel()
|
||||
{
|
||||
// Clear activeSets
|
||||
activeSets.clear();
|
||||
|
||||
// Read the current filters in filterModel
|
||||
auto currentFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrSet);
|
||||
|
||||
// Determine if we're in Exact Match mode or Includes mode
|
||||
QSet<QString> selectedSets;
|
||||
QSet<QString> excludedSets;
|
||||
for (const auto &filter : currentFilters) {
|
||||
if (filter->type() == CardFilter::Type::TypeAnd) {
|
||||
selectedSets.insert(filter->term());
|
||||
} else if (filter->type() == CardFilter::Type::TypeAndNot) {
|
||||
excludedSets.insert(filter->term());
|
||||
} else if (filter->type() == CardFilter::Type::TypeOr) {
|
||||
selectedSets.insert(filter->term());
|
||||
}
|
||||
}
|
||||
|
||||
// Determine exact match mode based on filter structure
|
||||
bool newExactMatchMode = !excludedSets.isEmpty();
|
||||
|
||||
if (newExactMatchMode != exactMatchMode) {
|
||||
exactMatchMode = newExactMatchMode;
|
||||
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
|
||||
}
|
||||
|
||||
// Sync button states with selected sets
|
||||
for (const auto &key : setButtons.keys()) {
|
||||
bool active = selectedSets.contains(key);
|
||||
activeSets[key] = active;
|
||||
setButtons[key]->setChecked(active);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySetFilterWidget::updateFilterMode(bool checked)
|
||||
{
|
||||
exactMatchMode = checked;
|
||||
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
|
||||
updateSetFilter();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H
|
||||
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDatabaseDisplaySetFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDatabaseDisplaySetFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||
void createSetButtons();
|
||||
void updateSetButtonsVisibility();
|
||||
void handleSetToggled(const QString &setShortName, bool active);
|
||||
|
||||
void updateSetFilter();
|
||||
void syncWithFilterModel();
|
||||
void updateFilterMode(bool checked);
|
||||
|
||||
private:
|
||||
FilterTreeModel *filterModel;
|
||||
QMap<QString, int> allMainCardTypesWithCount;
|
||||
QVBoxLayout *layout;
|
||||
QLineEdit *searchBox;
|
||||
FlowWidget *flowWidget;
|
||||
QPushButton *toggleButton; // Mode switch button
|
||||
|
||||
QMap<QString, bool> activeMainTypes; // Track active filters
|
||||
QMap<QString, QPushButton *> typeButtons; // Store toggle buttons
|
||||
QMap<QString, QPushButton *> setButtons; // Store set filter buttons
|
||||
QMap<QString, bool> activeSets; // Track active set filters
|
||||
|
||||
bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes"
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
#include "visual_database_display_sub_type_filter_widget.h"
|
||||
|
||||
#include "../../../../game/cards/card_database_manager.h"
|
||||
#include "../../../../game/filters/filter_tree.h"
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QTimer>
|
||||
|
||||
VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel)
|
||||
{
|
||||
allSubCardTypesWithCount = CardDatabaseManager::getInstance()->getAllSubCardTypesWithCount();
|
||||
|
||||
setMinimumWidth(300);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
// Create and setup the spinbox
|
||||
spinBox = new QSpinBox(this);
|
||||
spinBox->setMinimum(1);
|
||||
spinBox->setMaximum(getMaxSubTypeCount());
|
||||
spinBox->setValue(150);
|
||||
layout->addWidget(spinBox);
|
||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
&VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility);
|
||||
|
||||
// Create search box
|
||||
searchBox = new QLineEdit(this);
|
||||
searchBox->setPlaceholderText(tr("Search subtypes..."));
|
||||
layout->addWidget(searchBox);
|
||||
connect(searchBox, &QLineEdit::textChanged, this,
|
||||
&VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
flowWidget->setMaximumHeight(300);
|
||||
layout->addWidget(flowWidget);
|
||||
|
||||
// Toggle button setup (Exact Match / Includes mode)
|
||||
toggleButton = new QPushButton(this);
|
||||
toggleButton->setCheckable(true);
|
||||
layout->addWidget(toggleButton);
|
||||
connect(toggleButton, &QPushButton::toggled, this, &VisualDatabaseDisplaySubTypeFilterWidget::updateFilterMode);
|
||||
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() {
|
||||
QTimer::singleShot(100, this, &VisualDatabaseDisplaySubTypeFilterWidget::syncWithFilterModel);
|
||||
});
|
||||
|
||||
createSubTypeButtons(); // Populate buttons initially
|
||||
updateFilterMode(false); // Initialize the toggle button text
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySubTypeFilterWidget::createSubTypeButtons()
|
||||
{
|
||||
// Iterate through sub types and create buttons
|
||||
for (auto it = allSubCardTypesWithCount.begin(); it != allSubCardTypesWithCount.end(); ++it) {
|
||||
auto *button = new QPushButton(it.key(), flowWidget);
|
||||
button->setCheckable(true);
|
||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
||||
"QPushButton:checked { background-color: green; color: white; }");
|
||||
|
||||
flowWidget->addWidget(button);
|
||||
typeButtons[it.key()] = button;
|
||||
|
||||
// Connect toggle signal for each button
|
||||
connect(button, &QPushButton::toggled, this,
|
||||
[this, subType = it.key()](bool checked) { handleSubTypeToggled(subType, checked); });
|
||||
}
|
||||
updateSubTypeButtonsVisibility(); // Ensure visibility is updated initially
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility()
|
||||
{
|
||||
int threshold = spinBox->value();
|
||||
QString filterText = searchBox->text().trimmed().toLower();
|
||||
|
||||
// Iterate through buttons and hide/disable those below the threshold
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
QString subType = it.key().toLower();
|
||||
bool isActive = activeSubTypes.value(it.key(), false);
|
||||
bool visible = isActive || (allSubCardTypesWithCount[it.key()] >= threshold &&
|
||||
(filterText.isEmpty() || subType.contains(filterText)));
|
||||
|
||||
it.value()->setVisible(visible);
|
||||
it.value()->setEnabled(visible);
|
||||
}
|
||||
}
|
||||
|
||||
int VisualDatabaseDisplaySubTypeFilterWidget::getMaxSubTypeCount() const
|
||||
{
|
||||
int maxCount = 1;
|
||||
for (auto it = allSubCardTypesWithCount.begin(); it != allSubCardTypesWithCount.end(); ++it) {
|
||||
maxCount = qMax(maxCount, it.value());
|
||||
}
|
||||
return maxCount;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySubTypeFilterWidget::handleSubTypeToggled(const QString &subType, bool active)
|
||||
{
|
||||
activeSubTypes[subType] = active;
|
||||
|
||||
if (typeButtons.contains(subType)) {
|
||||
typeButtons[subType]->setChecked(active);
|
||||
}
|
||||
|
||||
updateSubTypeFilter();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeFilter()
|
||||
{
|
||||
filterModel->blockSignals(true);
|
||||
// Clear existing filters related to sub types
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrSubType);
|
||||
|
||||
if (exactMatchMode) {
|
||||
// Exact Match: Only selected sub types are allowed
|
||||
QSet<QString> selectedTypes;
|
||||
for (const auto &type : activeSubTypes.keys()) {
|
||||
if (activeSubTypes[type]) {
|
||||
selectedTypes.insert(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedTypes.isEmpty()) {
|
||||
// Require all selected subtypes (TypeAnd)
|
||||
for (const auto &type : selectedTypes) {
|
||||
QString typeString = type;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrSubType));
|
||||
}
|
||||
|
||||
// Exclude any other types (TypeAndNot)
|
||||
for (const auto &type : typeButtons.keys()) {
|
||||
if (!selectedTypes.contains(type)) {
|
||||
QString typeString = type;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(typeString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrSubType));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default Includes Mode (TypeOr) - match any selected subtypes
|
||||
for (const auto &type : activeSubTypes.keys()) {
|
||||
if (activeSubTypes[type]) {
|
||||
QString typeString = type;
|
||||
filterModel->addFilter(
|
||||
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrSubType));
|
||||
}
|
||||
}
|
||||
}
|
||||
filterModel->blockSignals(false);
|
||||
filterModel->filterTree()->blockSignals(false);
|
||||
|
||||
emit filterModel->filterTree()->changed();
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySubTypeFilterWidget::updateFilterMode(bool checked)
|
||||
{
|
||||
exactMatchMode = checked;
|
||||
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
|
||||
updateSubTypeFilter();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplaySubTypeFilterWidget::syncWithFilterModel()
|
||||
{
|
||||
// Temporarily block signals for each button to prevent toggling while updating button states
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
it.value()->blockSignals(true);
|
||||
}
|
||||
|
||||
// Uncheck all buttons
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
it.value()->setChecked(false);
|
||||
}
|
||||
|
||||
// Get active filters for sub types
|
||||
QSet<QString> activeTypes;
|
||||
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::AttrSubType)) {
|
||||
if (filter->type() == CardFilter::Type::TypeAnd) {
|
||||
activeTypes.insert(filter->term());
|
||||
}
|
||||
}
|
||||
|
||||
// Check the buttons for active types
|
||||
for (const auto &type : activeTypes) {
|
||||
activeSubTypes[type] = true;
|
||||
if (typeButtons.contains(type)) {
|
||||
typeButtons[type]->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable signal emissions for each button
|
||||
for (auto it = typeButtons.begin(); it != typeButtons.end(); ++it) {
|
||||
it.value()->blockSignals(false);
|
||||
}
|
||||
|
||||
// Update the visibility of buttons
|
||||
updateSubTypeButtonsVisibility();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_SUB_TYPE_FILTER_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_SUB_TYPE_FILTER_WIDGET_H
|
||||
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class VisualDatabaseDisplaySubTypeFilterWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VisualDatabaseDisplaySubTypeFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||
void createSubTypeButtons();
|
||||
void updateSubTypeButtonsVisibility();
|
||||
int getMaxSubTypeCount() const;
|
||||
|
||||
void handleSubTypeToggled(const QString &mainType, bool active);
|
||||
void updateSubTypeFilter();
|
||||
void updateFilterMode(bool checked);
|
||||
void syncWithFilterModel();
|
||||
|
||||
private:
|
||||
FilterTreeModel *filterModel;
|
||||
QMap<QString, int> allSubCardTypesWithCount;
|
||||
QSpinBox *spinBox;
|
||||
QVBoxLayout *layout;
|
||||
QLineEdit *searchBox;
|
||||
FlowWidget *flowWidget;
|
||||
QPushButton *toggleButton; // Mode switch button
|
||||
|
||||
QMap<QString, bool> activeSubTypes; // Track active filters
|
||||
QMap<QString, QPushButton *> typeButtons; // Store toggle buttons
|
||||
|
||||
bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes"
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_DISPLAY_SUB_TYPE_FILTER_WIDGET_H
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
#include "visual_database_display_widget.h"
|
||||
|
||||
#include "../../../../deck/custom_line_edit.h"
|
||||
#include "../../../../game/cards/card_database.h"
|
||||
#include "../../../../game/cards/card_database_manager.h"
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
#include "../../../../utility/card_info_comparator.h"
|
||||
#include "../../pixel_map_generator.h"
|
||||
#include "../cards/card_info_picture_with_text_overlay_widget.h"
|
||||
#include "../quick_settings/settings_button_widget.h"
|
||||
#include "visual_database_display_color_filter_widget.h"
|
||||
#include "visual_database_display_filter_save_load_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"
|
||||
#include "visual_database_display_sub_type_filter_widget.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QScrollBar>
|
||||
#include <qpropertyanimation.h>
|
||||
#include <utility>
|
||||
|
||||
VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
|
||||
AbstractTabDeckEditor *_deckEditor,
|
||||
CardDatabaseModel *database_model,
|
||||
CardDatabaseDisplayModel *database_display_model)
|
||||
: QWidget(parent), deckEditor(_deckEditor), databaseModel(database_model),
|
||||
databaseDisplayModel(database_display_model)
|
||||
{
|
||||
cards = new QList<CardInfoPtr>;
|
||||
connect(databaseDisplayModel, &CardDatabaseDisplayModel::modelDirty, this,
|
||||
&VisualDatabaseDisplayWidget::modelDirty);
|
||||
|
||||
// Set up main layout and widgets
|
||||
setMinimumSize(0, 0);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
mainLayout = new QVBoxLayout(this);
|
||||
setLayout(mainLayout);
|
||||
mainLayout->setSpacing(1);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||
cardSizeWidget = new CardSizeWidget(this, flowWidget);
|
||||
|
||||
searchContainer = new QWidget(this);
|
||||
searchLayout = new QHBoxLayout(searchContainer);
|
||||
searchContainer->setLayout(searchLayout);
|
||||
|
||||
searchEdit = new SearchLineEdit();
|
||||
searchEdit->setObjectName("searchEdit");
|
||||
searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)"));
|
||||
searchEdit->setClearButtonEnabled(true);
|
||||
searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||
auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
||||
searchEdit->installEventFilter(&searchKeySignals);
|
||||
|
||||
setFocusProxy(searchEdit);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
filterModel = new FilterTreeModel();
|
||||
filterModel->setObjectName("filterModel");
|
||||
databaseDisplayModel->setFilterTree(filterModel->filterTree());
|
||||
|
||||
connect(filterModel, &FilterTreeModel::layoutChanged, this, &VisualDatabaseDisplayWidget::searchModelChanged);
|
||||
|
||||
searchKeySignals.setObjectName("searchKeySignals");
|
||||
connect(searchEdit, &QLineEdit::textChanged, this, &VisualDatabaseDisplayWidget::updateSearch);
|
||||
/*connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard()));
|
||||
connect(&searchKeySignals, SIGNAL(onCtrlC()), this, SLOT(copyDatabaseCellContents()));*/
|
||||
connect(help, &QAction::triggered, deckEditor->databaseDisplayDockWidget,
|
||||
&DeckEditorDatabaseDisplayWidget::showSearchSyntaxHelp);
|
||||
|
||||
databaseView = new QTreeView(this);
|
||||
databaseView->setObjectName("databaseView");
|
||||
databaseView->setFocusProxy(searchEdit);
|
||||
databaseView->setRootIsDecorated(false);
|
||||
databaseView->setItemDelegate(nullptr);
|
||||
databaseView->setSortingEnabled(true);
|
||||
databaseView->sortByColumn(0, Qt::AscendingOrder);
|
||||
databaseView->setModel(databaseDisplayModel);
|
||||
databaseView->setVisible(false);
|
||||
|
||||
searchEdit->setTreeView(databaseView);
|
||||
|
||||
colorFilterWidget = new VisualDatabaseDisplayColorFilterWidget(this, filterModel);
|
||||
|
||||
quickFilterWidget = new SettingsButtonWidget(this);
|
||||
|
||||
saveLoadWidget = new VisualDatabaseDisplayFilterSaveLoadWidget(this, filterModel);
|
||||
nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, deckEditor, filterModel);
|
||||
mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel);
|
||||
subTypeFilterWidget = new VisualDatabaseDisplaySubTypeFilterWidget(this, filterModel);
|
||||
setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel);
|
||||
|
||||
quickFilterWidget->addSettingsWidget(saveLoadWidget);
|
||||
quickFilterWidget->addSettingsWidget(nameFilterWidget);
|
||||
// quickFilterWidget->addSettingsWidget(mainTypeFilterWidget);
|
||||
quickFilterWidget->addSettingsWidget(subTypeFilterWidget);
|
||||
quickFilterWidget->addSettingsWidget(setFilterWidget);
|
||||
|
||||
searchLayout->addWidget(colorFilterWidget);
|
||||
searchLayout->addWidget(quickFilterWidget);
|
||||
searchLayout->addWidget(searchEdit);
|
||||
|
||||
mainLayout->addWidget(searchContainer);
|
||||
|
||||
mainLayout->addWidget(mainTypeFilterWidget);
|
||||
|
||||
mainLayout->addWidget(flowWidget);
|
||||
|
||||
mainLayout->addWidget(cardSizeWidget);
|
||||
|
||||
debounceTimer = new QTimer(this);
|
||||
debounceTimer->setSingleShot(true); // Ensure it only fires once after the timeout
|
||||
|
||||
connect(debounceTimer, &QTimer::timeout, this, &VisualDatabaseDisplayWidget::searchModelChanged);
|
||||
|
||||
loadCardsTimer = new QTimer(this);
|
||||
loadCardsTimer->setSingleShot(true); // Ensure it only fires once after the timeout
|
||||
|
||||
connect(loadCardsTimer, &QTimer::timeout, this, [this]() { loadCurrentPage(); });
|
||||
loadCardsTimer->start(5000);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
loadCurrentPage();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
|
||||
{
|
||||
emit cardClickedDatabaseDisplay(event, instance);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::onHover(const CardInfoPtr &hoveredCard)
|
||||
{
|
||||
emit cardHoveredDatabaseDisplay(hoveredCard);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::addCard(const CardInfoPtr &cardToAdd)
|
||||
{
|
||||
cards->append(cardToAdd);
|
||||
auto *display = new CardInfoPictureWithTextOverlayWidget(flowWidget, false);
|
||||
display->setScaleFactor(cardSizeWidget->getSlider()->value());
|
||||
display->setCard(cardToAdd);
|
||||
flowWidget->addWidget(display);
|
||||
connect(display, &CardInfoPictureWithTextOverlayWidget::imageClicked, this, &VisualDatabaseDisplayWidget::onClick);
|
||||
connect(display, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this, &VisualDatabaseDisplayWidget::onHover);
|
||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, display, &CardInfoPictureWidget::setScaleFactor);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::populateCards()
|
||||
{
|
||||
int rowCount = databaseDisplayModel->rowCount();
|
||||
cards->clear();
|
||||
|
||||
// Calculate the start and end indices for the current page
|
||||
int start = currentPage * cardsPerPage;
|
||||
int end = qMin(start + cardsPerPage, rowCount);
|
||||
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Fetching from " << start << " to " << end << " cards";
|
||||
// Load more cards if we are at the end of the current list and can fetch more
|
||||
if (end >= rowCount && databaseDisplayModel->canFetchMore(QModelIndex())) {
|
||||
qCDebug(VisualDatabaseDisplayLog) << "We gotta load more";
|
||||
databaseDisplayModel->fetchMore(QModelIndex());
|
||||
}
|
||||
|
||||
for (int row = start; row < end; ++row) {
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Adding " << row;
|
||||
QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn);
|
||||
QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole);
|
||||
qCDebug(VisualDatabaseDisplayLog) << name.toString();
|
||||
if (CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(name.toString())) {
|
||||
addCard(info);
|
||||
} else {
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!";
|
||||
}
|
||||
}
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::updateSearch(const QString &search) const
|
||||
{
|
||||
databaseDisplayModel->setStringFilter(search);
|
||||
QModelIndexList sel = databaseView->selectionModel()->selectedRows();
|
||||
if (sel.isEmpty() && databaseDisplayModel->rowCount())
|
||||
databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0),
|
||||
QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::searchModelChanged()
|
||||
{
|
||||
// Clear the current page and prepare for new data
|
||||
flowWidget->clearLayout(); // Clear existing cards
|
||||
cards->clear(); // Clear the card list
|
||||
// Reset scrollbar position to the top after loading new cards
|
||||
if (QScrollBar *scrollBar = flowWidget->scrollArea->verticalScrollBar()) {
|
||||
scrollBar->setValue(0); // Reset scrollbar to top
|
||||
}
|
||||
|
||||
currentPage = 0;
|
||||
loadCurrentPage();
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Search model changed";
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::loadNextPage()
|
||||
{
|
||||
// Calculate the start and end indices for the next page
|
||||
int rowCount = databaseDisplayModel->rowCount();
|
||||
int start = currentPage * cardsPerPage;
|
||||
int end = qMin(start + cardsPerPage, rowCount);
|
||||
|
||||
// Load more cards if we are at the end of the current list and can fetch more
|
||||
if (end >= rowCount && databaseDisplayModel->canFetchMore(QModelIndex())) {
|
||||
databaseDisplayModel->fetchMore(QModelIndex());
|
||||
}
|
||||
|
||||
// Load the next page of cards and add them to the flow widget
|
||||
for (int row = start; row < end; ++row) {
|
||||
QModelIndex index = databaseDisplayModel->index(row, CardDatabaseModel::NameColumn);
|
||||
QVariant name = databaseDisplayModel->data(index, Qt::DisplayRole);
|
||||
if (CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(name.toString())) {
|
||||
addCard(info);
|
||||
} else {
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Card " << name.toString() << " not found in database!";
|
||||
}
|
||||
}
|
||||
|
||||
// Update the current page
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::loadCurrentPage()
|
||||
{
|
||||
// Ensure only the initial page is loaded
|
||||
if (currentPage == 0) {
|
||||
// Only load the first page initially
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Loading the first page";
|
||||
populateCards();
|
||||
} else {
|
||||
// If not the first page, just load the next page and append to the flow widget
|
||||
loadNextPage();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::modelDirty() const
|
||||
{
|
||||
debounceTimer->start(debounceTime);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::sortCardList(const QStringList &properties,
|
||||
Qt::SortOrder order = Qt::AscendingOrder) const
|
||||
{
|
||||
CardInfoComparator comparator(properties, order);
|
||||
std::sort(cards->begin(), cards->end(), comparator);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::databaseDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
|
||||
{
|
||||
(void)topLeft;
|
||||
(void)bottomRight;
|
||||
qCDebug(VisualDatabaseDisplayLog) << "Database Data changed";
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
int totalRows = databaseDisplayModel->rowCount(); // Total number of cards
|
||||
int nextPageStartIndex = (currentPage + 1) * cardsPerPage;
|
||||
|
||||
// Handle scrolling down
|
||||
if (event->angleDelta().y() < 0) {
|
||||
// Check if the next page has any cards to load
|
||||
if (nextPageStartIndex < totalRows) {
|
||||
loadCurrentPage(); // Load the next page
|
||||
event->accept(); // Accept the event as valid
|
||||
return;
|
||||
}
|
||||
qCDebug(VisualDatabaseDisplayLog) << nextPageStartIndex << ":" << totalRows;
|
||||
}
|
||||
|
||||
// Prevent overscrolling when there's no more data to load
|
||||
event->ignore();
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#ifndef VISUAL_DATABASE_DISPLAY_WIDGET_H
|
||||
#define VISUAL_DATABASE_DISPLAY_WIDGET_H
|
||||
|
||||
#include "../../../../deck/custom_line_edit.h"
|
||||
#include "../../../../deck/deck_list_model.h"
|
||||
#include "../../../../game/cards/card_database.h"
|
||||
#include "../../../../game/cards/card_database_model.h"
|
||||
#include "../../../../game/filters/filter_tree_model.h"
|
||||
#include "../../../game_logic/key_signals.h"
|
||||
#include "../../../tabs/abstract_tab_deck_editor.h"
|
||||
#include "../../layouts/flow_layout.h"
|
||||
#include "../cards/card_info_picture_with_text_overlay_widget.h"
|
||||
#include "../cards/card_size_widget.h"
|
||||
#include "../general/layout_containers/flow_widget.h"
|
||||
#include "../general/layout_containers/overlap_control_widget.h"
|
||||
#include "visual_database_display_color_filter_widget.h"
|
||||
#include "visual_database_display_filter_save_load_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"
|
||||
#include "visual_database_display_sub_type_filter_widget.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
#include <qscrollarea.h>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(VisualDatabaseDisplayLog, "visual_database_display");
|
||||
|
||||
class VisualDatabaseDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VisualDatabaseDisplayWidget(QWidget *parent,
|
||||
AbstractTabDeckEditor *deckEditor,
|
||||
CardDatabaseModel *database_model,
|
||||
CardDatabaseDisplayModel *database_display_model);
|
||||
|
||||
void adjustCardsPerPage();
|
||||
void populateCards();
|
||||
void loadNextPage();
|
||||
void loadCurrentPage();
|
||||
void sortCardList(const QStringList &properties, Qt::SortOrder order) const;
|
||||
void setDeckList(const DeckList &new_deck_list_model);
|
||||
|
||||
QWidget *searchContainer;
|
||||
QHBoxLayout *searchLayout;
|
||||
SearchLineEdit *searchEdit;
|
||||
FilterTreeModel *filterModel;
|
||||
VisualDatabaseDisplayColorFilterWidget *colorFilterWidget;
|
||||
|
||||
public slots:
|
||||
void searchModelChanged();
|
||||
|
||||
signals:
|
||||
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
||||
void cardHoveredDatabaseDisplay(CardInfoPtr hoveredCard);
|
||||
|
||||
protected slots:
|
||||
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
||||
void onHover(const CardInfoPtr &hoveredCard);
|
||||
void addCard(const CardInfoPtr &cardToAdd);
|
||||
void databaseDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
void modelDirty() const;
|
||||
void updateSearch(const QString &search) const;
|
||||
|
||||
private:
|
||||
SettingsButtonWidget *quickFilterWidget;
|
||||
VisualDatabaseDisplayFilterSaveLoadWidget *saveLoadWidget;
|
||||
VisualDatabaseDisplayNameFilterWidget *nameFilterWidget;
|
||||
VisualDatabaseDisplayMainTypeFilterWidget *mainTypeFilterWidget;
|
||||
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
|
||||
VisualDatabaseDisplaySetFilterWidget *setFilterWidget;
|
||||
KeySignals searchKeySignals;
|
||||
AbstractTabDeckEditor *deckEditor;
|
||||
CardDatabaseModel *databaseModel;
|
||||
CardDatabaseDisplayModel *databaseDisplayModel;
|
||||
QTreeView *databaseView;
|
||||
QList<CardInfoPtr> *cards;
|
||||
QVBoxLayout *mainLayout;
|
||||
QScrollArea *scrollArea;
|
||||
FlowWidget *flowWidget;
|
||||
QWidget *overlapCategories;
|
||||
QVBoxLayout *overlapCategoriesLayout;
|
||||
OverlapControlWidget *overlapControlWidget;
|
||||
CardSizeWidget *cardSizeWidget;
|
||||
QWidget *container;
|
||||
QTimer *debounceTimer;
|
||||
QTimer *loadCardsTimer;
|
||||
|
||||
int debounceTime = 300; // in Ms
|
||||
int currentPage = 0; // Current page index
|
||||
int cardsPerPage = 100; // Number of cards per page
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_DISPLAY_WIDGET_H
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include "visual_database_filter_display_widget.h"
|
||||
|
||||
#include "../../../../settings/cache_settings.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFontMetrics>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
FilterDisplayWidget::FilterDisplayWidget(QWidget *parent, const QString &filename, FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterFilename(filename), filterModel(_filterModel)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
// Create layout
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
layout->setSpacing(5);
|
||||
|
||||
// Filter button (displays filename)
|
||||
filterButton = new QPushButton(filterFilename, this);
|
||||
layout->addWidget(filterButton);
|
||||
|
||||
// Create close button (inside the filter button)
|
||||
closeButton = new QPushButton("x", this);
|
||||
closeButton->setFixedSize(20, 20); // Small square close button
|
||||
closeButton->setFocusPolicy(Qt::NoFocus);
|
||||
layout->addWidget(closeButton);
|
||||
|
||||
// Connect the filter button for loading the filter
|
||||
connect(filterButton, &QPushButton::clicked, this, &FilterDisplayWidget::loadFilter);
|
||||
|
||||
// Connect the close button for deleting the filter
|
||||
connect(closeButton, &QPushButton::clicked, this, &FilterDisplayWidget::deleteFilter);
|
||||
}
|
||||
|
||||
QSize FilterDisplayWidget::sizeHint() const
|
||||
{
|
||||
// Calculate the size based on the filter name and the close button
|
||||
QFontMetrics fm(font());
|
||||
int textWidth = fm.horizontalAdvance(filterFilename);
|
||||
int width = textWidth + 30 + 16; // Space for the filename and close button
|
||||
int height = fm.height() + 10; // Height based on font size + padding
|
||||
|
||||
return QSize(width, height);
|
||||
}
|
||||
|
||||
void FilterDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
emit filterClicked();
|
||||
}
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void FilterDisplayWidget::loadFilter()
|
||||
{
|
||||
// Trigger the loading of the filter
|
||||
emit filterLoadRequested(filterFilename);
|
||||
}
|
||||
|
||||
void FilterDisplayWidget::deleteFilter()
|
||||
{
|
||||
// Show confirmation dialog before deleting the filter
|
||||
QMessageBox::StandardButton reply = QMessageBox::question(
|
||||
this, tr("Confirm Delete"), tr("Are you sure you want to delete the filter '%1'?").arg(filterFilename),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (reply == QMessageBox::Yes) {
|
||||
// If confirmed, delete the filter
|
||||
QString filePath = SettingsCache::instance().getFiltersPath() + QDir::separator() + filterFilename;
|
||||
QFile file(filePath);
|
||||
if (file.remove()) {
|
||||
emit filterDeleted(filterFilename); // Emit signal for deletion
|
||||
delete this; // Delete this widget
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Delete Failed"), tr("Failed to delete filter '%1'.").arg(filterFilename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef VISUAL_DATABASE_FILTER_DISPLAY_WIDGET_H
|
||||
#define VISUAL_DATABASE_FILTER_DISPLAY_WIDGET_H
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class FilterTreeModel;
|
||||
|
||||
class FilterDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FilterDisplayWidget(QWidget *parent, const QString &filename, FilterTreeModel *_filterModel);
|
||||
~FilterDisplayWidget() = default;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void loadFilter();
|
||||
void deleteFilter();
|
||||
|
||||
signals:
|
||||
void filterClicked();
|
||||
void filterLoadRequested(const QString &filename);
|
||||
void filterDeleted(const QString &filename);
|
||||
|
||||
private:
|
||||
QString filterFilename;
|
||||
FilterTreeModel *filterModel;
|
||||
QPushButton *filterButton; // Button for the filter text
|
||||
QPushButton *closeButton; // Close button to delete the filter
|
||||
};
|
||||
|
||||
#endif // VISUAL_DATABASE_FILTER_DISPLAY_WIDGET_H
|
||||
|
|
@ -101,6 +101,11 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
QPushButton *deckPathButton = new QPushButton("...");
|
||||
connect(deckPathButton, SIGNAL(clicked()), this, SLOT(deckPathButtonClicked()));
|
||||
|
||||
filtersPathEdit = new QLineEdit(settings.getFiltersPath());
|
||||
filtersPathEdit->setReadOnly(true);
|
||||
QPushButton *filtersPathButton = new QPushButton("...");
|
||||
connect(filtersPathButton, SIGNAL(clicked()), this, SLOT(filtersPathButtonClicked()));
|
||||
|
||||
replaysPathEdit = new QLineEdit(settings.getReplaysPath());
|
||||
replaysPathEdit->setReadOnly(true);
|
||||
QPushButton *replaysPathButton = new QPushButton("...");
|
||||
|
|
@ -132,6 +137,7 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
bool isPortable = settings.getIsPortableBuild();
|
||||
if (isPortable) {
|
||||
deckPathEdit->setEnabled(false);
|
||||
filtersPathEdit->setEnabled(false);
|
||||
replaysPathEdit->setEnabled(false);
|
||||
picsPathEdit->setEnabled(false);
|
||||
cardDatabasePathEdit->setEnabled(false);
|
||||
|
|
@ -154,24 +160,27 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
pathsGrid->addWidget(&deckPathLabel, 0, 0);
|
||||
pathsGrid->addWidget(deckPathEdit, 0, 1);
|
||||
pathsGrid->addWidget(deckPathButton, 0, 2);
|
||||
pathsGrid->addWidget(&replaysPathLabel, 1, 0);
|
||||
pathsGrid->addWidget(replaysPathEdit, 1, 1);
|
||||
pathsGrid->addWidget(replaysPathButton, 1, 2);
|
||||
pathsGrid->addWidget(&picsPathLabel, 2, 0);
|
||||
pathsGrid->addWidget(picsPathEdit, 2, 1);
|
||||
pathsGrid->addWidget(picsPathButton, 2, 2);
|
||||
pathsGrid->addWidget(&cardDatabasePathLabel, 3, 0);
|
||||
pathsGrid->addWidget(cardDatabasePathEdit, 3, 1);
|
||||
pathsGrid->addWidget(cardDatabasePathButton, 3, 2);
|
||||
pathsGrid->addWidget(&customCardDatabasePathLabel, 4, 0);
|
||||
pathsGrid->addWidget(customCardDatabasePathEdit, 4, 1);
|
||||
pathsGrid->addWidget(customCardDatabasePathButton, 4, 2);
|
||||
pathsGrid->addWidget(&tokenDatabasePathLabel, 5, 0);
|
||||
pathsGrid->addWidget(tokenDatabasePathEdit, 5, 1);
|
||||
pathsGrid->addWidget(tokenDatabasePathButton, 5, 2);
|
||||
pathsGrid->addWidget(&filtersPathLabel, 1, 0);
|
||||
pathsGrid->addWidget(filtersPathEdit, 1, 1);
|
||||
pathsGrid->addWidget(filtersPathButton, 1, 2);
|
||||
pathsGrid->addWidget(&replaysPathLabel, 2, 0);
|
||||
pathsGrid->addWidget(replaysPathEdit, 2, 1);
|
||||
pathsGrid->addWidget(replaysPathButton, 2, 2);
|
||||
pathsGrid->addWidget(&picsPathLabel, 3, 0);
|
||||
pathsGrid->addWidget(picsPathEdit, 3, 1);
|
||||
pathsGrid->addWidget(picsPathButton, 3, 2);
|
||||
pathsGrid->addWidget(&cardDatabasePathLabel, 4, 0);
|
||||
pathsGrid->addWidget(cardDatabasePathEdit, 4, 1);
|
||||
pathsGrid->addWidget(cardDatabasePathButton, 4, 2);
|
||||
pathsGrid->addWidget(&customCardDatabasePathLabel, 5, 0);
|
||||
pathsGrid->addWidget(customCardDatabasePathEdit, 5, 1);
|
||||
pathsGrid->addWidget(customCardDatabasePathButton, 5, 2);
|
||||
pathsGrid->addWidget(&tokenDatabasePathLabel, 6, 0);
|
||||
pathsGrid->addWidget(tokenDatabasePathEdit, 6, 1);
|
||||
pathsGrid->addWidget(tokenDatabasePathButton, 6, 2);
|
||||
if (!isPortable) {
|
||||
pathsGrid->addWidget(resetAllPathsButton, 6, 0);
|
||||
pathsGrid->addWidget(allPathsResetLabel, 6, 1);
|
||||
pathsGrid->addWidget(resetAllPathsButton, 7, 0);
|
||||
pathsGrid->addWidget(allPathsResetLabel, 7, 1);
|
||||
}
|
||||
pathsGroupBox = new QGroupBox;
|
||||
pathsGroupBox->setLayout(pathsGrid);
|
||||
|
|
@ -226,6 +235,16 @@ void GeneralSettingsPage::deckPathButtonClicked()
|
|||
SettingsCache::instance().setDeckPath(path);
|
||||
}
|
||||
|
||||
void GeneralSettingsPage::filtersPathButtonClicked()
|
||||
{
|
||||
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), filtersPathEdit->text());
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
filtersPathEdit->setText(path);
|
||||
SettingsCache::instance().setFiltersPath(path);
|
||||
}
|
||||
|
||||
void GeneralSettingsPage::replaysPathButtonClicked()
|
||||
{
|
||||
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), replaysPathEdit->text());
|
||||
|
|
@ -307,6 +326,7 @@ void GeneralSettingsPage::retranslateUi()
|
|||
advertiseTranslationPageLabel.setText(
|
||||
QString("<a href='%1'>%2</a>").arg(WIKI_TRANSLATION_FAQ).arg(tr("How to help with translations")));
|
||||
deckPathLabel.setText(tr("Decks directory:"));
|
||||
filtersPathLabel.setText(tr("Filters directory:"));
|
||||
replaysPathLabel.setText(tr("Replays directory:"));
|
||||
picsPathLabel.setText(tr("Pictures directory:"));
|
||||
cardDatabasePathLabel.setText(tr("Card database:"));
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public:
|
|||
|
||||
private slots:
|
||||
void deckPathButtonClicked();
|
||||
void filtersPathButtonClicked();
|
||||
void replaysPathButtonClicked();
|
||||
void picsPathButtonClicked();
|
||||
void cardDatabasePathButtonClicked();
|
||||
|
|
@ -58,6 +59,7 @@ private:
|
|||
QStringList findQmFiles();
|
||||
QString languageName(const QString &lang);
|
||||
QLineEdit *deckPathEdit;
|
||||
QLineEdit *filtersPathEdit;
|
||||
QLineEdit *replaysPathEdit;
|
||||
QLineEdit *picsPathEdit;
|
||||
QLineEdit *cardDatabasePathEdit;
|
||||
|
|
@ -74,6 +76,7 @@ private:
|
|||
QComboBox updateReleaseChannelBox;
|
||||
QLabel languageLabel;
|
||||
QLabel deckPathLabel;
|
||||
QLabel filtersPathLabel;
|
||||
QLabel replaysPathLabel;
|
||||
QLabel picsPathLabel;
|
||||
QLabel cardDatabasePathLabel;
|
||||
|
|
|
|||
|
|
@ -451,6 +451,12 @@ void SettingsCache::setDeckPath(const QString &_deckPath)
|
|||
settings->setValue("paths/decks", deckPath);
|
||||
}
|
||||
|
||||
void SettingsCache::setFiltersPath(const QString &_filtersPath)
|
||||
{
|
||||
filtersPath = _filtersPath;
|
||||
settings->setValue("paths/filters", filtersPath);
|
||||
}
|
||||
|
||||
void SettingsCache::setReplaysPath(const QString &_replaysPath)
|
||||
{
|
||||
replaysPath = _replaysPath;
|
||||
|
|
@ -1312,6 +1318,7 @@ void SettingsCache::loadPaths()
|
|||
{
|
||||
QString dataPath = getDataPath();
|
||||
deckPath = getSafeConfigPath("paths/decks", dataPath + "/decks/");
|
||||
filtersPath = getSafeConfigPath("paths/filters", dataPath + "/filters/");
|
||||
replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/");
|
||||
themesPath = getSafeConfigPath("paths/themes", dataPath + "/themes/");
|
||||
picsPath = getSafeConfigPath("paths/pics", dataPath + "/pics/");
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ private:
|
|||
QByteArray tokenDialogGeometry;
|
||||
QByteArray setsDialogGeometry;
|
||||
QString lang;
|
||||
QString deckPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
|
||||
QString deckPath, filtersPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
|
||||
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName;
|
||||
bool tabVisualDeckStorageOpen, tabServerOpen, tabAccountOpen, tabDeckStorageOpen, tabReplaysOpen, tabAdminOpen,
|
||||
tabLogOpen;
|
||||
|
|
@ -233,6 +233,10 @@ public:
|
|||
{
|
||||
return deckPath;
|
||||
}
|
||||
QString getFiltersPath() const
|
||||
{
|
||||
return filtersPath;
|
||||
}
|
||||
QString getReplaysPath() const
|
||||
{
|
||||
return replaysPath;
|
||||
|
|
@ -754,6 +758,7 @@ public slots:
|
|||
void setShowTipsOnStartup(bool _showTipsOnStartup);
|
||||
void setSeenTips(const QList<int> &_seenTips);
|
||||
void setDeckPath(const QString &_deckPath);
|
||||
void setFiltersPath(const QString &_filtersPath);
|
||||
void setReplaysPath(const QString &_replaysPath);
|
||||
void setThemesPath(const QString &_themesPath);
|
||||
void setCustomCardDatabasePath(const QString &_customCardDatabasePath);
|
||||
|
|
|
|||
75
cockatrice/src/utility/card_info_comparator.cpp
Normal file
75
cockatrice/src/utility/card_info_comparator.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "card_info_comparator.h"
|
||||
|
||||
CardInfoComparator::CardInfoComparator(const QStringList &properties, Qt::SortOrder order)
|
||||
: m_properties(properties), m_order(order)
|
||||
{
|
||||
}
|
||||
|
||||
bool CardInfoComparator::operator()(const CardInfoPtr &a, const CardInfoPtr &b) const
|
||||
{
|
||||
// Iterate over each property in the list
|
||||
for (const QString &property : m_properties) {
|
||||
QVariant valueA = getProperty(a, property);
|
||||
QVariant valueB = getProperty(b, property);
|
||||
|
||||
// Compare the current property
|
||||
if (valueA != valueB) {
|
||||
// If values differ, perform comparison
|
||||
return compareVariants(valueA, valueB) ? (m_order == Qt::AscendingOrder) : (m_order == Qt::DescendingOrder);
|
||||
}
|
||||
}
|
||||
|
||||
// If all properties are equal, return false (indicating they are considered equal for sorting purposes)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CardInfoComparator::compareVariants(const QVariant &a, const QVariant &b) const
|
||||
{
|
||||
// Determine the type of QVariant based on Qt version
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (a.typeId() != b.typeId()) {
|
||||
#else
|
||||
if (a.type() != b.type()) {
|
||||
#endif
|
||||
// If they are not the same type, compare as strings
|
||||
return a.toString() < b.toString();
|
||||
}
|
||||
|
||||
// Perform type-specific comparison
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
switch (a.typeId()) {
|
||||
#else
|
||||
switch (a.type()) {
|
||||
#endif
|
||||
case QMetaType::Int:
|
||||
return a.toInt() < b.toInt();
|
||||
case QMetaType::Double:
|
||||
return a.toDouble() < b.toDouble();
|
||||
case QMetaType::QString:
|
||||
return a.toString() < b.toString();
|
||||
case QMetaType::Bool:
|
||||
return a.toBool() < b.toBool();
|
||||
default:
|
||||
// Default to comparing as strings
|
||||
return a.toString() < b.toString();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant CardInfoComparator::getProperty(const CardInfoPtr &card, const QString &property) const
|
||||
{
|
||||
// Check if the property exists in the main fields of the class
|
||||
if (property == "name") {
|
||||
return card->getName();
|
||||
} else if (property == "text") {
|
||||
return card->getText();
|
||||
} else if (property == "isToken") {
|
||||
return card->getIsToken();
|
||||
}
|
||||
|
||||
// Otherwise, check if it's a custom property in the QVariantHash
|
||||
if (card->hasProperty(property)) {
|
||||
return card->getProperty(property);
|
||||
}
|
||||
|
||||
return QVariant(); // Return an invalid variant if the property does not exist
|
||||
}
|
||||
24
cockatrice/src/utility/card_info_comparator.h
Normal file
24
cockatrice/src/utility/card_info_comparator.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef CARD_INFO_COMPARATOR_H
|
||||
#define CARD_INFO_COMPARATOR_H
|
||||
|
||||
#include "../game/cards/card_database.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
#include <Qt>
|
||||
|
||||
class CardInfoComparator
|
||||
{
|
||||
public:
|
||||
explicit CardInfoComparator(const QStringList &properties, Qt::SortOrder order = Qt::AscendingOrder);
|
||||
bool operator()(const CardInfoPtr &a, const CardInfoPtr &b) const;
|
||||
|
||||
private:
|
||||
QStringList m_properties; // List of properties to sort by
|
||||
Qt::SortOrder m_order;
|
||||
|
||||
QVariant getProperty(const CardInfoPtr &card, const QString &property) const;
|
||||
bool compareVariants(const QVariant &a, const QVariant &b) const;
|
||||
};
|
||||
|
||||
#endif // CARD_INFO_COMPARATOR_H
|
||||
|
|
@ -106,6 +106,9 @@ void SettingsCache::setSeenTips(const QList<int> & /* _seenTips */)
|
|||
void SettingsCache::setDeckPath(const QString & /* _deckPath */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setFiltersPath(const QString & /*_filtersPath */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setReplaysPath(const QString & /* _replaysPath */)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,9 @@ void SettingsCache::setSeenTips(const QList<int> & /* _seenTips */)
|
|||
void SettingsCache::setDeckPath(const QString & /* _deckPath */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setFiltersPath(const QString & /*_filtersPath */)
|
||||
{
|
||||
}
|
||||
void SettingsCache::setReplaysPath(const QString & /* _replaysPath */)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user