* move message_log_widget to game

* move files

* update headers

* fix cmakelists

* oracle fixes

* split implementation out to cpp

* fix recursive import

* fix main file

* format
This commit is contained in:
ebbit1q 2025-09-20 14:35:52 +02:00 committed by GitHub
parent f484c98152
commit 17dcaf9afa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
337 changed files with 728 additions and 721 deletions

View file

@ -0,0 +1,257 @@
#include "visual_database_display_color_filter_widget.h"
#include "../../../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);
// 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(filterModel, &FilterTreeModel::layoutChanged, this,
[this]() { 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;
}
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
}
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active)
{
if (active) {
addFilter(color);
} else {
removeFilter(color);
}
}
void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color)
{
QString colorString = color;
QString typeStr;
// Remove previous filters
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<const CardFilter *> matchingFilters;
for (const CardFilter *filter : allColorFilters) {
if (filter->term() == color) {
matchingFilters.append(filter);
}
}
for (const CardFilter *filter : matchingFilters) {
filterModel->removeFilter(filter);
}
// Add actual filter
switch (currentMode) {
case FilterMode::ExactMatch:
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
break;
case FilterMode::Includes:
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
case FilterMode::IncludeExclude:
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
break;
}
}
void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color)
{
QString colorString = color;
// Remove inclusion filters
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<const CardFilter *> matchingFilters;
for (const CardFilter *filter : allColorFilters) {
if (filter->term() == color) {
matchingFilters.append(filter);
}
}
for (const CardFilter *filter : matchingFilters) {
filterModel->removeFilter(filter);
}
// Add exclusion filters if the mode demands it
switch (currentMode) {
case FilterMode::ExactMatch:
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
break;
case FilterMode::IncludeExclude:
filterModel->addFilter(
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
break;
case FilterMode::Includes:
// No exclusion in Includes mode
break;
}
}
void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
{
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;
}
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
handleColorToggled(manaSymbolWidget->getSymbolChar(), manaSymbolWidget->isColorActive());
}
filterModel->blockSignals(false);
filterModel->filterTree()->blockSignals(false);
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
retranslateUi(); // Update button text based on the mode
emit filterModeChanged(currentMode); // Signal mode change
}
void VisualDatabaseDisplayColorFilterWidget::setManaSymbolActive(QChar color, bool active)
{
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
if (manaSymbolWidget->getSymbolChar() == color) {
manaSymbolWidget->setColorActive(active);
}
}
}
QList<QChar> VisualDatabaseDisplayColorFilterWidget::getActiveColors()
{
QList<QChar> activeColors;
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
if (manaSymbolWidget->isColorActive()) {
activeColors.append(manaSymbolWidget->getSymbolChar());
}
}
return activeColors;
}
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
{
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
bool found = false;
for (const CardFilter *filter : allColorFilters) {
if (manaSymbolWidget->getSymbolChar() == filter->term()) {
switch (currentMode) {
case FilterMode::ExactMatch:
switch (filter->type()) {
case CardFilter::Type::TypeAnd:
setManaSymbolActive(filter->term().at(0), true);
break;
default:
setManaSymbolActive(filter->term().at(0), false);
break;
}
break;
case FilterMode::Includes:
switch (filter->type()) {
case CardFilter::Type::TypeOr:
setManaSymbolActive(filter->term().at(0), true);
break;
default:
setManaSymbolActive(filter->term().at(0), false);
break;
}
break;
case FilterMode::IncludeExclude:
switch (filter->type()) {
case CardFilter::Type::TypeOr:
setManaSymbolActive(filter->term().at(0), true);
break;
default:
setManaSymbolActive(filter->term().at(0), false);
break;
}
break;
}
found = true;
}
}
if (!found) {
setManaSymbolActive(manaSymbolWidget->getSymbolChar(), false);
}
}
}

View file

@ -0,0 +1,63 @@
#ifndef VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H
#include "../../../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);
private slots:
void handleColorToggled(QChar color, bool active);
void addFilter(QChar color);
void removeFilter(QChar color);
void updateFilterMode();
void setManaSymbolActive(QChar color, bool active);
QList<QChar> getActiveColors();
void syncWithFilterModel();
private:
FilterTreeModel *filterModel;
QHBoxLayout *layout;
QPushButton *toggleButton;
FilterMode currentMode = FilterMode::Includes; // Default mode
};
#endif // VISUAL_DATABASE_DISPLAY_COLOR_FILTER_WIDGET_H

