More performant display updating.

This commit is contained in:
Lukas Brübach 2025-03-01 21:31:31 +01:00
parent 268d3f2e6b
commit d60b613fd5
12 changed files with 320 additions and 245 deletions

View file

@ -65,10 +65,8 @@ void TabDeckEditorVisual::createCentralFrame()
&TabDeckEditorVisual::changeModelIndexAndCardInfo); &TabDeckEditorVisual::changeModelIndexAndCardInfo);
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChangedDatabaseDisplay, this, connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChangedDatabaseDisplay, this,
&AbstractTabDeckEditor::updateCard); &AbstractTabDeckEditor::updateCard);
connect(tabContainer, &TabDeckEditorVisualTabWidget::mainboardCardClicked, this, connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClicked, this,
&TabDeckEditorVisual::processMainboardCardClick); &TabDeckEditorVisual::processMainboardCardClick);
connect(tabContainer, &TabDeckEditorVisualTabWidget::sideboardCardClicked, this,
&TabDeckEditorVisual::processSideboardCardClick);
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClickedDatabaseDisplay, this, connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClickedDatabaseDisplay, this,
&TabDeckEditorVisual::processCardClickDatabaseDisplay); &TabDeckEditorVisual::processCardClickDatabaseDisplay);
@ -161,10 +159,12 @@ void TabDeckEditorVisual::changeModelIndexToCard(const CardInfoPtr &activeCard)
deckDockWidget->deckView->setCurrentIndex(index); deckDockWidget->deckView->setCurrentIndex(index);
} }
void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance) void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance,
QString zoneName)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
actSwapCard(instance->getInfo(), DECK_ZONE_MAIN); actSwapCard(instance->getInfo(), zoneName);
} else if (event->button() == Qt::RightButton) { } else if (event->button() == Qt::RightButton) {
actDecrementCard(instance->getInfo()); actDecrementCard(instance->getInfo());
} else if (event->button() == Qt::MiddleButton) { } else if (event->button() == Qt::MiddleButton) {
@ -172,17 +172,6 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, CardInfo
} }
} }
void TabDeckEditorVisual::processSideboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
{
if (event->button() == Qt::LeftButton) {
actSwapCard(instance->getInfo(), DECK_ZONE_SIDE);
} else if (event->button() == Qt::RightButton) {
actDecrementCardFromSideboard(instance->getInfo());
} else if (event->button() == Qt::MiddleButton) {
deckDockWidget->actRemoveCard();
}
}
void TabDeckEditorVisual::processCardClickDatabaseDisplay(QMouseEvent *event, void TabDeckEditorVisual::processCardClickDatabaseDisplay(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance) CardInfoPictureWithTextOverlayWidget *instance)
{ {

View file

@ -44,8 +44,8 @@ public:
public slots: public slots:
void onDeckChanged() override; void onDeckChanged() override;
void showPrintingSelector() override; void showPrintingSelector() override;
void processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void
void processSideboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
}; };

View file

