mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-23 18:06:26 -07:00
[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:
parent
22b0f69706
commit
b13c682a7a
3 changed files with 44 additions and 28 deletions
|
|
@ -718,20 +718,20 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Section dividers can be collapsed/expanded by the user. Surface those
|
// Section dividers can be collapsed/expanded by the user. Surface those
|
||||||
// changes only from real user interaction. Programmatic expansion is
|
// changes only from real user interaction. Programmatic expansion is
|
||||||
// applied through setSectionExpanded() / setExpandedProgrammatically().
|
// applied through setSectionExpanded() / setExpandedProgrammatically().
|
||||||
connect(userTree, &QTreeWidget::itemExpanded, this,
|
connect(userTree, &QTreeWidget::itemExpanded, this,
|
||||||
[this](QTreeWidgetItem *item) { handleSectionExpansion(item, true); });
|
[this](QTreeWidgetItem *item) { handleSectionExpansion(item, true); });
|
||||||
connect(userTree, &QTreeWidget::itemCollapsed, this,
|
connect(userTree, &QTreeWidget::itemCollapsed, this,
|
||||||
[this](QTreeWidgetItem *item) { handleSectionExpansion(item, false); });
|
[this](QTreeWidgetItem *item) { handleSectionExpansion(item, false); });
|
||||||
|
|
||||||
// Hide popup when list scrolls (reference row has moved)
|
// Hide popup when list scrolls (reference row has moved)
|
||||||
connect(userTree->verticalScrollBar(), &QScrollBar::valueChanged, this, [this] {
|
connect(userTree->verticalScrollBar(), &QScrollBar::valueChanged, this, [this] {
|
||||||
showPopupTimer->stop();
|
showPopupTimer->stop();
|
||||||
hidePopup(true);
|
hidePopup(true);
|
||||||
requestAvatarsForVisibleItems();
|
requestVisibleItemResources();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Forward join requests from popup upward
|
// Forward join requests from popup upward
|
||||||
connect(userInfoPopup, &UserInfoPopup::joinGameRequested, this, &UserListWidget::joinGameRequested);
|
connect(userInfoPopup, &UserInfoPopup::joinGameRequested, this, &UserListWidget::joinGameRequested);
|
||||||
|
|
@ -746,7 +746,7 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
|
||||||
|
|
||||||
// Keep the popup-less scroll path alive for avatar prefetch.
|
// Keep the popup-less scroll path alive for avatar prefetch.
|
||||||
connect(userTree->verticalScrollBar(), &QScrollBar::valueChanged, this,
|
connect(userTree->verticalScrollBar(), &QScrollBar::valueChanged, this,
|
||||||
[this] { requestAvatarsForVisibleItems(); });
|
[this] { requestVisibleItemResources(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section dividers can be collapsed/expanded by the user. Surface those
|
// Section dividers can be collapsed/expanded by the user. Surface those
|
||||||
|
|
@ -1541,9 +1541,9 @@ void UserListWidget::updateCount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserListWidget::setShowTitle(bool showTitle)
|
void UserListWidget::setShowTitle(bool _showTitle)
|
||||||
{
|
{
|
||||||
this->showTitle = showTitle;
|
this->showTitle = _showTitle;
|
||||||
updateCount();
|
updateCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue