[DeckList] Make deck tree card traversal recursive (#7175)

* [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

* Fix rebase mistake.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-24 18:44:05 +02:00 committed by GitHub
parent 22b0f69706
commit b13c682a7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 44 additions and 28 deletions

View file

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

View file

@ -78,9 +78,10 @@ public:
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;