[Refactor] Move the attachment graph and card view teardown onto CardState

This commit is contained in:
Lukas Brübach 2026-10-06 18:47:44 +02:00 • committed by GitHub
parent cea80b0898
commit dd04eed6d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 54 additions and 19 deletions

View file

@ -13,11 +13,17 @@ void CardState::resetState(bool keepAnnotations)
annotation.clear(); annotation.clear();
} }
attachedTo = nullptr; attachedTo = nullptr;
attachedCards.clear();
setTapped(false, false); setTapped(false, false);
setDoesntUntap(false); setDoesntUntap(false);
emit stateReset(); emit stateReset();
} }
void CardState::deleteView()
{
emit viewDeleteRequested();
}
void CardState::setCardRef(const CardRef &_cardRef) void CardState::setCardRef(const CardRef &_cardRef)
{ {
if (cardRef == _cardRef) { if (cardRef == _cardRef) {
@ -188,7 +194,7 @@ void CardState::setDestroyOnZoneChange(bool _destroyOnZoneChange)
emit stateChanged(); emit stateChanged();
} }
void CardState::setAttachedTo(CardItem *_attachedTo) void CardState::setAttachedTo(CardState *_attachedTo)
{ {
if (attachedTo == _attachedTo) { if (attachedTo == _attachedTo) {
return; return;

View file

@ -1,6 +1,7 @@
#ifndef COCKATRICE_CARD_STATE_H #ifndef COCKATRICE_CARD_STATE_H
#define COCKATRICE_CARD_STATE_H #define COCKATRICE_CARD_STATE_H
#include <QList>
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QPoint> #include <QPoint>
@ -32,7 +33,8 @@ private:
bool doesntUntap = false; bool doesntUntap = false;
bool destroyOnZoneChange = false; bool destroyOnZoneChange = false;
CardItem *attachedTo = nullptr; CardState *attachedTo = nullptr;
QList<CardItem *> attachedCards;
CardZoneLogic *zone = nullptr; CardZoneLogic *zone = nullptr;
signals: signals:
@ -51,9 +53,11 @@ signals:
void ptChanged(const QString &newPt); void ptChanged(const QString &newPt);
void doesntUntapChanged(bool newValue); void doesntUntapChanged(bool newValue);
void destroyOnZoneChangeChanged(bool newValue); void destroyOnZoneChangeChanged(bool newValue);
void attachedToChanged(CardItem *newAttachedTo); void attachedToChanged(CardState *newAttachedTo);
void zoneChanged(CardState *changedCard, CardZoneLogic *newZone); void zoneChanged(CardState *changedCard, CardZoneLogic *newZone);
void viewDeleteRequested();
public: public:
explicit CardState(QObject *parent, CardZoneLogic *_zone = nullptr, const CardRef &_cardRef = {}, int _id = -1) explicit CardState(QObject *parent, CardZoneLogic *_zone = nullptr, const CardRef &_cardRef = {}, int _id = -1)
: QObject(parent), id(_id), cardRef(_cardRef), zone(_zone) : QObject(parent), id(_id), cardRef(_cardRef), zone(_zone)
@ -62,6 +66,15 @@ public:
void resetState(bool keepAnnotations); void resetState(bool keepAnnotations);
/**
* @brief Asks the card view owning this state to tear itself down.
*
* The state never holds a card item directly, so it cannot delete one. It asks instead and lets the view run
* its own teardown, which detaches attachments, drops it from the active card slot and unregisters its
* animations before the graphics object goes away.
*/
void deleteView();
int getId() const int getId() const
{ {
return id; return id;
@ -187,12 +200,27 @@ public:
void setDestroyOnZoneChange(bool _destroyOnZoneChange); void setDestroyOnZoneChange(bool _destroyOnZoneChange);
CardItem *getAttachedTo() const CardState *getAttachedTo() const
{ {
return attachedTo; return attachedTo;
} }
void setAttachedTo(CardItem *_attachedTo); void setAttachedTo(CardState *_attachedTo);
void addAttachedCard(CardItem *card)
{
attachedCards.append(card);
}
void removeAttachedCard(CardItem *card)
{
attachedCards.removeOne(card);
}
const QList<CardItem *> &getAttachedCards() const
{
return attachedCards;
}
}; };
#endif // COCKATRICE_CARD_STATE_H #endif // COCKATRICE_CARD_STATE_H

View file

@ -356,7 +356,7 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
} }
if (card->getAttachedTo() && (startZone != targetZone)) { if (card->getAttachedTo() && (startZone != targetZone)) {
CardItem *parentCard = card->getAttachedTo(); CardState *parentCard = card->getAttachedTo();
card->setAttachedTo(nullptr); card->setAttachedTo(nullptr);
parentCard->getZone()->reorganizeCards(); parentCard->getZone()->reorganizeCards();
} }
@ -440,7 +440,7 @@ void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
emit logDestroyCard(player, card->getName()); emit logDestroyCard(player, card->getName());
zone->takeCard(-1, event.card_id(), true); zone->takeCard(-1, event.card_id(), true);
card->deleteLater(); card->getState()->deleteView();
} }
void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event) void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
@ -469,7 +469,7 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
return; return;
} }
CardItem *oldParent = startCard->getAttachedTo(); CardState *oldParent = startCard->getAttachedTo();
startCard->setAttachedTo(targetCard); startCard->setAttachedTo(targetCard);