@ -20,13 +20,10 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent,
visualDeckView = new VisualDeckEditorWidget(this, deckModel); visualDeckView = new VisualDeckEditorWidget(this, deckModel);
visualDeckView->setObjectName("visualDeckView"); visualDeckView->setObjectName("visualDeckView");
visualDeckView->updateDisplay();
connect(visualDeckView, &VisualDeckEditorWidget::activeCardChanged, this, connect(visualDeckView, &VisualDeckEditorWidget::activeCardChanged, this,
&TabDeckEditorVisualTabWidget::onCardChanged); &TabDeckEditorVisualTabWidget::onCardChanged);
connect(visualDeckView, &VisualDeckEditorWidget::mainboardCardClicked, this, connect(visualDeckView, &VisualDeckEditorWidget::cardClicked, this,
&TabDeckEditorVisualTabWidget::onMainboardCardClickedDeckEditor); &TabDeckEditorVisualTabWidget::onCardClickedDeckEditor);
connect(visualDeckView, &VisualDeckEditorWidget::sideboardCardClicked, this,
&TabDeckEditorVisualTabWidget::onSideboardCardClickedDeckEditor);
visualDatabaseDisplay = visualDatabaseDisplay =
new VisualDatabaseDisplayWidget(this, deckEditor, cardDatabaseModel, cardDatabaseDisplayModel); new VisualDatabaseDisplayWidget(this, deckEditor, cardDatabaseModel, cardDatabaseDisplayModel);
@ -57,16 +54,11 @@ void TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay(CardInfoPtr acti
emit cardChangedDatabaseDisplay(activeCard); emit cardChangedDatabaseDisplay(activeCard);
} }
void TabDeckEditorVisualTabWidget::onMainboardCardClickedDeckEditor(QMouseEvent *event, void TabDeckEditorVisualTabWidget::onCardClickedDeckEditor(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance) CardInfoPictureWithTextOverlayWidget *instance,
QString zoneName)
{ {
emit mainboardCardClicked(event, instance); emit cardClicked(event, instance, zoneName);
}
void TabDeckEditorVisualTabWidget::onSideboardCardClickedDeckEditor(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance)
{
emit sideboardCardClicked(event, instance);
} }
void TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay(QMouseEvent *event, void TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay(QMouseEvent *event,

View file

@ -39,15 +39,13 @@ public:
public slots: public slots:
void onCardChanged(CardInfoPtr activeCard); void onCardChanged(CardInfoPtr activeCard);
void onCardChangedDatabaseDisplay(CardInfoPtr activeCard); void onCardChangedDatabaseDisplay(CardInfoPtr activeCard);
void onMainboardCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void onCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void onSideboardCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
signals: signals:
void cardChanged(CardInfoPtr activeCard); void cardChanged(CardInfoPtr activeCard);
void cardChangedDatabaseDisplay(CardInfoPtr activeCard); void cardChangedDatabaseDisplay(CardInfoPtr activeCard);
void mainboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void sideboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
private: private:

View file

@ -1,23 +1,33 @@
#include "deck_card_zone_display_widget.h" #include "deck_card_zone_display_widget.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../utility/card_info_comparator.h"
#include "overlapped_card_group_display_widget.h" #include "overlapped_card_group_display_widget.h"
#include <QResizeEvent> #include <QResizeEvent>
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent, DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
QList<QPair<QString, QList<CardInfoPtr>>> cardLists, DeckListModel *_deckListModel,
QString bannerText, QString _zoneName,
QString _activeSortCriteria,
int bannerOpacity, int bannerOpacity,
int subBannerOpacity) int subBannerOpacity)
: QWidget(parent), cardLists(cardLists), bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity) : QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), activeSortCriteria(_activeSortCriteria),
bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity)
{ {
layout = new QVBoxLayout(this); layout = new QVBoxLayout(this);
setLayout(layout); setLayout(layout);
banner = new BannerWidget(this, bannerText, Qt::Orientation::Vertical, bannerOpacity); banner = new BannerWidget(this, zoneName, Qt::Orientation::Vertical, bannerOpacity);
layout->addWidget(banner); layout->addWidget(banner);
cardGroupContainer = new QWidget(this);
cardGroupLayout = new QVBoxLayout(cardGroupContainer);
cardGroupContainer->setLayout(cardGroupLayout);
layout->addWidget(cardGroupContainer);
displayCards(); displayCards();
connect(deckListModel, &DeckListModel::dataChanged, this, &DeckCardZoneDisplayWidget::displayCards);
} }
void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event) void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
{ {
@ -31,7 +41,7 @@ void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
} }
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card) void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
{ {
emit cardClicked(event, card); emit cardClicked(event, card, zoneName);
} }
void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card) void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card)
{ {
@ -40,15 +50,70 @@ void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card)
void DeckCardZoneDisplayWidget::displayCards() void DeckCardZoneDisplayWidget::displayCards()
{ {
for (QPair<QString, QList<CardInfoPtr>> &pair : cardLists) { addCardGroupIfItDoesNotExist();
QString criteriaValue = pair.first; deleteCardGroupIfItDoesNotExist();
QList<CardInfoPtr> cardList = pair.second; }
OverlappedCardGroupDisplayWidget *display_widget = void DeckCardZoneDisplayWidget::addCardGroupIfItDoesNotExist()
new OverlappedCardGroupDisplayWidget(this, &cardList, criteriaValue, subBannerOpacity); {
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, QList<OverlappedCardGroupDisplayWidget *> cardGroupsDisplayWidgets =
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *))); cardGroupContainer->findChildren<OverlappedCardGroupDisplayWidget *>();
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
layout->addWidget(display_widget); QList<QString> cardGroups = getSortCriteriaValueList();
for (QString cardGroup : cardGroups) {
bool found = false;
for (OverlappedCardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) {
found = true;
}
}
if (!found) {
OverlappedCardGroupDisplayWidget *display_widget = new OverlappedCardGroupDisplayWidget(
this, deckListModel, zoneName, cardGroup, activeSortCriteria, subBannerOpacity);
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
cardGroupLayout->addWidget(display_widget);
}
} }
} }
void DeckCardZoneDisplayWidget::deleteCardGroupIfItDoesNotExist()
{
QList<OverlappedCardGroupDisplayWidget *> cardGroupsDisplayWidgets =
cardGroupLayout->findChildren<OverlappedCardGroupDisplayWidget *>();
for (OverlappedCardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
bool found = false;
for (QString cardGroup : getSortCriteriaValueList()) {
if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) {
found = true;
}
}
if (!found) {
cardGroupLayout->removeWidget(cardGroupDisplayWidget);
}
}
}
void DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged(QString _activeSortCriteria)
{
activeSortCriteria = _activeSortCriteria;
}
QList<QString> DeckCardZoneDisplayWidget::getSortCriteriaValueList()
{
QList<QString> sortCriteriaValues;
QList<CardInfoPtr> *cardsInZone = deckListModel->getCardsAsCardInfoPtrsForZone(zoneName);
for (CardInfoPtr cardInZone : *cardsInZone) {
sortCriteriaValues.append(cardInZone->getProperty(activeSortCriteria));
}
sortCriteriaValues.removeDuplicates();
sortCriteriaValues.sort();
return sortCriteriaValues;
}

