From d60b613fd5dcc7eefb2847e37d050a74ecd4b7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Sat, 1 Mar 2025 21:31:31 +0100 Subject: [PATCH] More performant display updating. --- .../tab_deck_editor_visual.cpp | 21 +-- .../tab_deck_editor_visual.h | 4 +- .../tab_deck_editor_visual_tab_widget.cpp | 20 +-- .../tab_deck_editor_visual_tab_widget.h | 6 +- .../cards/deck_card_zone_display_widget.cpp | 95 +++++++++-- .../cards/deck_card_zone_display_widget.h | 18 +- .../overlapped_card_group_display_widget.cpp | 104 +++++++++-- .../overlapped_card_group_display_widget.h | 19 ++- .../deck_analytics/mana_base_widget.cpp | 45 ++--- .../deck_analytics/mana_devotion_widget.cpp | 47 +++-- .../visual_deck_editor_widget.cpp | 161 +++++++----------- .../visual_deck_editor_widget.h | 25 +-- 12 files changed, 320 insertions(+), 245 deletions(-) diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index a78dc1fa0..555099d57 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -65,10 +65,8 @@ void TabDeckEditorVisual::createCentralFrame() &TabDeckEditorVisual::changeModelIndexAndCardInfo); connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChangedDatabaseDisplay, this, &AbstractTabDeckEditor::updateCard); - connect(tabContainer, &TabDeckEditorVisualTabWidget::mainboardCardClicked, this, + connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClicked, this, &TabDeckEditorVisual::processMainboardCardClick); - connect(tabContainer, &TabDeckEditorVisualTabWidget::sideboardCardClicked, this, - &TabDeckEditorVisual::processSideboardCardClick); connect(tabContainer, &TabDeckEditorVisualTabWidget::cardClickedDatabaseDisplay, this, &TabDeckEditorVisual::processCardClickDatabaseDisplay); @@ -161,10 +159,12 @@ void TabDeckEditorVisual::changeModelIndexToCard(const CardInfoPtr &activeCard) 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) { - actSwapCard(instance->getInfo(), DECK_ZONE_MAIN); + actSwapCard(instance->getInfo(), zoneName); } else if (event->button() == Qt::RightButton) { actDecrementCard(instance->getInfo()); } 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, CardInfoPictureWithTextOverlayWidget *instance) { diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h index eac30a23e..c8fb6625f 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual.h @@ -44,8 +44,8 @@ public: public slots: void onDeckChanged() override; void showPrintingSelector() override; - void processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); - void processSideboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); + void + processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName); void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); }; diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp index 25900dabf..586989822 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp @@ -20,13 +20,10 @@ TabDeckEditorVisualTabWidget::TabDeckEditorVisualTabWidget(QWidget *parent, visualDeckView = new VisualDeckEditorWidget(this, deckModel); visualDeckView->setObjectName("visualDeckView"); - visualDeckView->updateDisplay(); connect(visualDeckView, &VisualDeckEditorWidget::activeCardChanged, this, &TabDeckEditorVisualTabWidget::onCardChanged); - connect(visualDeckView, &VisualDeckEditorWidget::mainboardCardClicked, this, - &TabDeckEditorVisualTabWidget::onMainboardCardClickedDeckEditor); - connect(visualDeckView, &VisualDeckEditorWidget::sideboardCardClicked, this, - &TabDeckEditorVisualTabWidget::onSideboardCardClickedDeckEditor); + connect(visualDeckView, &VisualDeckEditorWidget::cardClicked, this, + &TabDeckEditorVisualTabWidget::onCardClickedDeckEditor); visualDatabaseDisplay = new VisualDatabaseDisplayWidget(this, deckEditor, cardDatabaseModel, cardDatabaseDisplayModel); @@ -57,16 +54,11 @@ void TabDeckEditorVisualTabWidget::onCardChangedDatabaseDisplay(CardInfoPtr acti emit cardChangedDatabaseDisplay(activeCard); } -void TabDeckEditorVisualTabWidget::onMainboardCardClickedDeckEditor(QMouseEvent *event, - CardInfoPictureWithTextOverlayWidget *instance) +void TabDeckEditorVisualTabWidget::onCardClickedDeckEditor(QMouseEvent *event, + CardInfoPictureWithTextOverlayWidget *instance, + QString zoneName) { - emit mainboardCardClicked(event, instance); -} - -void TabDeckEditorVisualTabWidget::onSideboardCardClickedDeckEditor(QMouseEvent *event, - CardInfoPictureWithTextOverlayWidget *instance) -{ - emit sideboardCardClicked(event, instance); + emit cardClicked(event, instance, zoneName); } void TabDeckEditorVisualTabWidget::onCardClickedDatabaseDisplay(QMouseEvent *event, diff --git a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h index d891e3639..be24aef27 100644 --- a/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h +++ b/cockatrice/src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.h @@ -39,15 +39,13 @@ public: public slots: void onCardChanged(CardInfoPtr activeCard); void onCardChangedDatabaseDisplay(CardInfoPtr activeCard); - void onMainboardCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); - void onSideboardCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); + void onCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName); void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); signals: void cardChanged(CardInfoPtr activeCard); void cardChangedDatabaseDisplay(CardInfoPtr activeCard); - void mainboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); - void sideboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); + void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName); void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); private: diff --git a/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp index 2fefba34b..827a1938f 100644 --- a/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp @@ -1,23 +1,33 @@ #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 DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent, - QList>> cardLists, - QString bannerText, + DeckListModel *_deckListModel, + QString _zoneName, + QString _activeSortCriteria, int bannerOpacity, 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); setLayout(layout); - banner = new BannerWidget(this, bannerText, Qt::Orientation::Vertical, bannerOpacity); + banner = new BannerWidget(this, zoneName, Qt::Orientation::Vertical, bannerOpacity); layout->addWidget(banner); + cardGroupContainer = new QWidget(this); + cardGroupLayout = new QVBoxLayout(cardGroupContainer); + cardGroupContainer->setLayout(cardGroupLayout); + layout->addWidget(cardGroupContainer); + displayCards(); + connect(deckListModel, &DeckListModel::dataChanged, this, &DeckCardZoneDisplayWidget::displayCards); } void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event) { @@ -31,7 +41,7 @@ void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event) } void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card) { - emit cardClicked(event, card); + emit cardClicked(event, card, zoneName); } void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card) { @@ -40,15 +50,70 @@ void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card) void DeckCardZoneDisplayWidget::displayCards() { - for (QPair> &pair : cardLists) { - QString criteriaValue = pair.first; - QList cardList = pair.second; + addCardGroupIfItDoesNotExist(); + deleteCardGroupIfItDoesNotExist(); +} - OverlappedCardGroupDisplayWidget *display_widget = - new OverlappedCardGroupDisplayWidget(this, &cardList, criteriaValue, subBannerOpacity); - connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this, - SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *))); - connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr))); - layout->addWidget(display_widget); +void DeckCardZoneDisplayWidget::addCardGroupIfItDoesNotExist() +{ + QList cardGroupsDisplayWidgets = + cardGroupContainer->findChildren(); + + QList 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); + } } -} \ No newline at end of file +} + +void DeckCardZoneDisplayWidget::deleteCardGroupIfItDoesNotExist() +{ + QList cardGroupsDisplayWidgets = + cardGroupLayout->findChildren(); + 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 DeckCardZoneDisplayWidget::getSortCriteriaValueList() +{ + QList sortCriteriaValues; + + QList *cardsInZone = deckListModel->getCardsAsCardInfoPtrsForZone(zoneName); + + for (CardInfoPtr cardInZone : *cardsInZone) { + sortCriteriaValues.append(cardInZone->getProperty(activeSortCriteria)); + } + + sortCriteriaValues.removeDuplicates(); + sortCriteriaValues.sort(); + return sortCriteriaValues; +} diff --git a/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h index bd117be26..09fe79df6 100644 --- a/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h +++ b/cockatrice/src/client/ui/widgets/cards/deck_card_zone_display_widget.h @@ -1,6 +1,7 @@ #ifndef 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 "../general/display/banner_widget.h" #include "../general/layout_containers/overlap_widget.h" @@ -15,10 +16,13 @@ class DeckCardZoneDisplayWidget : public QWidget public: DeckCardZoneDisplayWidget(QWidget *parent, - QList>> cardLists, - QString bannerText, + DeckListModel *deckListModel, + QString zoneName, + QString activeSortCriteria, int bannerOpacity, int subBannerOpacity); + DeckListModel *deckListModel; + QString zoneName; void addCardsToOverlapWidget(); void resizeEvent(QResizeEvent *event) override; @@ -26,15 +30,21 @@ public slots: void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void onHover(CardInfoPtr card); void displayCards(); + void addCardGroupIfItDoesNotExist(); + void deleteCardGroupIfItDoesNotExist(); + void onActiveSortCriteriaChanged(QString activeSortCriteria); + QList getSortCriteriaValueList(); signals: - void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); + void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card, QString zoneName); void cardHovered(CardInfoPtr card); private: - QList>> cardLists; + QString activeSortCriteria; QVBoxLayout *layout; BannerWidget *banner; + QWidget *cardGroupContainer; + QVBoxLayout *cardGroupLayout; int bannerOpacity = 20; int subBannerOpacity = 10; OverlapWidget *overlapWidget; diff --git a/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.cpp b/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.cpp index 934e19367..4b444581d 100644 --- a/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.cpp @@ -1,45 +1,119 @@ #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 OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent, - QList *cards, - QString bannerText, + DeckListModel *_deckListModel, + QString _zoneName, + QString _cardGroupCategory, + QString _activeSortCriteria, int bannerOpacity) - : QWidget(parent), cardsToDisplay(cards) + : QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), cardGroupCategory(_cardGroupCategory), + activeSortCriteria(_activeSortCriteria) { layout = new QVBoxLayout(this); setLayout(layout); 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); layout->addWidget(banner); 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++) { - CardInfoPtr card = cardsToDisplay->at(i); - if (card) { - CardInfoPictureWithTextOverlayWidget *display = - new CardInfoPictureWithTextOverlayWidget(overlapWidget, true); - display->setCard(card); + QList cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName)); + + QList existingWidgets = + overlapWidget->findChildren(); + + QHash requiredCounts; + for (const CardInfoPtr &card : cardsInZone) { + requiredCounts[card->getName()]++; + } + + QHash> 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 &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->adjustMaxColumnsAndRows(); } +QList OverlappedCardGroupDisplayWidget::getCardsMatchingGroup(QList *cardsToSort) +{ + QList 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 *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) { emit cardClicked(event, card); diff --git a/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.h b/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.h index 217fd4ea9..959ab856c 100644 --- a/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.h +++ b/cockatrice/src/client/ui/widgets/cards/overlapped_card_group_display_widget.h @@ -1,6 +1,7 @@ #ifndef 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 "../general/display/banner_widget.h" #include "../general/layout_containers/overlap_widget.h" @@ -15,20 +16,32 @@ class OverlappedCardGroupDisplayWidget : public QWidget Q_OBJECT public: - OverlappedCardGroupDisplayWidget(QWidget *parent, QList *cards, QString bannerText, int bannerOpacity); - void addCardsToOverlapWidget(); + OverlappedCardGroupDisplayWidget(QWidget *parent, + DeckListModel *deckListModel, + QString zoneName, + QString cardGroupCategory, + QString activeSortCriteria, + int bannerOpacity); + + QList getCardsMatchingGroup(QList *cardsToSort); void resizeEvent(QResizeEvent *event) override; + DeckListModel *deckListModel; + QString zoneName; + QString cardGroupCategory; + QString activeSortCriteria; + public slots: + void sortCardList(QList *cardsToSort, QStringList properties, Qt::SortOrder order); void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void onHover(CardInfoPtr card); + void updateCardDisplays(); signals: void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card); void cardHovered(CardInfoPtr card); private: - QList *cardsToDisplay; QVBoxLayout *layout; BannerWidget *banner; OverlapWidget *overlapWidget; diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp index 899ac7515..5fed47171 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_base_widget.cpp @@ -100,41 +100,26 @@ QHash ManaBaseWidget::analyzeManaBase() QHash ManaBaseWidget::determineManaProduction(const QString &rulesText) { - // Initialize mana counts - QHash 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); + QHash manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}}; - // Define regex patterns for different mana production rules - 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)"); + QString text = rulesText.toLower(); // Normalize case for matching - // Check for mana production rules in the text - if (tapAddColorless.match(rulesText).hasMatch()) { - manaCounts["C"] += 1; // Adds colorless mana + // Quick keyword-based checks for any color and colorless mana + if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) { + 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()) { - manaCounts["W"] += 1; // Assumes at least 1 of any color can be produced - manaCounts["U"] += 1; - 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); + // Optimized regex for specific mana symbols + static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})"); + QRegularExpressionMatch match = specificColorRegex.match(rulesText); if (match.hasMatch()) { - QString color = match.captured(1); - manaCounts[color] += 1; + manaCounts[match.captured(1)]++; } return manaCounts; diff --git a/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp b/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp index 0f0e59403..c21c0ccc6 100644 --- a/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp @@ -99,38 +99,29 @@ void ManaDevotionWidget::updateDisplay() // Function to count mana symbols W, U, B, R, G in the input string std::unordered_map ManaDevotionWidget::countManaSymbols(const QString &manaString) { - // Only track counts for W, U, B, R, G std::unordered_map manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}}; - // Convert QString to std::string for regex processing - std::string stdManaString = manaString.toStdString(); - 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) { + int len = manaString.length(); + for (int i = 0; i < len; ++i) { if (manaString[i] == '{') { - // Skip over the {X/Y} pattern - while (manaString[i] != '}' && i < manaString.size()) { - ++i; + ++i; // Move past '{' + if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) { + 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()]++; } } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index 205b2182e..2b3925ca4 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -19,8 +19,6 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel) : QWidget(parent), deckListModel(_deckListModel) { - this->mainDeckCards = new QList; - this->sideboardCards = new QList; connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged); // The Main Widget and Main Layout, which contain a single Widget: The Scroll Area @@ -36,7 +34,9 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_ << "cmc" << "name"; sortByComboBox->addItems(sortProperties); - connect(sortByComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(activeSortCriteriaChanged())); + connect(sortByComboBox, &QComboBox::currentIndexChanged, this, + &VisualDeckEditorWidget::actChangeActiveSortCriteria); + actChangeActiveSortCriteria(); scrollArea = new QScrollArea(); scrollArea->setWidgetResizable(true); @@ -52,13 +52,63 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_ scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter); 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(scrollArea); - mainLayout->addWidget(overlap_control_widget); + mainLayout->addWidget(overlapControlWidget); +} + +void VisualDeckEditorWidget::updateZoneWidgets() +{ + addZoneIfDoesNotExist(); + deleteZoneIfDoesNotExist(); +} + +void VisualDeckEditorWidget::addZoneIfDoesNotExist() +{ + QList cardZoneDisplayWidgets = + zoneContainer->findChildren(); + 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 cardZoneDisplayWidgets = + zoneContainer->findChildren(); + 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) @@ -67,11 +117,10 @@ void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event) zoneContainer->setMaximumWidth(scrollArea->viewport()->width()); } -void VisualDeckEditorWidget::activeSortCriteriaChanged() +void VisualDeckEditorWidget::actChangeActiveSortCriteria() { activeSortCriteria = sortByComboBox->currentText(); - qDebug() << "activeSortCriteria changed: " << activeSortCriteria; - updateDisplay(); + emit activeSortCriteriaChanged(activeSortCriteria); } void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight) @@ -81,69 +130,7 @@ void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelInde (void)bottomRight; // 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. - QTimer::singleShot(0, this, [this] { updateDisplay(); }); -} - -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>> VisualDeckEditorWidget::sortCards(QList *cardsToSort) -{ - QList>> sortedCardGroups; - QList 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; + updateZoneWidgets(); } /*void VisualDeckEditorWidget::sortCards() @@ -187,31 +174,9 @@ void VisualDeckEditorWidget::onHover(CardInfoPtr hoveredCard) emit activeCardChanged(hoveredCard); } -void VisualDeckEditorWidget::onMainboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance) +void VisualDeckEditorWidget::onCardClick(QMouseEvent *event, + CardInfoPictureWithTextOverlayWidget *instance, + QString zoneName) { - emit mainboardCardClicked(event, instance); -} - -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); + emit cardClicked(event, instance, zoneName); } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.h index 8669c2a3b..08c473340 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.h @@ -17,41 +17,34 @@ class VisualDeckEditorWidget : public QWidget public: explicit VisualDeckEditorWidget(QWidget *parent, DeckListModel *deckListModel); void resizeEvent(QResizeEvent *event) override; - void updateDisplay(); - QWidget *displayCards(QList>> cardLists, int bannerOpacity); - QList>> sortCards(QList *cardsToSort); - OverlapWidget *getSortedCards(); - void populateCards(); - void sortCardList(QStringList properties, Qt::SortOrder order); - void setDeckList(const DeckList &new_deck_list_model); + void setDeckList(const DeckList &_deckListModel); public slots: void decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight); + void updateZoneWidgets(); + void addZoneIfDoesNotExist(); + void deleteZoneIfDoesNotExist(); signals: void activeCardChanged(CardInfoPtr activeCard); - void mainboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); - void sideboardCardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); + void activeSortCriteriaChanged(QString activeSortCriteria); + void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName); protected slots: void onHover(CardInfoPtr hoveredCard); - void onMainboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); - void onSideboardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance); - void activeSortCriteriaChanged(); + void onCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName); + void actChangeActiveSortCriteria(); private: DeckListModel *deckListModel; - QList *mainDeckCards; - QList *sideboardCards; QVBoxLayout *mainLayout; QComboBox *sortByComboBox; QString activeSortCriteria = "maintype"; - FlowWidget *flowWidget; QScrollArea *scrollArea; QWidget *zoneContainer; QVBoxLayout *zoneContainerLayout; - OverlapControlWidget *overlap_control_widget; + OverlapControlWidget *overlapControlWidget; QWidget *container; };