diff --git a/cockatrice/src/game/selection_subtype_tally.cpp b/cockatrice/src/game/selection_subtype_tally.cpp index c033d42c9..afc5bc382 100644 --- a/cockatrice/src/game/selection_subtype_tally.cpp +++ b/cockatrice/src/game/selection_subtype_tally.cpp @@ -8,8 +8,10 @@ namespace { +/** @brief Extracts subtypes from a single card face's type line. */ QStringList extractSubtypesFromFace(const QString &faceType) { + // Card type format: "Creature — Goblin Warrior" or "Legendary Enchantment — Saga" QStringList parts = faceType.split(QStringLiteral(" — ")); if (parts.size() > 1) { return parts[1].split(QStringLiteral(" "), Qt::SkipEmptyParts); @@ -22,68 +24,41 @@ QStringList extractSubtypesFromFace(const QString &faceType) namespace SelectionSubtypeTally { -QList countSubtypes(const QList &cards) +QList countSubtypes(const QList &cards) { - QMap> subtypesByMainType; - QMap cardCountPerMainType; + QMap subtypeCounts; for (CardItem *card : cards) { if (card->getFaceDown() || card->getCard().isEmpty()) { continue; } - QString mainType = card->getCardInfo().getMainCardType(); - if (mainType.isEmpty()) { - mainType = QStringLiteral("Other"); - } - QString cardType = card->getCardInfo().getCardType(); + // Handle double-faced cards: "Creature — Human // Creature — Werewolf" QStringList cardFaces = cardType.split(QStringLiteral(" // ")); - bool contributedSubtypes = false; for (const QString &face : cardFaces) { QStringList subtypes = extractSubtypesFromFace(face); for (const QString &subtype : subtypes) { - subtypesByMainType[mainType][subtype]++; - contributedSubtypes = true; + subtypeCounts[subtype]++; } } - - if (contributedSubtypes) { - cardCountPerMainType[mainType]++; - } } - QList groups; - for (auto it = subtypesByMainType.constBegin(); it != subtypesByMainType.constEnd(); ++it) { - MainTypeGroup group; - group.mainType = it.key(); - group.cardCount = cardCountPerMainType.value(it.key(), 0); - - for (auto subIt = it.value().constBegin(); subIt != it.value().constEnd(); ++subIt) { - group.subtypes.append({subIt.key(), subIt.value()}); - } - - // Sort subtypes: by count ascending, then alphabetically - std::sort(group.subtypes.begin(), group.subtypes.end(), [](const SubtypeEntry &a, const SubtypeEntry &b) { - if (a.count != b.count) { - return a.count < b.count; - } - return a.name < b.name; - }); - - groups.append(group); + QList entries; + for (auto it = subtypeCounts.constBegin(); it != subtypeCounts.constEnd(); ++it) { + entries.append({it.key(), it.value()}); } - // Sort groups: by card count ascending, then alphabetically by main type - std::sort(groups.begin(), groups.end(), [](const MainTypeGroup &a, const MainTypeGroup &b) { - if (a.cardCount != b.cardCount) { - return a.cardCount < b.cardCount; + // Sort by count ascending, then alphabetically (lowest counts at bottom of display) + std::sort(entries.begin(), entries.end(), [](const SubtypeEntry &a, const SubtypeEntry &b) { + if (a.count != b.count) { + return a.count < b.count; } - return a.mainType < b.mainType; + return a.name < b.name; }); - return groups; + return entries; } } // namespace SelectionSubtypeTally diff --git a/cockatrice/src/game/selection_subtype_tally.h b/cockatrice/src/game/selection_subtype_tally.h index a5014fafe..9038653f6 100644 --- a/cockatrice/src/game/selection_subtype_tally.h +++ b/cockatrice/src/game/selection_subtype_tally.h @@ -3,26 +3,34 @@ #include #include -#include class CardItem; +/** @brief A single subtype (e.g., "Goblin", "Warrior") with its occurrence count. */ struct SubtypeEntry { - QString name; - int count; -}; - -struct MainTypeGroup -{ - QString mainType; - int cardCount; - QList subtypes; + QString name; ///< The subtype name + int count; ///< Number of selected cards with this subtype + + bool operator==(const SubtypeEntry &other) const + { + return name == other.name && count == other.count; + } }; +/** + * @brief Extracts and tallies subtypes from selected cards. + */ namespace SelectionSubtypeTally { -QList countSubtypes(const QList &cards); +/** + * @brief Parses card type lines and counts each subtype occurrence. + * + * Skips face-down cards and cards without type info. + * @param cards The list of selected card items to analyze. + * @return Entries sorted by count ascending, then alphabetically. + */ +QList countSubtypes(const QList &cards); } // namespace SelectionSubtypeTally #endif diff --git a/cockatrice/src/game_graphics/game_view.cpp b/cockatrice/src/game_graphics/game_view.cpp index 52349e14c..4a47b7aed 100644 --- a/cockatrice/src/game_graphics/game_view.cpp +++ b/cockatrice/src/game_graphics/game_view.cpp @@ -89,10 +89,8 @@ void GameView::resizeEvent(QResizeEvent *event) { QGraphicsView::resizeEvent(event); - GameScene *s = dynamic_cast(scene()); - if (s) { - s->processViewSizeChange(event->size()); - } + GameScene *s = static_cast(scene()); + s->processViewSizeChange(event->size()); updateSceneRect(scene()->sceneRect()); updateSelectionCount(event->size()); @@ -249,33 +247,28 @@ void GameView::updateSelectionCount(const QSize &viewSize) if (!SettingsCache::instance().getShowSubtypeSelectionTally() || count <= 1) { subtypeCountContainer->hide(); + cachedSubtypeEntries.clear(); return; } - GameScene *gameScene = dynamic_cast(scene()); - if (!gameScene) { - subtypeCountContainer->hide(); - return; - } - - QList groups = SelectionSubtypeTally::countSubtypes(gameScene->selectedCards()); - if (groups.isEmpty()) { - subtypeCountContainer->hide(); - return; - } - - QList entries; - for (const MainTypeGroup &group : groups) { - entries.append(group.subtypes); - } + GameScene *gameScene = static_cast(scene()); + QList entries = SelectionSubtypeTally::countSubtypes(gameScene->selectedCards()); if (entries.isEmpty()) { subtypeCountContainer->hide(); + cachedSubtypeEntries.clear(); return; } - QSize containerSize = rebuildSubtypeLabels(entries); - subtypeCountContainer->resize(containerSize); + // Only rebuild labels if entries changed + QSize containerSize; + if (entries != cachedSubtypeEntries) { + cachedSubtypeEntries = entries; + containerSize = rebuildSubtypeLabels(entries); + subtypeCountContainer->resize(containerSize); + } else { + containerSize = subtypeCountContainer->size(); + } int x = availableWidth - containerSize.width() - kMarginInPixels; int y; diff --git a/cockatrice/src/game_graphics/game_view.h b/cockatrice/src/game_graphics/game_view.h index 5fe0562cb..a44f15808 100644 --- a/cockatrice/src/game_graphics/game_view.h +++ b/cockatrice/src/game_graphics/game_view.h @@ -7,13 +7,14 @@ #ifndef GAMEVIEW_H #define GAMEVIEW_H +#include "selection_subtype_tally.h" + #include class GameScene; class QGridLayout; class QLabel; class QRubberBand; -struct SubtypeEntry; class GameView : public QGraphicsView { @@ -23,9 +24,10 @@ private: QRubberBand *rubberBand; QLabel *dragCountLabel; QLabel *totalCountLabel; - QWidget *subtypeCountContainer; ///< Container widget for subtype tally display - QGridLayout *subtypeCountLayout; ///< Grid layout for subtype name/count pairs + QWidget *subtypeCountContainer; + QGridLayout *subtypeCountLayout; QPointF selectionOrigin; + QList cachedSubtypeEntries; ///< Cached entries to avoid redundant rebuilds QSize rebuildSubtypeLabels(const QList &entries); void clearSubtypeLabels();