View file

@ -1,6 +1,7 @@
#ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H #ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H
#define DECK_CARD_ZONE_DISPLAY_WIDGET_H #define DECK_CARD_ZONE_DISPLAY_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database.h"
#include "../general/display/banner_widget.h" #include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h" #include "../general/layout_containers/overlap_widget.h"
@ -15,10 +16,13 @@ class DeckCardZoneDisplayWidget : public QWidget
public: public:
DeckCardZoneDisplayWidget(QWidget *parent, DeckCardZoneDisplayWidget(QWidget *parent,
QList<QPair<QString, QList<CardInfoPtr>>> cardLists, DeckListModel *deckListModel,
QString bannerText, QString zoneName,
QString activeSortCriteria,
int bannerOpacity, int bannerOpacity,
int subBannerOpacity); int subBannerOpacity);
DeckListModel *deckListModel;
QString zoneName;
void addCardsToOverlapWidget(); void addCardsToOverlapWidget();
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
@ -26,15 +30,21 @@ public slots:
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(CardInfoPtr card); void onHover(CardInfoPtr card);
void displayCards(); void displayCards();
void addCardGroupIfItDoesNotExist();
void deleteCardGroupIfItDoesNotExist();
void onActiveSortCriteriaChanged(QString activeSortCriteria);
QList<QString> getSortCriteriaValueList();
signals: signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card, QString zoneName);
void cardHovered(CardInfoPtr card); void cardHovered(CardInfoPtr card);
private: private:
QList<QPair<QString, QList<CardInfoPtr>>> cardLists; QString activeSortCriteria;
QVBoxLayout *layout; QVBoxLayout *layout;
BannerWidget *banner; BannerWidget *banner;
QWidget *cardGroupContainer;
QVBoxLayout *cardGroupLayout;
int bannerOpacity = 20; int bannerOpacity = 20;
int subBannerOpacity = 10; int subBannerOpacity = 10;
OverlapWidget *overlapWidget; OverlapWidget *overlapWidget;

View file