View file

@ -0,0 +1,154 @@
#include "visual_database_display_filter_save_load_widget.h"
#include "../../../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"));
saveButton->setToolTip(tr("Save all currently applied filters to a file"));
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::Name);
// 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);
}
}
}

View file

@ -0,0 +1,43 @@
#ifndef VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_FILTER_SAVE_LOAD_WIDGET_H
#include "../../../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

View file

@ -0,0 +1,204 @@
#include "visual_database_display_main_type_filter_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/filter_tree.h"
#include "../../../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
retranslateUi();
}
void VisualDatabaseDisplayMainTypeFilterWidget::retranslateUi()
{
spinBox->setToolTip(tr("Do not display card main-types with less than this amount of cards in the database"));
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
}
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();
}

View file

@ -0,0 +1,43 @@
#ifndef VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_MAIN_TYPE_FILTER_WIDGET_H
#include "../../../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 retranslateUi();
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

View file

@ -0,0 +1,186 @@
#include "visual_database_display_name_filter_widget.h"
#include "../../../dialogs/dlg_load_deck_from_clipboard.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);
loadFromClipboardButton = new QPushButton(this);
layout->addWidget(loadFromClipboardButton);
connect(loadFromClipboardButton, &QPushButton::clicked, this,
&VisualDatabaseDisplayNameFilterWidget::actLoadFromClipboard);
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"));
loadFromDeckButton->setToolTip(tr("Apply all card names in currently loaded deck as exact match name filters"));
loadFromClipboardButton->setText(tr("Load from Clipboard"));
loadFromClipboardButton->setToolTip(tr("Apply all card names in clipboard as exact match name filters"));
}
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::actLoadFromClipboard()
{
DlgLoadDeckFromClipboard dlg(this);
if (!dlg.exec())
return;
QStringList cardsInClipboard = dlg.getDeckList()->getCardList();
for (QString cardName : cardsInClipboard) {
createNameFilter(cardName);
}
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);
QTimer::singleShot(0, this,
&VisualDatabaseDisplayNameFilterWidget::updateFilterModel); // Avoid concurrent modification
});
flowWidget->addWidget(button);
activeFilters[name] = button;
}
void VisualDatabaseDisplayNameFilterWidget::removeNameFilter(const QString &name)
{
if (activeFilters.contains(name)) {
activeFilters[name]->deleteLater(); // Safe deletion
activeFilters.remove(name);
}
}
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);
}
}
}

View file

@ -0,0 +1,47 @@
#ifndef VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H
#include "../../../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;
QPushButton *loadFromClipboardButton;
QMap<QString, QPushButton *> activeFilters; // Store active name filter buttons
private slots:
void actLoadFromDeck();
void actLoadFromClipboard();
};
#endif // VISUAL_DATABASE_DISPLAY_NAME_FILTER_WIDGET_H

View file