View file

@ -251,7 +251,7 @@ void PlayerLogic::deleteCard(CardItem *card)
} else if (dialogSemaphore) { } else if (dialogSemaphore) {
cardsToDelete.append(card); cardsToDelete.append(card);
} else { } else {
card->deleteLater(); card->getState()->deleteView();
} }
} }
@ -332,7 +332,7 @@ bool PlayerLogic::clearCardsToDelete()
for (auto &i : cardsToDelete) { for (auto &i : cardsToDelete) {
if (i != nullptr) { if (i != nullptr) {
i->deleteLater(); i->getState()->deleteView();
} }
} }
cardsToDelete.clear(); cardsToDelete.clear();

View file

@ -147,7 +147,7 @@ void ZoneViewZoneLogic::removeCard(int position, bool toNewZone)
} }
CardItem *card = cards.takeAt(position); CardItem *card = cards.takeAt(position);
card->deleteLater(); card->getState()->deleteView();
// The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that // The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that
// card gets dragged within the view. // card gets dragged within the view.
@ -165,7 +165,7 @@ void ZoneViewZoneLogic::removeCard(int position, bool toNewZone)
void ZoneViewZoneLogic::clearCards() void ZoneViewZoneLogic::clearCards()
{ {
for (auto card : cards) { for (auto card : cards) {
card->deleteLater(); card->getState()->deleteView();
} }
cards.clear(); cards.clear();
emit cardCountChanged(); emit cardCountChanged();

View file

@ -65,12 +65,13 @@ CardItem::CardItem(PlayerLogic *_owner,
}); });
connect(state, &CardState::stateReset, this, [this] { connect(state, &CardState::stateReset, this, [this] {
attachedCards.clear();
if (scene()) { if (scene()) {
static_cast<GameScene *>(scene())->unregisterAnimationItem(this); static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
} }
update(); update();
}); });
connect(state, &CardState::viewDeleteRequested, this, &CardItem::deleteLater);
} }
void CardItem::prepareDelete() void CardItem::prepareDelete()
@ -84,6 +85,7 @@ void CardItem::prepareDelete()
owner = nullptr; owner = nullptr;
} }
const QList<CardItem *> &attachedCards = state->getAttachedCards();
while (!attachedCards.isEmpty()) { while (!attachedCards.isEmpty()) {
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards() attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
attachedCards.first()->setAttachedTo(nullptr); attachedCards.first()->setAttachedTo(nullptr);
@ -224,7 +226,7 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
} }
state->setGridPoint(QPoint(-1, state->getGridPoint().y())); state->setGridPoint(QPoint(-1, state->getGridPoint().y()));
state->setAttachedTo(_attachedTo); state->setAttachedTo(_attachedTo == nullptr ? nullptr : _attachedTo->getState());
if (state->getAttachedTo() != nullptr) { if (state->getAttachedTo() != nullptr) {
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its // If the zone is being torn down, it might already be null by the time a card tries to un-attach all its
// attached cards // attached cards

View file

@ -34,7 +34,6 @@ class CardItem : public AbstractCardItem
Q_OBJECT Q_OBJECT
private: private:
CardDragItem *dragItem; CardDragItem *dragItem;
QList<CardItem *> attachedCards;
void prepareDelete(); void prepareDelete();
void handleClickedToPlay(bool shiftHeld); void handleClickedToPlay(bool shiftHeld);
@ -116,22 +115,22 @@ public:
{ {
state->setDestroyOnZoneChange(_destroy); state->setDestroyOnZoneChange(_destroy);
} }
[[nodiscard]] CardItem *getAttachedTo() const [[nodiscard]] CardState *getAttachedTo() const
{ {
return state->getAttachedTo(); return state->getAttachedTo();
} }
void setAttachedTo(CardItem *_attachedTo); void setAttachedTo(CardItem *_attachedTo);
void addAttachedCard(CardItem *card) void addAttachedCard(CardItem *card)
{ {
attachedCards.append(card); state->addAttachedCard(card);
} }
void removeAttachedCard(CardItem *card) void removeAttachedCard(CardItem *card)
{ {
attachedCards.removeOne(card); state->removeAttachedCard(card);
} }
[[nodiscard]] const QList<CardItem *> &getAttachedCards() const [[nodiscard]] const QList<CardItem *> &getAttachedCards() const
{ {
return attachedCards; return state->getAttachedCards();
} }
void resetState(bool keepAnnotations = false); void resetState(bool keepAnnotations = false);
void processCardInfo(const ServerInfo_Card &_info); void processCardInfo(const ServerInfo_Card &_info);