@ -1,45 +1,119 @@
#include "overlapped_card_group_display_widget.h" #include "overlapped_card_group_display_widget.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../game/cards/card_database_manager.h"
#include "../../../../utility/card_info_comparator.h"
#include "card_info_picture_with_text_overlay_widget.h" #include "card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent> #include <QResizeEvent>
OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent, OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
QList<CardInfoPtr> *cards, DeckListModel *_deckListModel,
QString bannerText, QString _zoneName,
QString _cardGroupCategory,
QString _activeSortCriteria,
int bannerOpacity) int bannerOpacity)
: QWidget(parent), cardsToDisplay(cards) : QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), cardGroupCategory(_cardGroupCategory),
activeSortCriteria(_activeSortCriteria)
{ {
layout = new QVBoxLayout(this); layout = new QVBoxLayout(this);
setLayout(layout); setLayout(layout);
setMinimumSize(QSize(0, 0)); setMinimumSize(QSize(0, 0));
banner = new BannerWidget(this, bannerText, Qt::Orientation::Vertical, bannerOpacity); banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity);
overlapWidget = new OverlapWidget(this, 80, 1, 1, Qt::Vertical, true); overlapWidget = new OverlapWidget(this, 80, 1, 1, Qt::Vertical, true);
layout->addWidget(banner); layout->addWidget(banner);
layout->addWidget(overlapWidget); layout->addWidget(overlapWidget);
addCardsToOverlapWidget(); updateCardDisplays();
connect(deckListModel, &DeckListModel::dataChanged, this, &OverlappedCardGroupDisplayWidget::updateCardDisplays);
} }
void OverlappedCardGroupDisplayWidget::addCardsToOverlapWidget() void OverlappedCardGroupDisplayWidget::updateCardDisplays()
{ {
for (int i = 0; i < cardsToDisplay->size(); i++) { QList<CardInfoPtr> cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
CardInfoPtr card = cardsToDisplay->at(i);
if (card) { QList<CardInfoPictureWithTextOverlayWidget *> existingWidgets =
CardInfoPictureWithTextOverlayWidget *display = overlapWidget->findChildren<CardInfoPictureWithTextOverlayWidget *>();
new CardInfoPictureWithTextOverlayWidget(overlapWidget, true);
display->setCard(card); QHash<QString, int> requiredCounts;
for (const CardInfoPtr &card : cardsInZone) {
requiredCounts[card->getName()]++;
}
QHash<QString, QList<CardInfoPictureWithTextOverlayWidget *>> widgetMap;
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
widgetMap[widget->getInfo()->getName()].append(widget);
}
for (auto it = widgetMap.begin(); it != widgetMap.end(); ++it) {
QString name = it.key();
QList<CardInfoPictureWithTextOverlayWidget *> &widgets = it.value();
int neededCount = requiredCounts.value(name, 0);
while (widgets.size() > neededCount) {
CardInfoPictureWithTextOverlayWidget *widgetToRemove = widgets.takeLast();
overlapWidget->removeWidget(widgetToRemove);
delete widgetToRemove;
}
}
for (auto it = requiredCounts.begin(); it != requiredCounts.end(); ++it) {
QString name = it.key();
int missingCount = it.value() - widgetMap[name].size();
for (int i = 0; i < missingCount; ++i) {
auto *display = new CardInfoPictureWithTextOverlayWidget(overlapWidget, true);
auto cardIt = std::find_if(cardsInZone.begin(), cardsInZone.end(),
[&](const CardInfoPtr &c) { return c->getName() == name; });
if (cardIt != cardsInZone.end()) {
display->setCard(*cardIt);
}
connect(display, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
&OverlappedCardGroupDisplayWidget::onClick);
connect(display, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
&OverlappedCardGroupDisplayWidget::onHover);
connect(display, SIGNAL(imageClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
connect(display, SIGNAL(hoveredOnCard(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
overlapWidget->addWidget(display); overlapWidget->addWidget(display);
} }
} }
overlapWidget->adjustMaxColumnsAndRows(); overlapWidget->adjustMaxColumnsAndRows();
} }
QList<CardInfoPtr> OverlappedCardGroupDisplayWidget::getCardsMatchingGroup(QList<CardInfoPtr> *cardsToSort)
{
QList<CardInfoPtr> activeList;
QStringList sortCriteria;
sortCriteria.append(activeSortCriteria);
sortCardList(cardsToSort, sortCriteria, Qt::SortOrder::AscendingOrder);
for (int i = 0; i < cardsToSort->size(); ++i) {
CardInfoPtr info = cardsToSort->at(i);
if (info) {
if (info->getProperty(activeSortCriteria) == cardGroupCategory) {
activeList.append(info);
}
} else {
qDebug() << "Card not found in database!";
}
}
return activeList;
}
void OverlappedCardGroupDisplayWidget::sortCardList(QList<CardInfoPtr> *cardsToSort,
const QStringList properties,
Qt::SortOrder order = Qt::AscendingOrder)
{
CardInfoComparator comparator(properties, order);
std::sort(cardsToSort->begin(), cardsToSort->end(), comparator);
}
void OverlappedCardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card) void OverlappedCardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
{ {
emit cardClicked(event, card); emit cardClicked(event, card);

View file

@ -1,6 +1,7 @@
#ifndef OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H #ifndef OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
#define OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H #define OVERLAPPED_CARD_GROUP_DISPLAY_WIDGET_H
#include "../../../../deck/deck_list_model.h"
#include "../../../../game/cards/card_database.h" #include "../../../../game/cards/card_database.h"
#include "../general/display/banner_widget.h" #include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h" #include "../general/layout_containers/overlap_widget.h"
@ -15,20 +16,32 @@ class OverlappedCardGroupDisplayWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
OverlappedCardGroupDisplayWidget(QWidget *parent, QList<CardInfoPtr> *cards, QString bannerText, int bannerOpacity); OverlappedCardGroupDisplayWidget(QWidget *parent,
void addCardsToOverlapWidget(); DeckListModel *deckListModel,
QString zoneName,
QString cardGroupCategory,
QString activeSortCriteria,
int bannerOpacity);
QList<CardInfoPtr> getCardsMatchingGroup(QList<CardInfoPtr> *cardsToSort);
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
DeckListModel *deckListModel;
QString zoneName;
QString cardGroupCategory;
QString activeSortCriteria;
public slots: public slots:
void sortCardList(QList<CardInfoPtr> *cardsToSort, QStringList properties, Qt::SortOrder order);
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void onHover(CardInfoPtr card); void onHover(CardInfoPtr card);
void updateCardDisplays();
signals: signals:
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
void cardHovered(CardInfoPtr card); void cardHovered(CardInfoPtr card);
private: private:
QList<CardInfoPtr> *cardsToDisplay;
QVBoxLayout *layout; QVBoxLayout *layout;
BannerWidget *banner; BannerWidget *banner;
OverlapWidget *overlapWidget; OverlapWidget *overlapWidget;

View file

@ -100,41 +100,26 @@ QHash<QString, int> ManaBaseWidget::analyzeManaBase()
QHash<QString, int> ManaBaseWidget::determineManaProduction(const QString &rulesText) QHash<QString, int> ManaBaseWidget::determineManaProduction(const QString &rulesText)
{ {
// Initialize mana counts QHash<QString, int> manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}};
QHash<QString, int> manaCounts;
manaCounts.insert("W", 0);
manaCounts.insert("U", 0);
manaCounts.insert("B", 0);
manaCounts.insert("R", 0);
manaCounts.insert("G", 0);
manaCounts.insert("C", 0);
// Define regex patterns for different mana production rules QString text = rulesText.toLower(); // Normalize case for matching
QRegularExpression tapAddColorless(R"(\{T\}:\s*Add\s*\{C\}|Add\s*one\s*colorless\s*mana)");
QRegularExpression tapAddAnyColor(
R"(\{T\}:\s*Add\s*one\s*mana\s*of\s*any\s*color|Add\s*one\s*mana\s*of\s*any\s*color)");
QRegularExpression tapAddSpecificColor(
R"(\{T\}:\s*Add\s*\{(W|U|B|R|G)\}|Add\s*one\s*(white|blue|black|red|green)\s*mana)");
// Check for mana production rules in the text // Quick keyword-based checks for any color and colorless mana
if (tapAddColorless.match(rulesText).hasMatch()) { if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) {
manaCounts["C"] += 1; // Adds colorless mana for (const auto &color : {QStringLiteral("W"), QStringLiteral("U"), QStringLiteral("B"), QStringLiteral("R"),
QStringLiteral("G")}) {
manaCounts[color]++;
}
}
if (text.contains("{t}: add {c}") || text.contains("add one colorless mana")) {
manaCounts["C"]++;
} }
if (tapAddAnyColor.match(rulesText).hasMatch()) { // Optimized regex for specific mana symbols
manaCounts["W"] += 1; // Assumes at least 1 of any color can be produced static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})");
manaCounts["U"] += 1; QRegularExpressionMatch match = specificColorRegex.match(rulesText);
manaCounts["B"] += 1;
manaCounts["R"] += 1;
manaCounts["G"] += 1;
}
// Check for specific color production
QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{(W|U|B|R|G)\})");
auto match = specificColorRegex.match(rulesText);
if (match.hasMatch()) { if (match.hasMatch()) {
QString color = match.captured(1); manaCounts[match.captured(1)]++;
manaCounts[color] += 1;
} }
return manaCounts; return manaCounts;

View file

@ -99,38 +99,29 @@ void ManaDevotionWidget::updateDisplay()
// Function to count mana symbols W, U, B, R, G in the input string // Function to count mana symbols W, U, B, R, G in the input string
std::unordered_map<char, int> ManaDevotionWidget::countManaSymbols(const QString &manaString) std::unordered_map<char, int> ManaDevotionWidget::countManaSymbols(const QString &manaString)
{ {
// Only track counts for W, U, B, R, G
std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}}; std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}};
// Convert QString to std::string for regex processing int len = manaString.length();
std::string stdManaString = manaString.toStdString(); for (int i = 0; i < len; ++i) {
std::regex regexPattern(R"(\{([WUBRG])\/([WUBRG])\})");
std::string::const_iterator searchStart(stdManaString.cbegin());
std::smatch match;
// Go through the string and find pairs within {}/ for W, U, B, R, G
while (std::regex_search(searchStart, stdManaString.cend(), match, regexPattern)) {
char mana1 = match[1].str()[0];
char mana2 = match[2].str()[0];
// Increment the count for both mana symbols
manaCounts[mana1]++;
manaCounts[mana2]++;
// Move past the current match
searchStart = match.suffix().first;
}
// Now go through the string again but ignore the {}/ parts and non-WUBRG symbols
for (int i = 0; i < manaString.size(); ++i) {
if (manaString[i] == '{') { if (manaString[i] == '{') {
// Skip over the {X/Y} pattern ++i; // Move past '{'
while (manaString[i] != '}' && i < manaString.size()) { if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
++i; char mana1 = manaString[i].toLatin1();
++i; // Move to next character
if (i < len && manaString[i] == '/') {
++i; // Move past '/'
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
char mana2 = manaString[i].toLatin1();
manaCounts[mana1]++;
manaCounts[mana2]++;
}
} else {
manaCounts[mana1]++;
}
}
while (i < len && manaString[i] != '}') {
++i; // Skip to closing '}'
} }
} else if (manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
// If it's one of W, U, B, R, G, count it
manaCounts[manaString[i].toLatin1()]++;
} }
} }