@ -0,0 +1,272 @@
#include "visual_database_display_set_filter_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/filter_tree.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../settings/cache_settings.h"
#include <QLineEdit>
#include <QPushButton>
#include <QTimer>
#include <algorithm>
VisualDatabaseDisplayRecentSetFilterSettingsWidget::VisualDatabaseDisplayRecentSetFilterSettingsWidget(QWidget *parent)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
setLayout(layout);
filterToMostRecentSetsCheckBox = new QCheckBox(this);
filterToMostRecentSetsCheckBox->setChecked(
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsEnabled());
connect(filterToMostRecentSetsCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled);
filterToMostRecentSetsAmount = new QSpinBox(this);
filterToMostRecentSetsAmount->setMinimum(1);
filterToMostRecentSetsAmount->setMaximum(100);
filterToMostRecentSetsAmount->setValue(
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount());
connect(filterToMostRecentSetsAmount, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount);
layout->addWidget(filterToMostRecentSetsCheckBox);
layout->addWidget(filterToMostRecentSetsAmount);
retranslateUi();
}
void VisualDatabaseDisplayRecentSetFilterSettingsWidget::retranslateUi()
{
filterToMostRecentSetsCheckBox->setText(tr("Filter to most recent sets"));
}
VisualDatabaseDisplaySetFilterWidget::VisualDatabaseDisplaySetFilterWidget(QWidget *parent,
FilterTreeModel *_filterModel)
: QWidget(parent), filterModel(_filterModel)
{
setMinimumWidth(300);
setMaximumHeight(300);
layout = new QVBoxLayout(this);
setLayout(layout);
recentSetsSettingsWidget = new VisualDatabaseDisplayRecentSetFilterSettingsWidget(this);
layout->addWidget(recentSetsSettingsWidget);
connect(&SettingsCache::instance(), &SettingsCache::visualDatabaseDisplayFilterToMostRecentSetsEnabledChanged, this,
&VisualDatabaseDisplaySetFilterWidget::filterToRecentSets);
connect(&SettingsCache::instance(), &SettingsCache::visualDatabaseDisplayFilterToMostRecentSetsAmountChanged, this,
&VisualDatabaseDisplaySetFilterWidget::filterToRecentSets);
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
retranslateUi();
}
void VisualDatabaseDisplaySetFilterWidget::retranslateUi()
{
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
}
void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
{
SetList allSets = CardDatabaseManager::getInstance()->getSetList();
// Sort by release date
std::sort(allSets.begin(), allSets.end(),
[](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); });
for (const auto &set : allSets) {
QString shortName = set->getShortName();
QString longName = set->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); });
}
filterToRecentSets();
}
void VisualDatabaseDisplaySetFilterWidget::filterToRecentSets()
{
if (SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsEnabled()) {
for (auto set : activeSets.keys()) {
activeSets[set] = false;
}
SetList allSets = CardDatabaseManager::getInstance()->getSetList();
// Sort by release date
std::sort(allSets.begin(), allSets.end(),
[](const auto &a, const auto &b) { return a->getReleaseDate() > b->getReleaseDate(); });
int setsToPreactivate = SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount();
int setsActivated = 0;
for (const auto &set : allSets) {
QString shortName = set->getShortName();
QString longName = set->getLongName();
auto button = setButtons[shortName];
if (setsActivated < setsToPreactivate) {
setsActivated++;
activeSets[shortName] = true;
button->blockSignals(true);
button->setChecked(true);
button->blockSignals(false);
} else {
break;
}
}
updateSetFilter();
updateSetButtonsVisibility();
}
}
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]->blockSignals(true);
setButtons[key]->setChecked(active);
setButtons[key]->blockSignals(false);
}
}
void VisualDatabaseDisplaySetFilterWidget::updateFilterMode(bool checked)
{
exactMatchMode = checked;
updateSetFilter();
retranslateUi();
}

View file

@ -0,0 +1,61 @@
#ifndef VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_SET_FILTER_WIDGET_H
#include "../../../filters/filter_tree_model.h"
#include "../general/layout_containers/flow_widget.h"
#include <QCheckBox>
#include <QLineEdit>
#include <QMap>
#include <QPushButton>
#include <QSpinBox>
#include <QToolButton>
#include <QVBoxLayout>
#include <QWidget>
class VisualDatabaseDisplayRecentSetFilterSettingsWidget : public QWidget
{
Q_OBJECT
public:
VisualDatabaseDisplayRecentSetFilterSettingsWidget(QWidget *parent);
void retranslateUi();
private:
QHBoxLayout *layout;
QCheckBox *filterToMostRecentSetsCheckBox;
QSpinBox *filterToMostRecentSetsAmount;
};
class VisualDatabaseDisplaySetFilterWidget : public QWidget
{
Q_OBJECT
public:
explicit VisualDatabaseDisplaySetFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
void retranslateUi();
void createSetButtons();
void filterToRecentSets();
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;
VisualDatabaseDisplayRecentSetFilterSettingsWidget *recentSetsSettingsWidget;
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

View file

@ -0,0 +1,211 @@
#include "visual_database_display_sub_type_filter_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/filter_tree.h"
#include "../../../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
retranslateUi();
}
void VisualDatabaseDisplaySubTypeFilterWidget::retranslateUi()
{
spinBox->setToolTip(tr("Do not display card sub-types with less than this amount of cards in the database"));
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
}
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();
}

