[DeckList] Make deck tree card traversal recursive

getCardNodes and forEachCard now descend into nested zones instead of
assuming a flat main/side/token layout. For today's flat trees this is
behavior-preserving; it also removes two latent crashes (unchecked
dynamic_cast dereference, null card nodes passed to forEachCard
callers). Nested zones are introduced by later custom-zones units.

Took 3 minutes


Took 7 seconds

Took 9 seconds
This commit is contained in:
Lukas Brübach 2026-08-23 22:00:59 +02:00
parent 22b0f69706
commit c4c8484e76
2 changed files with 29 additions and 13 deletions

View file

@ -41,13 +41,19 @@ QList<const DecklistCardNode *> DecklistNodeTree::getCardNodes(const QSet<QStrin
{ {
QList<const DecklistCardNode *> result; QList<const DecklistCardNode *> result;
for (auto *zoneNode : getZoneNodes(restrictToZones)) { std::function<void(const InnerDecklistNode *)> collectCards = [&collectCards,
for (auto *cardNode : *zoneNode) { &result](const InnerDecklistNode *node) {
auto *cardCardNode = dynamic_cast<DecklistCardNode *>(cardNode); for (int i = 0; i < node->size(); i++) {
if (cardCardNode) { if (auto *card = dynamic_cast<const DecklistCardNode *>(node->at(i))) {
result.append(cardCardNode); result.append(card);
} else if (auto *inner = dynamic_cast<const InnerDecklistNode *>(node->at(i))) {
collectCards(inner);
} }
} }
};
for (auto *zoneNode : getZoneNodes(restrictToZones)) {
collectCards(zoneNode);
} }
return result; return result;
@ -160,13 +166,22 @@ bool DecklistNodeTree::deleteNode(AbstractDecklistNode *node, InnerDecklistNode
void DecklistNodeTree::forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func) const void DecklistNodeTree::forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func) const
{ {
// Support for this is only possible if the internal structure // Cards nested in custom zones are reported with their top-level board zone
// doesn't get more complicated. // so that callers can classify cards by board (main/side/maybeboard/tokens).
std::function<void(InnerDecklistNode *, InnerDecklistNode *)> walk = [&func, &walk](InnerDecklistNode *boardZone,
InnerDecklistNode *node) {
for (int i = 0; i < node->size(); i++) {
if (auto *card = dynamic_cast<DecklistCardNode *>(node->at(i))) {
func(boardZone, card);
} else if (auto *inner = dynamic_cast<InnerDecklistNode *>(node->at(i))) {
walk(boardZone, inner);
}
}
};
for (int i = 0; i < root->size(); i++) { for (int i = 0; i < root->size(); i++) {
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(root->at(i)); if (auto *zone = dynamic_cast<InnerDecklistNode *>(root->at(i))) {
for (int j = 0; j < node->size(); j++) { walk(zone, zone);
DecklistCardNode *card = dynamic_cast<DecklistCardNode *>(node->at(j));
func(node, card);
} }
} }
} }

View file

@ -78,9 +78,10 @@ public:
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr); bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
/** /**
* @brief Apply a function to every card in the deck tree. This can modify the cards. * @brief Applies a function to every card in the deck tree. This can modify the cards.
* *
* @param func Function taking (zone node, card node). * @param func Function taking (top-level board zone node, card node). Cards nested
* in custom zones are reported with their board zone.
*/ */
void forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func) const; void forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func) const;