View file

@ -19,8 +19,6 @@
VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel) VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel) : QWidget(parent), deckListModel(_deckListModel)
{ {
this->mainDeckCards = new QList<CardInfoPtr>;
this->sideboardCards = new QList<CardInfoPtr>;
connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged); connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area // The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
@ -36,7 +34,9 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_
<< "cmc" << "cmc"
<< "name"; << "name";
sortByComboBox->addItems(sortProperties); sortByComboBox->addItems(sortProperties);
connect(sortByComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(activeSortCriteriaChanged())); connect(sortByComboBox, &QComboBox::currentIndexChanged, this,
&VisualDeckEditorWidget::actChangeActiveSortCriteria);
actChangeActiveSortCriteria();
scrollArea = new QScrollArea(); scrollArea = new QScrollArea();
scrollArea->setWidgetResizable(true); scrollArea->setWidgetResizable(true);
@ -52,13 +52,63 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_
scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter); scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter);
scrollArea->setWidget(zoneContainer); scrollArea->setWidget(zoneContainer);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); updateZoneWidgets();
overlap_control_widget = new OverlapControlWidget(80, 1, 1, Qt::Vertical, this); overlapControlWidget = new OverlapControlWidget(80, 1, 1, Qt::Vertical, this);
mainLayout->addWidget(sortByComboBox); mainLayout->addWidget(sortByComboBox);
mainLayout->addWidget(scrollArea); mainLayout->addWidget(scrollArea);
mainLayout->addWidget(overlap_control_widget); mainLayout->addWidget(overlapControlWidget);
}
void VisualDeckEditorWidget::updateZoneWidgets()
{
addZoneIfDoesNotExist();
deleteZoneIfDoesNotExist();
}
void VisualDeckEditorWidget::addZoneIfDoesNotExist()
{
QList<DeckCardZoneDisplayWidget *> cardZoneDisplayWidgets =
zoneContainer->findChildren<DeckCardZoneDisplayWidget *>();
for (const QString &zone : *deckListModel->getZones()) {
bool found = false;
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
if (displayWidget->zoneName == zone) {
found = true;
}
}
if (!found) {
DeckCardZoneDisplayWidget *zoneDisplayWidget =
new DeckCardZoneDisplayWidget(zoneContainer, deckListModel, zone, activeSortCriteria, 20, 10);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this,
&VisualDeckEditorWidget::onCardClick);
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
zoneContainerLayout->addWidget(zoneDisplayWidget);
}
}
}
void VisualDeckEditorWidget::deleteZoneIfDoesNotExist()
{
QList<DeckCardZoneDisplayWidget *> cardZoneDisplayWidgets =
zoneContainer->findChildren<DeckCardZoneDisplayWidget *>();
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
bool found = false;
for (const QString &zone : *deckListModel->getZones()) {
if (displayWidget->zoneName == zone) {
found = true;
}
}
if (!found) {
zoneContainerLayout->removeWidget(displayWidget);
}
}
} }
void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event) void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event)
@ -67,11 +117,10 @@ void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event)
zoneContainer->setMaximumWidth(scrollArea->viewport()->width()); zoneContainer->setMaximumWidth(scrollArea->viewport()->width());
} }
void VisualDeckEditorWidget::activeSortCriteriaChanged() void VisualDeckEditorWidget::actChangeActiveSortCriteria()
{ {
activeSortCriteria = sortByComboBox->currentText(); activeSortCriteria = sortByComboBox->currentText();
qDebug() << "activeSortCriteria changed: " << activeSortCriteria; emit activeSortCriteriaChanged(activeSortCriteria);
updateDisplay();
} }
void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight) void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight)
@ -81,69 +130,7 @@ void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelInde
(void)bottomRight; (void)bottomRight;
// Necessary to delay this in this manner else the updateDisplay will nuke widgets while their onClick event // Necessary to delay this in this manner else the updateDisplay will nuke widgets while their onClick event
// hasn't returned yet. Interval of 0 means QT will schedule this after the current event loop has finished. // hasn't returned yet. Interval of 0 means QT will schedule this after the current event loop has finished.
QTimer::singleShot(0, this, [this] { updateDisplay(); }); updateZoneWidgets();
}
void VisualDeckEditorWidget::updateDisplay()
{
// Clear the layout first
populateCards();
zoneContainer = new QWidget(scrollArea);
zoneContainer->setMaximumWidth(scrollArea->viewport()->width());
zoneContainerLayout = new QVBoxLayout(zoneContainer);
DeckCardZoneDisplayWidget *mainBoardWidget =
new DeckCardZoneDisplayWidget(zoneContainer, sortCards(mainDeckCards), tr("Mainboard"), 20, 10);
connect(mainBoardWidget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(mainBoardWidget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onMainboardClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
DeckCardZoneDisplayWidget *sideBoardWidget =
new DeckCardZoneDisplayWidget(zoneContainer, sortCards(sideboardCards), tr("Sideboard"), 40, 30);
connect(sideBoardWidget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
connect(sideBoardWidget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
SLOT(onSideboardClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
zoneContainerLayout->addWidget(mainBoardWidget);
zoneContainerLayout->addWidget(sideBoardWidget);
scrollArea->setWidget(zoneContainer);
update();
}
QList<QPair<QString, QList<CardInfoPtr>>> VisualDeckEditorWidget::sortCards(QList<CardInfoPtr> *cardsToSort)
{
QList<QPair<QString, QList<CardInfoPtr>>> sortedCardGroups;
QList<CardInfoPtr> activeList;
QStringList sortCriteria;
sortCriteria.append(activeSortCriteria);
sortCardList(sortCriteria, Qt::SortOrder::AscendingOrder);
QString lastSortCriteriaValue = cardsToSort->isEmpty() ? "" : cardsToSort->at(0)->getProperty(activeSortCriteria);
for (int i = 0; i < cardsToSort->size(); ++i) {
CardInfoPtr info = cardsToSort->at(i);
if (info) {
QString currentCriteriaValue = info->getProperty(activeSortCriteria);
if (currentCriteriaValue != lastSortCriteriaValue) {
// Save the current tuple (criteriaValue, list) and start a new list
sortedCardGroups.append(qMakePair(lastSortCriteriaValue, activeList));
activeList.clear();
lastSortCriteriaValue = currentCriteriaValue;
}
activeList.append(info);
} else {
qDebug() << "Card not found in database!";
}
}
// Add the last tuple to sortedCardGroups
if (!activeList.isEmpty()) {
sortedCardGroups.append(qMakePair(lastSortCriteriaValue, activeList));
}
return sortedCardGroups;
} }
/*void VisualDeckEditorWidget::sortCards() /*void VisualDeckEditorWidget::sortCards()
@ -187,31 +174,9 @@ void VisualDeckEditorWidget::onHover(CardInfoPtr hoveredCard)
emit activeCardChanged(hoveredCard); emit activeCardChanged(hoveredCard);
} }
void VisualDeckEditorWidget::onMainboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance) void VisualDeckEditorWidget::onCardClick(QMouseEvent *event,
CardInfoPictureWithTextOverlayWidget *instance,
QString zoneName)
{ {
emit mainboardCardClicked(event, instance); emit cardClicked(event, instance, zoneName);
}
void VisualDeckEditorWidget::onSideboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance)
{
emit sideboardCardClicked(event, instance);
}
void VisualDeckEditorWidget::populateCards()
{
mainDeckCards->clear();
sideboardCards->clear();
if (!deckListModel)
return;
mainDeckCards = deckListModel->getCardsAsCardInfoPtrsForZone(DECK_ZONE_MAIN);
sideboardCards = deckListModel->getCardsAsCardInfoPtrsForZone(DECK_ZONE_SIDE);
}
void VisualDeckEditorWidget::sortCardList(const QStringList properties, Qt::SortOrder order = Qt::AscendingOrder)
{
CardInfoComparator comparator(properties, order);
std::sort(mainDeckCards->begin(), mainDeckCards->end(), comparator);
std::sort(sideboardCards->begin(), sideboardCards->end(), comparator);
} }

View file

@ -17,41 +17,34 @@ class VisualDeckEditorWidget : public QWidget
public: public:
explicit VisualDeckEditorWidget(QWidget *parent, DeckListModel *deckListModel); explicit VisualDeckEditorWidget(QWidget *parent, DeckListModel *deckListModel);
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
void updateDisplay();
QWidget *displayCards(QList<QPair<QString, QList<CardInfoPtr>>> cardLists, int bannerOpacity);
QList<QPair<QString, QList<CardInfoPtr>>> sortCards(QList<CardInfoPtr> *cardsToSort);
OverlapWidget *getSortedCards(); void setDeckList(const DeckList &_deckListModel);
void populateCards();
void sortCardList(QStringList properties, Qt::SortOrder order);
void setDeckList(const DeckList &new_deck_list_model);
public slots: public slots:
void decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight); void decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight);
void updateZoneWidgets();
void addZoneIfDoesNotExist();
void deleteZoneIfDoesNotExist();
signals: signals:
void activeCardChanged(CardInfoPtr activeCard); void activeCardChanged(CardInfoPtr activeCard);
void mainboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void activeSortCriteriaChanged(QString activeSortCriteria);
void sideboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
protected slots: protected slots:
void onHover(CardInfoPtr hoveredCard); void onHover(CardInfoPtr hoveredCard);
void onMainboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void onCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
void onSideboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); void actChangeActiveSortCriteria();
void activeSortCriteriaChanged();
private: private:
DeckListModel *deckListModel; DeckListModel *deckListModel;
QList<CardInfoPtr> *mainDeckCards;
QList<CardInfoPtr> *sideboardCards;
QVBoxLayout *mainLayout; QVBoxLayout *mainLayout;
QComboBox *sortByComboBox; QComboBox *sortByComboBox;
QString activeSortCriteria = "maintype"; QString activeSortCriteria = "maintype";
FlowWidget *flowWidget;
QScrollArea *scrollArea; QScrollArea *scrollArea;
QWidget *zoneContainer; QWidget *zoneContainer;
QVBoxLayout *zoneContainerLayout; QVBoxLayout *zoneContainerLayout;
OverlapControlWidget *overlap_control_widget; OverlapControlWidget *overlapControlWidget;
QWidget *container; QWidget *container;
}; };