View file

@ -0,0 +1,44 @@
#ifndef VISUAL_DATABASE_DISPLAY_SUB_TYPE_FILTER_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_SUB_TYPE_FILTER_WIDGET_H
#include "../../../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 retranslateUi();
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

View file

@ -0,0 +1,378 @@
#include "visual_database_display_widget.h"
#include "../../../database/card_database.h"
#include "../../../database/card_database_manager.h"
#include "../../../deck/custom_line_edit.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../filters/syntax_help.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<ExactCard>;
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);
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(searchEdit); });
searchEdit->installEventFilter(&searchKeySignals);
setFocusProxy(searchEdit);
setFocusPolicy(Qt::ClickFocus);
filterModel = new FilterTreeModel();
filterModel->setObjectName("filterModel");
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()));*/
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);
filterContainer = new QWidget(this);
filterContainerLayout = new QHBoxLayout(filterContainer);
filterContainer->setLayout(filterContainerLayout);
clearFilterWidget = new QToolButton();
clearFilterWidget->setFixedSize(32, 32);
clearFilterWidget->setIcon(QPixmap("theme:icons/delete"));
connect(clearFilterWidget, &QToolButton::clicked, this, [this] {
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
filterModel->clear();
filterModel->blockSignals(false);
filterModel->filterTree()->blockSignals(false);
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
});
quickFilterSaveLoadWidget = new SettingsButtonWidget(this);
quickFilterSaveLoadWidget->setButtonIcon(QPixmap("theme:icons/lock"));
quickFilterNameWidget = new SettingsButtonWidget(this);
quickFilterNameWidget->setButtonIcon(QPixmap("theme:icons/rename"));
quickFilterSubTypeWidget = new SettingsButtonWidget(this);
quickFilterSubTypeWidget->setButtonIcon(QPixmap("theme:icons/player"));
quickFilterSetWidget = new SettingsButtonWidget(this);
quickFilterSetWidget->setButtonIcon(QPixmap("theme:icons/scales"));
filterContainer->setMaximumHeight(80);
databaseLoadIndicator = new QLabel(this);
databaseLoadIndicator->setAlignment(Qt::AlignCenter);
mainLayout->addWidget(databaseLoadIndicator);
if (CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) {
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&VisualDatabaseDisplayWidget::initialize);
quickFilterSaveLoadWidget->setVisible(false);
quickFilterNameWidget->setVisible(false);
quickFilterSubTypeWidget->setVisible(false);
quickFilterSetWidget->setVisible(false);
} else {
initialize();
databaseLoadIndicator->setVisible(false);
}
retranslateUi();
}
void VisualDatabaseDisplayWidget::initialize()
{
databaseLoadIndicator->setVisible(false);
quickFilterSaveLoadWidget->setVisible(true);
quickFilterNameWidget->setVisible(true);
quickFilterSubTypeWidget->setVisible(true);
quickFilterSetWidget->setVisible(true);
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);
quickFilterSaveLoadWidget->addSettingsWidget(saveLoadWidget);
quickFilterNameWidget->addSettingsWidget(nameFilterWidget);
quickFilterSubTypeWidget->addSettingsWidget(subTypeFilterWidget);
quickFilterSetWidget->addSettingsWidget(setFilterWidget);
filterContainerLayout->addWidget(quickFilterSaveLoadWidget);
filterContainerLayout->addWidget(quickFilterNameWidget);
filterContainerLayout->addWidget(quickFilterSubTypeWidget);
filterContainerLayout->addWidget(quickFilterSetWidget);
filterContainerLayout->addWidget(mainTypeFilterWidget);
searchLayout->addWidget(colorFilterWidget);
searchLayout->addWidget(clearFilterWidget);
searchLayout->addWidget(searchEdit);
mainLayout->addWidget(searchContainer);
mainLayout->addWidget(filterContainer);
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);
databaseDisplayModel->setFilterTree(filterModel->filterTree());
connect(filterModel, &FilterTreeModel::layoutChanged, 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);
retranslateUi();
}
void VisualDatabaseDisplayWidget::retranslateUi()
{
databaseLoadIndicator->setText(tr("Loading database ..."));
clearFilterWidget->setToolTip(tr("Clear all filters"));
quickFilterSaveLoadWidget->setToolTip(tr("Save and load filters"));
quickFilterNameWidget->setToolTip(tr("Filter by exact card name"));
quickFilterSubTypeWidget->setToolTip(tr("Filter by card sub-type"));
quickFilterSetWidget->setToolTip(tr("Filter by set"));
}
void VisualDatabaseDisplayWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
loadCurrentPage();
}
void VisualDatabaseDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
{
emit cardClickedDatabaseDisplay(event, instance);
}
void VisualDatabaseDisplayWidget::onHover(const ExactCard &hoveredCard)
{
emit cardHoveredDatabaseDisplay(hoveredCard);
}
void VisualDatabaseDisplayWidget::addCard(const ExactCard &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::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::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::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());
}
loadPage(start, end);
}
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
loadPage(start, end);
}
void VisualDatabaseDisplayWidget::loadPage(int start, int end)
{
QList<const CardFilter *> setFilters = filterModel->getFiltersOfType(CardFilter::AttrSet);
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()->getCardInfo(name.toString())) {
if (!setFilters.empty()) {
SetToPrintingsMap setMap = info->getSets();
for (const CardFilter *setFilter : setFilters) {
if (setMap.contains(setFilter->term())) {
for (PrintingInfo printing : setMap[setFilter->term()]) {
addCard(ExactCard(info, printing));
}
}
}
} else {
addCard(CardDatabaseManager::getInstance()->getPreferredCard(info));
}
} else {
qCDebug(VisualDatabaseDisplayLog) << "Card not found in database!";
}
}
currentPage++;
}
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](const ExactCard &a, const ExactCard &b) {
return comparator(a.getCardPtr(), b.getCardPtr());
});
}
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();
}

