diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview_color_identity_filter_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview_color_identity_filter_widget.cpp new file mode 100644 index 000000000..b3428cc08 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview_color_identity_filter_widget.cpp @@ -0,0 +1,190 @@ +#include "deck_preview_color_identity_filter_widget.h" + +#include "deck_preview_widget.h" + +#include +#include + +DeckPreviewColorIdentityFilterCircleWidget::DeckPreviewColorIdentityFilterCircleWidget(QChar color, QWidget *parent) + : QWidget(parent), colorChar(color), isActive(false), circleDiameter(50) +{ + setFixedSize(circleDiameter, circleDiameter); +} + +void DeckPreviewColorIdentityFilterCircleWidget::setColorActive(bool active) +{ + if (isActive != active) { + isActive = active; + update(); + } +} + +bool DeckPreviewColorIdentityFilterCircleWidget::isColorActive() const +{ + return isActive; +} + +QChar DeckPreviewColorIdentityFilterCircleWidget::getColorChar() const +{ + return colorChar; +} + +void DeckPreviewColorIdentityFilterCircleWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QColor circleColor; + switch (colorChar.unicode()) { + case 'W': + circleColor = Qt::white; + break; + case 'U': + circleColor = QColor(0, 115, 230); + break; + case 'B': + circleColor = QColor(50, 50, 50); + break; + case 'R': + circleColor = QColor(230, 30, 30); + break; + case 'G': + circleColor = QColor(30, 180, 30); + break; + default: + circleColor = Qt::transparent; + break; + } + + if (!isActive) { + circleColor.setAlpha(100); // Dim inactive circles + } + + painter.setBrush(circleColor); + painter.setPen(Qt::black); + painter.drawEllipse(rect()); + + if (isActive) { + QFont font = painter.font(); + font.setBold(true); + font.setPointSize(circleDiameter / 3); + painter.setFont(font); + painter.setPen(colorChar.unicode() == 'B' ? Qt::white : Qt::black); + painter.drawText(rect(), Qt::AlignCenter, colorChar); + } +} + +void DeckPreviewColorIdentityFilterCircleWidget::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + isActive = !isActive; + emit colorToggled(colorChar, isActive); + update(); +} + +DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent) + : QWidget(parent), layout(new QHBoxLayout(this)) +{ + setLayout(layout); + layout->setSpacing(5); + layout->setContentsMargins(0, 0, 0, 0); + + QString fullColorIdentity = "WUBRG"; + for (const QChar &color : fullColorIdentity) { + auto *circle = new DeckPreviewColorIdentityFilterCircleWidget(color, this); + layout->addWidget(circle); + + // Initialize the activeColors map + activeColors[color] = false; + + // Connect the color toggled signal + connect(circle, &DeckPreviewColorIdentityFilterCircleWidget::colorToggled, this, + &DeckPreviewColorIdentityFilterWidget::handleColorToggled); + } + toggleButton = new QPushButton("Mode: Includes", this); + toggleButton->setCheckable(true); // Enable checkable state + layout->addWidget(toggleButton); + + // Connect the button's toggled signal + connect(toggleButton, &QPushButton::toggled, this, &DeckPreviewColorIdentityFilterWidget::updateFilterMode); + connect(this, &DeckPreviewColorIdentityFilterWidget::activeColorsChanged, parent, + &VisualDeckStorageWidget::refreshBannerCards); + connect(this, &DeckPreviewColorIdentityFilterWidget::filterModeChanged, parent, + &VisualDeckStorageWidget::refreshBannerCards); +} + +void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar color, bool active) +{ + activeColors[color] = active; + emit activeColorsChanged(); +} + +void DeckPreviewColorIdentityFilterWidget::updateFilterMode(bool checked) +{ + exactMatchMode = checked; // Toggle between modes + toggleButton->setText(checked ? "Mode: Exact Match" : "Mode: Includes"); + emit filterModeChanged(exactMatchMode); +} + +QList DeckPreviewColorIdentityFilterWidget::filterWidgets(QList &widgets) +{ + QList filteredWidgets; + + // Check if no colors are active + bool noColorsActive = true; + for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) { + if (it.value()) { + noColorsActive = false; + break; + } + } + + // If no colors are active, return the unfiltered list of widgets + if (noColorsActive) { + return widgets; + } + + for (const auto &widget : widgets) { + QString colorIdentity = widget->getColorIdentity(); + + bool matchesFilter = true; + if (exactMatchMode) { + // Exact match mode: active colors must exactly match colorIdentity + + // Create a set of active colors + QSet activeColorSet; + for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) { + if (it.value()) { + activeColorSet.insert(it.key().toUpper()); // Use uppercase for uniformity + } + } + + // Create a set of colors from the color identity string + QSet colorIdentitySet; + for (const QChar &color : colorIdentity) { + colorIdentitySet.insert(color.toUpper()); // Ensure case uniformity + } + + // Compare the sets: the sets must match exactly + if (activeColorSet != colorIdentitySet) { + matchesFilter = false; + } + } else { + // Includes mode: colorIdentity must contain all active colors + for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) { + if (it.value() && !colorIdentity.contains(it.key())) { + matchesFilter = false; + break; + } + } + } + + if (matchesFilter) { + filteredWidgets << widget; + } + } + + return filteredWidgets; +} diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview_color_identity_filter_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview_color_identity_filter_widget.h new file mode 100644 index 000000000..d0868a571 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview_color_identity_filter_widget.h @@ -0,0 +1,61 @@ +#ifndef DECK_PREVIEW_COLOR_IDENTITY_FILTER_WIDGET_H +#define DECK_PREVIEW_COLOR_IDENTITY_FILTER_WIDGET_H + +#include "visual_deck_storage_widget.h" + +#include +#include +#include +#include +#include + +class DeckPreviewWidget; +class VisualDeckStorageWidget; + +class DeckPreviewColorIdentityFilterCircleWidget : public QWidget +{ + Q_OBJECT + +public: + explicit DeckPreviewColorIdentityFilterCircleWidget(QChar color, QWidget *parent = nullptr); + void setColorActive(bool active); + bool isColorActive() const; + QChar getColorChar() const; + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +signals: + void colorToggled(QChar color, bool active); + +private: + QChar colorChar; + bool isActive; + int circleDiameter; +}; + +class DeckPreviewColorIdentityFilterWidget : public QWidget +{ + Q_OBJECT + +public: + explicit DeckPreviewColorIdentityFilterWidget(VisualDeckStorageWidget *parent); + QList filterWidgets(QList &widgets); + +signals: + void filterModeChanged(bool exactMatchMode); + void activeColorsChanged(); + +private slots: + void handleColorToggled(QChar color, bool active); + void updateFilterMode(bool checked); + +private: + QHBoxLayout *layout; + QPushButton *toggleButton; + QMap activeColors; + bool exactMatchMode = false; // Default to "includes" mode +}; + +#endif // DECK_PREVIEW_COLOR_IDENTITY_FILTER_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp index 381f17a71..204f62919 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp @@ -22,10 +22,11 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare sortWidget = new VisualDeckStorageSortWidget(this); searchWidget = new VisualDeckStorageSearchWidget(this); + deckPreviewColorIdentityFilterWidget = new DeckPreviewColorIdentityFilterWidget(this); - // Add combo box to the main layout layout->addWidget(sortWidget); layout->addWidget(searchWidget); + layout->addWidget(deckPreviewColorIdentityFilterWidget); flowWidget = new FlowWidget(this, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); layout->addWidget(flowWidget); @@ -81,7 +82,9 @@ void VisualDeckStorageWidget::refreshBannerCards() allDecks.append(display); } - auto filteredFiles = searchWidget->filterFiles(sortWidget->filterFiles(allDecks), searchWidget->getSearchText()); + auto filteredByColorIdentity = + deckPreviewColorIdentityFilterWidget->filterWidgets(sortWidget->filterFiles(allDecks)); + auto filteredFiles = searchWidget->filterFiles(filteredByColorIdentity, searchWidget->getSearchText()); flowWidget->clearLayout(); // Clear existing widgets in the flow layout diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h index 5e993b6f3..cdf39cb07 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.h @@ -5,6 +5,7 @@ #include "../../../../deck/deck_view.h" #include "../../../ui/widgets/general/layout_containers/flow_widget.h" #include "../cards/card_size_widget.h" +#include "deck_preview_color_identity_filter_widget.h" #include "deck_preview_widget.h" #include "visual_deck_storage_search_widget.h" #include "visual_deck_storage_sort_widget.h" @@ -13,6 +14,7 @@ class VisualDeckStorageSearchWidget; class VisualDeckStorageSortWidget; +class DeckPreviewColorIdentityFilterWidget; class VisualDeckStorageWidget final : public QWidget { Q_OBJECT @@ -39,6 +41,7 @@ private: VisualDeckStorageSortWidget *sortWidget; VisualDeckStorageSearchWidget *searchWidget; + DeckPreviewColorIdentityFilterWidget *deckPreviewColorIdentityFilterWidget; CardSizeWidget *cardSizeWidget; };