View file

@ -0,0 +1,112 @@
#ifndef VISUAL_DATABASE_DISPLAY_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_WIDGET_H
#include "../../../database/card_database.h"
#include "../../../database/card_database_model.h"
#include "../../../deck/custom_line_edit.h"
#include "../../../deck/deck_list_model.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../../../utility/key_signals.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 retranslateUi();
void adjustCardsPerPage();
void populateCards();
void loadPage(int start, int end);
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(const ExactCard &hoveredCard);
protected slots:
void initialize();
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void onHover(const ExactCard &hoveredCard);
void addCard(const ExactCard &cardToAdd);
void databaseDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void wheelEvent(QWheelEvent *event) override;
void modelDirty() const;
void updateSearch(const QString &search) const;
private:
QLabel *databaseLoadIndicator;
QToolButton *clearFilterWidget;
QWidget *filterContainer;
QHBoxLayout *filterContainerLayout;
SettingsButtonWidget *quickFilterSaveLoadWidget;
VisualDatabaseDisplayFilterSaveLoadWidget *saveLoadWidget;
SettingsButtonWidget *quickFilterNameWidget;
VisualDatabaseDisplayNameFilterWidget *nameFilterWidget;
VisualDatabaseDisplayMainTypeFilterWidget *mainTypeFilterWidget;
SettingsButtonWidget *quickFilterSubTypeWidget;
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
SettingsButtonWidget *quickFilterSetWidget;
VisualDatabaseDisplaySetFilterWidget *setFilterWidget;
KeySignals searchKeySignals;
AbstractTabDeckEditor *deckEditor;
CardDatabaseModel *databaseModel;
CardDatabaseDisplayModel *databaseDisplayModel;
QTreeView *databaseView;
QList<ExactCard> *cards;
QVBoxLayout *mainLayout;
QScrollArea *scrollArea;
FlowWidget *flowWidget;
QWidget *overlapCategories;
QVBoxLayout *overlapCategoriesLayout;
OverlapControlWidget *overlapControlWidget;
CardSizeWidget *cardSizeWidget;
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

View file

@ -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));
}
}
}

View file

@ -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