From 95a86703b3fab84a09bca1ebff8621b14f9d0a96 Mon Sep 17 00:00:00 2001
From: "transifex-integration[bot]"
<43880903+transifex-integration[bot]@users.noreply.github.com>
Date: Thu, 24 Apr 2025 22:39:35 -0400
Subject: [PATCH 01/19] Updates for project Cockatrice and language it (#5876)
* Translate cockatrice/cockatrice_en@source.ts in it
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.
* Translate cockatrice/cockatrice_en@source.ts in it
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'it'.
---------
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
---
cockatrice/translations/cockatrice_it.ts | 33 ++++++++++++------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/cockatrice/translations/cockatrice_it.ts b/cockatrice/translations/cockatrice_it.ts
index f310b5842..7cdce9896 100644
--- a/cockatrice/translations/cockatrice_it.ts
+++ b/cockatrice/translations/cockatrice_it.ts
@@ -203,7 +203,7 @@ Controlla se la cartella è valida e prova ancora.
Minimum overlap percentage of cards on the stack and in vertical hand
- Sovrapposizione % minima delle carte in pila e nella mano verticale
+ Sovrapposizione % minima delle carte in pila e nella mano verticale:
@@ -2770,13 +2770,13 @@ Per favore, visitate la pagina di download per aggiornare manualmente.
Released
- Rilasciato
+ Data di rilascio
Changelog
- Cambiamenti della versione
+ Changelog
@@ -2789,13 +2789,14 @@ Per favore, visitate la pagina di download per aggiornare manualmente.
Unfortunately there are no download packages available for your operating system.
You may have to build from source yourself.
-
+ Purtroppo l'aggiornamento automatico non è riuscito a trovare una versione compatibile.
+Dovrai scaricare la nuova versione manualmente.
Please check the <a href="%1">releases page</a> on our Github and download the build for your system.
Please check the download page manually and visit the wiki for instructions on compiling.
-
+ Per favore, controlla la <a href="%1">pagina delle release</a> sul nostro Github e scarica la versione giusta per il tuo sistema.
@@ -3668,7 +3669,7 @@ La tua versione è la %1, la versione online è la %2.
Start &local game...
- Inizia &partita in locale...
+ Inizia partita in &locale...
@@ -3688,7 +3689,7 @@ La tua versione è la %1, la versione online è la %2.
&Restore password...
- &Recupera password...
+ Recupera &password...
@@ -3713,7 +3714,7 @@ La tua versione è la %1, la versione online è la %2.
C&ard Database
- &Database delle carte
+ &Database carte
@@ -3916,7 +3917,7 @@ Scopri metodi alternativi per visualizzare i set o disabilitare set ed effetti n
An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel.
-
+ Si è verificato un errore nel cercare di scrivere al processo. Ad esempio, il processo potrebbe non essere in esecuzione, o potrebbe aver chiuso il suo canale di input.
@@ -5850,7 +5851,7 @@ Il database delle carte verrà ricaricato.
Time started
- Inizio tempo
+ Inizio
@@ -7122,7 +7123,7 @@ Più informazioni inserisci, più specifici saranno i risultati.
Deck Editor
- Editor dei mazzi
+ &Editor dei mazzi
@@ -7142,12 +7143,12 @@ Più informazioni inserisci, più specifici saranno i risultati.
Deck Storage
- Archivio mazzi
+ &Archivio mazzi
Game Replays
- Replay partite
+ &Replay partite
@@ -8299,12 +8300,12 @@ Se pregato di evitare di continuare questa attività o potrebbero venire presi u
Analyze Deck (deckstats.net)
Analyze Deck
- Analizza Mazzo (deckstats.net)
+ Analizza mazzo (deckstats.net)
Analyze Deck (tappedout.net)
- Analizza Mazzo (tappedout.net)
+ Analizza mazzo (tappedout.net)
@@ -8496,7 +8497,7 @@ Se pregato di evitare di continuare questa attività o potrebbero venire presi u
Set Red Counters...
- Imposta Contatore Rosso...
+ Imposta Segnalino Rosso...
From 6fd1e9a4c4e2322f8bc4aa0778ddbaedf38867cb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 24 Apr 2025 22:42:18 -0400
Subject: [PATCH 02/19] Bump serialize-javascript from 6.0.0 to 6.0.2 in
/webclient (#5878)
Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 6.0.0 to 6.0.2.
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)
---
updated-dependencies:
- dependency-name: serialize-javascript
dependency-version: 6.0.2
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
webclient/package-lock.json | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/webclient/package-lock.json b/webclient/package-lock.json
index f5c2ea22e..fa93ceb06 100644
--- a/webclient/package-lock.json
+++ b/webclient/package-lock.json
@@ -17962,9 +17962,10 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+ "license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
@@ -32825,9 +32826,9 @@
}
},
"serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"requires": {
"randombytes": "^2.1.0"
}
From 1409dcc2e860b27d9b19f324e90f83fa8986e687 Mon Sep 17 00:00:00 2001
From: Basile Clement
Date: Sun, 27 Apr 2025 01:55:54 +0200
Subject: [PATCH 03/19] Remove `isView` flag from CardZone (#5728)
* Remove `isView` flag from CardZone
This flag is used for two purposes:
1. It is used as a check for casting to a zone to a `ZoneViewZone`;
2. Non-view zones are added to the player's zones on construction
This patch removes the `isView` flag and instead:
1. We directly cast zones to `ZoneViewZone` using a dynamic (qobject)
cast and use the result of the cast instead of the `isView` flag to
detect if we are a view zone or not;
2. The player records its own zones when they are created, simplifying
control flow.
* Review
---
cockatrice/src/game/board/card_item.cpp | 9 +++----
cockatrice/src/game/player/player.cpp | 32 +++++++++--------------
cockatrice/src/game/player/player.h | 7 ++++-
cockatrice/src/game/zones/card_zone.cpp | 11 +++-----
cockatrice/src/game/zones/card_zone.h | 8 +-----
cockatrice/src/game/zones/select_zone.cpp | 5 ++--
cockatrice/src/game/zones/select_zone.h | 3 +--
cockatrice/src/game/zones/view_zone.cpp | 2 +-
8 files changed, 30 insertions(+), 47 deletions(-)
diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp
index e9a345e86..55df7a477 100644
--- a/cockatrice/src/game/board/card_item.cpp
+++ b/cockatrice/src/game/board/card_item.cpp
@@ -336,8 +336,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
2 * QApplication::startDragDistance())
return;
- if (zone->getIsView()) {
- const ZoneViewZone *view = static_cast(zone);
+ if (const ZoneViewZone *view = qobject_cast(zone)) {
if (view->getRevealZone() && !view->getWriteableRevealZone())
return;
} else if (!owner->getLocalOrJudge())
@@ -394,10 +393,8 @@ void CardItem::playCard(bool faceDown)
*/
static bool isUnwritableRevealZone(CardZone *zone)
{
- if (zone && zone->getIsView()) {
- if (auto *view = static_cast(zone)) {
- return view->getRevealZone() && !view->getWriteableRevealZone();
- }
+ if (auto *view = qobject_cast(zone)) {
+ return view->getRevealZone() && !view->getWriteableRevealZone();
}
return false;
}
diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp
index e4d319633..c893e27af 100644
--- a/cockatrice/src/game/player/player.cpp
+++ b/cockatrice/src/game/player/player.cpp
@@ -124,7 +124,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
qreal avatarMargin = (counterAreaWidth + CARD_HEIGHT + 15 - playerTarget->boundingRect().width()) / 2.0;
playerTarget->setPos(QPointF(avatarMargin, avatarMargin));
- auto *_deck = new PileZone(this, "deck", true, false, playerArea);
+ auto *_deck = addZone(new PileZone(this, "deck", true, false, playerArea));
QPointF base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0,
10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0);
_deck->setPos(base);
@@ -135,22 +135,23 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
handCounter->setPos(base + QPointF(0, h + 10));
qreal h2 = handCounter->boundingRect().height();
- PileZone *grave = new PileZone(this, "grave", false, true, playerArea);
+ PileZone *grave = addZone(new PileZone(this, "grave", false, true, playerArea));
grave->setPos(base + QPointF(0, h + h2 + 10));
- PileZone *rfg = new PileZone(this, "rfg", false, true, playerArea);
+ PileZone *rfg = addZone(new PileZone(this, "rfg", false, true, playerArea));
rfg->setPos(base + QPointF(0, 2 * h + h2 + 10));
- PileZone *sb = new PileZone(this, "sb", false, false, playerArea);
+ PileZone *sb = addZone(new PileZone(this, "sb", false, false, playerArea));
sb->setVisible(false);
- table = new TableZone(this, this);
+ table = addZone(new TableZone(this, this));
connect(table, &TableZone::sizeChanged, this, &Player::updateBoundingRect);
- stack = new StackZone(this, (int)table->boundingRect().height(), this);
+ stack = addZone(new StackZone(this, (int)table->boundingRect().height(), this));
- hand = new HandZone(this, _local || _judge || (_parent->getSpectator() && _parent->getSpectatorsSeeEverything()),
- (int)table->boundingRect().height(), this);
+ hand = addZone(new HandZone(this,
+ _local || _judge || (_parent->getSpectator() && _parent->getSpectatorsSeeEverything()),
+ (int)table->boundingRect().height(), this));
connect(hand, &HandZone::cardCountChanged, handCounter, &HandCounter::updateNumber);
connect(handCounter, &HandCounter::showContextMenu, hand, &HandZone::showContextMenu);
@@ -2889,11 +2890,6 @@ void Player::deleteCard(CardItem *card)
}
}
-void Player::addZone(CardZone *zone)
-{
- zones.insert(zone->getName(), zone);
-}
-
AbstractCounter *Player::addCounter(const ServerInfo_Counter &counter)
{
return addCounter(counter.id(), QString::fromStdString(counter.name()),
@@ -3692,9 +3688,8 @@ void Player::actCardCounterTrigger()
*/
static bool isUnwritableRevealZone(CardZone *zone)
{
- if (zone && zone->getIsView()) {
- auto *view = static_cast(zone);
- return view && view->getRevealZone() && !view->getWriteableRevealZone();
+ if (auto *view = qobject_cast(zone)) {
+ return view->getRevealZone() && !view->getWriteableRevealZone();
}
return false;
}
@@ -3784,8 +3779,7 @@ void Player::updateCardMenu(const CardItem *card)
bool revealedCard = false;
bool writeableCard = getLocalOrJudge();
- if (card->getZone() && card->getZone()->getIsView()) {
- auto *view = dynamic_cast(card->getZone());
+ if (auto *view = qobject_cast(card->getZone())) {
if (view->getRevealZone()) {
if (view->getWriteableRevealZone()) {
writeableCard = true;
@@ -3955,7 +3949,7 @@ void Player::updateCardMenu(const CardItem *card)
cardMenu->addSeparator();
cardMenu->addAction(aSelectAll);
- if (card->getZone()->getIsView()) {
+ if (qobject_cast(card->getZone())) {
cardMenu->addAction(aSelectColumn);
}
diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h
index 76365d50d..027a7658b 100644
--- a/cockatrice/src/game/player/player.h
+++ b/cockatrice/src/game/player/player.h
@@ -409,7 +409,12 @@ public:
void playCardToTable(const CardItem *c, bool faceDown);
void addCard(CardItem *c);
void deleteCard(CardItem *c);
- void addZone(CardZone *z);
+
+ template T *addZone(T *zone)
+ {
+ zones.insert(zone->getName(), zone);
+ return zone;
+ }
AbstractCounter *addCounter(const ServerInfo_Counter &counter);
AbstractCounter *addCounter(int counterId, const QString &name, QColor color, int radius, int value);
diff --git a/cockatrice/src/game/zones/card_zone.cpp b/cockatrice/src/game/zones/card_zone.cpp
index 24eaa4f52..05b4c35b9 100644
--- a/cockatrice/src/game/zones/card_zone.cpp
+++ b/cockatrice/src/game/zones/card_zone.cpp
@@ -19,21 +19,16 @@
* @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it
* @param _contentsKnown whether the cards in the zone are known to the client
* @param parent the parent graphics object.
- * @param _isView whether this zone is a view of another zone. Modifications to a view should modify the original
*/
CardZone::CardZone(Player *_p,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
- QGraphicsItem *parent,
- bool _isView)
+ QGraphicsItem *parent)
: AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), views{}, menu(nullptr),
- doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable), isView(_isView)
+ doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable)
{
- if (!isView)
- player->addZone(this);
-
// If we join a game before the card db finishes loading, the cards might have the wrong printings.
// Force refresh all cards in the zone when db finishes loading to fix that.
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
@@ -236,4 +231,4 @@ void CardZone::moveAllToZone()
QPointF CardZone::closestGridPoint(const QPointF &point)
{
return point;
-}
\ No newline at end of file
+}
diff --git a/cockatrice/src/game/zones/card_zone.h b/cockatrice/src/game/zones/card_zone.h
index 2b9114a6d..8ce31e885 100644
--- a/cockatrice/src/game/zones/card_zone.h
+++ b/cockatrice/src/game/zones/card_zone.h
@@ -35,7 +35,6 @@ protected:
QAction *doubleClickAction;
bool hasCardAttr;
bool isShufflable;
- bool isView;
bool alwaysRevealTopCard;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
@@ -65,8 +64,7 @@ public:
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
- QGraphicsItem *parent = nullptr,
- bool _isView = false);
+ QGraphicsItem *parent = nullptr);
void retranslateUi();
void clearContents();
bool getHasCardAttr() const
@@ -115,10 +113,6 @@ public:
}
virtual void reorganizeCards() = 0;
virtual QPointF closestGridPoint(const QPointF &point);
- bool getIsView() const
- {
- return isView;
- }
bool getAlwaysRevealTopCard() const
{
return alwaysRevealTopCard;
diff --git a/cockatrice/src/game/zones/select_zone.cpp b/cockatrice/src/game/zones/select_zone.cpp
index bb6c40683..b26aa23ff 100644
--- a/cockatrice/src/game/zones/select_zone.cpp
+++ b/cockatrice/src/game/zones/select_zone.cpp
@@ -37,9 +37,8 @@ SelectZone::SelectZone(Player *_player,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
- QGraphicsItem *parent,
- bool isView)
- : CardZone(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent, isView)
+ QGraphicsItem *parent)
+ : CardZone(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent)
{
}
diff --git a/cockatrice/src/game/zones/select_zone.h b/cockatrice/src/game/zones/select_zone.h
index c8c88b450..6aa56a1c6 100644
--- a/cockatrice/src/game/zones/select_zone.h
+++ b/cockatrice/src/game/zones/select_zone.h
@@ -26,8 +26,7 @@ public:
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
- QGraphicsItem *parent = nullptr,
- bool isView = false);
+ QGraphicsItem *parent = nullptr);
};
qreal divideCardSpaceInZone(qreal index, int cardCount, qreal totalHeight, qreal cardHeight, bool reverse = false);
diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp
index 1c35b6613..fd9387064 100644
--- a/cockatrice/src/game/zones/view_zone.cpp
+++ b/cockatrice/src/game/zones/view_zone.cpp
@@ -30,7 +30,7 @@ ZoneViewZone::ZoneViewZone(Player *_p,
bool _writeableRevealZone,
QGraphicsItem *parent,
bool _isReversed)
- : SelectZone(_p, _origZone->getName(), false, false, true, parent, true), bRect(QRectF()), minRows(0),
+ : SelectZone(_p, _origZone->getName(), false, false, true, parent), bRect(QRectF()), minRows(0),
numberCards(_numberCards), origZone(_origZone), revealZone(_revealZone),
writeableRevealZone(_writeableRevealZone), groupBy(CardList::NoSort), sortBy(CardList::NoSort),
isReversed(_isReversed)
From 42ce9f4d8954578dba4eb41ceaec2cbd8d67d910 Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Sat, 26 Apr 2025 16:59:59 -0700
Subject: [PATCH 04/19] Allow tokens on the stack (#5886)
---
common/server_player.cpp | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/common/server_player.cpp b/common/server_player.cpp
index 60ebfa3c0..eb8a9977b 100644
--- a/common/server_player.cpp
+++ b/common/server_player.cpp
@@ -417,6 +417,29 @@ static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Ca
return event;
}
+/**
+ * Determines whether moving the card from startZone to targetZone should cause the card to be destroyed.
+ */
+static bool
+shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, const Server_CardZone *targetZone)
+{
+ if (!card->getDestroyOnZoneChange()) {
+ return false;
+ }
+
+ if (startZone->getName() == targetZone->getName()) {
+ return false;
+ }
+
+ // Allow tokens on the stack
+ if ((startZone->getName() == "table" || startZone->getName() == "stack") &&
+ (targetZone->getName() == "table" || targetZone->getName() == "stack")) {
+ return false;
+ }
+
+ return true;
+}
+
Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
Server_CardZone *startzone,
const QList &_cards,
@@ -523,7 +546,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
}
}
- if (card->getDestroyOnZoneChange() && (startzone->getName() != targetzone->getName())) {
+ if (shouldDestroyOnMove(card, startzone, targetzone)) {
Event_DestroyCard event;
event.set_zone_name(startzone->getName().toStdString());
event.set_card_id(static_cast(card->getId()));
From e3465be8c118c7f4013229471b57dd2c082c8159 Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Sun, 27 Apr 2025 21:27:22 -0700
Subject: [PATCH 05/19] Allow cards to transform directly on stack (#5888)
---
cockatrice/src/game/player/player.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp
index c893e27af..ebbbb5291 100644
--- a/cockatrice/src/game/player/player.cpp
+++ b/cockatrice/src/game/player/player.cpp
@@ -2066,6 +2066,8 @@ void Player::createCard(const CardItem *sourceCard,
break;
case CardRelation::TransformInto:
+ // allow cards to directly transform on stack
+ cmd.set_zone(sourceCard->getZone()->getName() == "stack" ? "stack" : "table");
// Transform card zone changes are handled server-side
cmd.set_target_zone(sourceCard->getZone()->getName().toStdString());
cmd.set_target_card_id(sourceCard->getId());
From bb8213deb58bfce779b6bf6a6bc90de94dfdd759 Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Sun, 27 Apr 2025 21:30:23 -0700
Subject: [PATCH 06/19] Support creating face-down tokens (#5800)
* add new fields to proto
* update token dlg
* send facedown in command
* update server to get it to work
* disable certain edits when face down
* update client event processing
* log face-down token creation
* Don't support colors on face-down tokens
The other client doesn't know about the color, so it causes a desync
* Update wording
Co-authored-by: Basile Clement
* Allow annotations on face-down tokens
---------
Co-authored-by: Basile Clement
---
cockatrice/src/dialogs/dlg_create_token.cpp | 22 ++++++-
cockatrice/src/dialogs/dlg_create_token.h | 3 +
cockatrice/src/game/player/player.cpp | 8 ++-
cockatrice/src/game/player/player.h | 2 +-
cockatrice/src/server/message_log_widget.cpp | 14 +++--
cockatrice/src/server/message_log_widget.h | 2 +-
common/pb/command_create_token.proto | 1 +
common/pb/event_create_token.proto | 1 +
common/server_player.cpp | 62 +++++++++++++++++---
common/server_player.h | 1 +
10 files changed, 97 insertions(+), 19 deletions(-)
diff --git a/cockatrice/src/dialogs/dlg_create_token.cpp b/cockatrice/src/dialogs/dlg_create_token.cpp
index 7b3db2773..14adaa48a 100644
--- a/cockatrice/src/dialogs/dlg_create_token.cpp
+++ b/cockatrice/src/dialogs/dlg_create_token.cpp
@@ -63,6 +63,9 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
destroyCheckBox->setChecked(true);
+ faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
+ connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
+
QGridLayout *grid = new QGridLayout;
grid->addWidget(nameLabel, 0, 0);
grid->addWidget(nameEdit, 0, 1);
@@ -73,6 +76,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
grid->addWidget(annotationLabel, 3, 0);
grid->addWidget(annotationEdit, 3, 1);
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
+ grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
tokenDataGroupBox->setLayout(grid);
@@ -155,6 +159,21 @@ void DlgCreateToken::closeEvent(QCloseEvent *event)
SettingsCache::instance().setTokenDialogGeometry(saveGeometry());
}
+void DlgCreateToken::faceDownCheckBoxToggled(bool checked)
+{
+ if (checked) {
+ colorEdit->setCurrentIndex(6);
+ colorEdit->setEnabled(false);
+ ptEdit->clear();
+ ptEdit->clearFocus();
+ ptEdit->setEnabled(false);
+ } else {
+ colorEdit->setEnabled(true);
+ ptEdit->setEnabled(true);
+ annotationEdit->setEnabled(true);
+ }
+}
+
void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
{
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
@@ -230,5 +249,6 @@ TokenInfo DlgCreateToken::getTokenInfo() const
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
.pt = ptEdit->text(),
.annotation = annotationEdit->text(),
- .destroy = destroyCheckBox->isChecked()};
+ .destroy = destroyCheckBox->isChecked(),
+ .faceDown = faceDownCheckBox->isChecked()};
}
diff --git a/cockatrice/src/dialogs/dlg_create_token.h b/cockatrice/src/dialogs/dlg_create_token.h
index 373e094f1..cd1b48c81 100644
--- a/cockatrice/src/dialogs/dlg_create_token.h
+++ b/cockatrice/src/dialogs/dlg_create_token.h
@@ -24,6 +24,7 @@ struct TokenInfo
QString pt;
QString annotation;
bool destroy = true;
+ bool faceDown = false;
};
class DlgCreateToken : public QDialog
@@ -36,6 +37,7 @@ public:
protected:
void closeEvent(QCloseEvent *event) override;
private slots:
+ void faceDownCheckBoxToggled(bool checked);
void tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
void updateSearch(const QString &search);
void actChooseTokenFromAll(bool checked);
@@ -51,6 +53,7 @@ private:
QComboBox *colorEdit;
QLineEdit *nameEdit, *ptEdit, *annotationEdit;
QCheckBox *destroyCheckBox;
+ QCheckBox *faceDownCheckBox;
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
CardInfoPictureWidget *pic;
QTreeView *chooseTokenView;
diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp
index ebbbb5291..160d8d336 100644
--- a/cockatrice/src/game/player/player.cpp
+++ b/cockatrice/src/game/player/player.cpp
@@ -1855,6 +1855,7 @@ void Player::actCreateAnotherToken()
cmd.set_pt(lastTokenInfo.pt.toStdString());
cmd.set_annotation(lastTokenInfo.annotation.toStdString());
cmd.set_destroy_on_zone_change(lastTokenInfo.destroy);
+ cmd.set_face_down(lastTokenInfo.faceDown);
cmd.set_x(-1);
cmd.set_y(lastTokenTableRow);
@@ -2233,10 +2234,10 @@ void Player::eventCreateToken(const Event_CreateToken &event)
CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()),
QString::fromStdString(event.card_provider_id()), event.card_id());
- // use db PT if not provided in event
+ // use db PT if not provided in event and not face-down
if (!QString::fromStdString(event.pt()).isEmpty()) {
card->setPT(QString::fromStdString(event.pt()));
- } else {
+ } else if (!event.face_down()) {
CardInfoPtr dbCard = card->getInfo();
if (dbCard) {
card->setPT(dbCard->getPowTough());
@@ -2245,8 +2246,9 @@ void Player::eventCreateToken(const Event_CreateToken &event)
card->setColor(QString::fromStdString(event.color()));
card->setAnnotation(QString::fromStdString(event.annotation()));
card->setDestroyOnZoneChange(event.destroy_on_zone_change());
+ card->setFaceDown(event.face_down());
- emit logCreateToken(this, card->getName(), card->getPT());
+ emit logCreateToken(this, card->getName(), card->getPT(), card->getFaceDown());
zone->addCard(card, true, event.x(), event.y());
}
diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h
index 027a7658b..ea5278572 100644
--- a/cockatrice/src/game/player/player.h
+++ b/cockatrice/src/game/player/player.h
@@ -128,7 +128,7 @@ signals:
Player *targetPlayer,
QString targetCard,
bool _playerTarget);
- void logCreateToken(Player *player, QString cardName, QString pt);
+ void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
void logDrawCards(Player *player, int number, bool deckIsEmpty);
void logUndoDraw(Player *player, QString cardName);
void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX);
diff --git a/cockatrice/src/server/message_log_widget.cpp b/cockatrice/src/server/message_log_widget.cpp
index 7590e044b..0d21be045 100644
--- a/cockatrice/src/server/message_log_widget.cpp
+++ b/cockatrice/src/server/message_log_widget.cpp
@@ -214,12 +214,16 @@ void MessageLogWidget::logCreateArrow(Player *player,
}
}
-void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt)
+void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt, bool faceDown)
{
- appendHtmlServerMessage(tr("%1 creates token: %2%3.")
- .arg(sanitizeHtml(player->getName()))
- .arg(cardLink(std::move(cardName)))
- .arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
+ if (faceDown) {
+ appendHtmlServerMessage(tr("%1 creates a face down token.").arg(sanitizeHtml(player->getName())));
+ } else {
+ appendHtmlServerMessage(tr("%1 creates token: %2%3.")
+ .arg(sanitizeHtml(player->getName()))
+ .arg(cardLink(std::move(cardName)))
+ .arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
+ }
}
void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize)
diff --git a/cockatrice/src/server/message_log_widget.h b/cockatrice/src/server/message_log_widget.h
index f6780b729..21b03dee9 100644
--- a/cockatrice/src/server/message_log_widget.h
+++ b/cockatrice/src/server/message_log_widget.h
@@ -41,7 +41,7 @@ public slots:
Player *targetPlayer,
QString targetCard,
bool playerTarget);
- void logCreateToken(Player *player, QString cardName, QString pt);
+ void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
void logDestroyCard(Player *player, QString cardName);
void logDrawCards(Player *player, int number, bool deckIsEmpty);
diff --git a/common/pb/command_create_token.proto b/common/pb/command_create_token.proto
index 671ce3fe3..4b7d11098 100644
--- a/common/pb/command_create_token.proto
+++ b/common/pb/command_create_token.proto
@@ -27,4 +27,5 @@ message Command_CreateToken {
optional TargetMode target_mode = 11;
optional string card_provider_id = 12;
+ optional bool face_down = 13;
}
diff --git a/common/pb/event_create_token.proto b/common/pb/event_create_token.proto
index bc88a744c..6947b6048 100644
--- a/common/pb/event_create_token.proto
+++ b/common/pb/event_create_token.proto
@@ -15,4 +15,5 @@ message Event_CreateToken {
optional sint32 x = 8;
optional sint32 y = 9;
optional string card_provider_id = 10;
+ optional bool face_down = 11;
}
diff --git a/common/server_player.cpp b/common/server_player.cpp
index eb8a9977b..01f6947fa 100644
--- a/common/server_player.cpp
+++ b/common/server_player.cpp
@@ -386,13 +386,23 @@ void Server_Player::revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorag
}
}
-static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord)
+/**
+ * Creates the create token event.
+ * By default, will set event's name and color fields to empty if the token is face-down
+ */
+static Event_CreateToken
+makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, bool revealFacedownInfo = false)
{
Event_CreateToken event;
event.set_zone_name(zone->getName().toStdString());
event.set_card_id(card->getId());
- event.set_card_name(card->getName().toStdString());
- event.set_card_provider_id(card->getProviderId().toStdString());
+ event.set_face_down(card->getFaceDown());
+
+ if (!card->getFaceDown() || revealFacedownInfo) {
+ event.set_card_name(card->getName().toStdString());
+ event.set_card_provider_id(card->getProviderId().toStdString());
+ }
+
event.set_color(card->getColor().toStdString());
event.set_pt(card->getPT().toStdString());
event.set_annotation(card->getAnnotation().toStdString());
@@ -401,7 +411,6 @@ static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card
event.set_y(yCoord);
return event;
}
-
static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr)
{
Event_AttachCard event;
@@ -1494,7 +1503,8 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
const QString cardName = nameFromStdString(cmd.card_name());
const QString cardProviderId = nameFromStdString(cmd.card_provider_id());
if (zone->hasCoords()) {
- xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, false);
+ bool dontStackSameName = cmd.face_down();
+ xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, dontStackSameName);
}
if (xCoord < 0) {
xCoord = 0;
@@ -1505,13 +1515,17 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
auto *card = new Server_Card(cardName, cardProviderId, newCardId(), xCoord, yCoord);
card->moveToThread(thread());
- card->setPT(nameFromStdString(cmd.pt()));
- card->setColor(nameFromStdString(cmd.color()));
+ // Client should already prevent face-down tokens from having attributes; this just an extra server-side check
+ if (!cmd.face_down()) {
+ card->setColor(nameFromStdString(cmd.color()));
+ card->setPT(nameFromStdString(cmd.pt()));
+ }
card->setAnnotation(nameFromStdString(cmd.annotation()));
card->setDestroyOnZoneChange(cmd.destroy_on_zone_change());
+ card->setFaceDown(cmd.face_down());
zone->insertCard(card, xCoord, yCoord);
- ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId);
+ sendCreateTokenEvents(zone, card, xCoord, yCoord, ges);
// check if the token is a replacement for an existing card
if (!targetCard) {
@@ -1642,6 +1656,38 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
return Response::RespOk;
}
+/**
+ * Creates and sends the events required to properly communicate the given token creation.
+ * Primarily written to handle creating face-down tokens.
+ */
+void Server_Player::sendCreateTokenEvents(Server_CardZone *zone,
+ Server_Card *card,
+ int xCoord,
+ int yCoord,
+ GameEventStorage &ges)
+{
+ // Token is not face-down; things are easy
+ if (!card->getFaceDown()) {
+ ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId);
+ return;
+ }
+
+ // Token is face-down. We have to send different info to each player
+ auto eventOthers = makeCreateTokenEvent(zone, card, xCoord, yCoord, false);
+ ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
+
+ auto eventPrivate = makeCreateTokenEvent(zone, card, xCoord, yCoord, true);
+ ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
+
+ // Event_CreateToken didn't use to have face_down field; send attribute event afterward for backwards compatibility
+ Event_SetCardAttr event;
+ event.set_zone_name(zone->getName().toStdString());
+ event.set_card_id(card->getId());
+ event.set_attribute(AttrFaceDown);
+ event.set_attr_value("1");
+ ges.enqueueGameEvent(event, playerId);
+}
+
Response::ResponseCode
Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
{
diff --git a/common/server_player.h b/common/server_player.h
index ffa12f8b0..b9d414482 100644
--- a/common/server_player.h
+++ b/common/server_player.h
@@ -84,6 +84,7 @@ private:
bool conceded;
bool sideboardLocked;
void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges);
+ void sendCreateTokenEvents(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, GameEventStorage &ges);
public:
mutable QMutex playerMutex;
From fc40fea97a2425b4a1eb4272c9b17e03a15981c2 Mon Sep 17 00:00:00 2001
From: tooomm
Date: Mon, 28 Apr 2025 06:31:00 +0200
Subject: [PATCH 07/19] Fix install Qt step (#5883)
---
.github/workflows/desktop-build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml
index fbb882aa2..df8ca010b 100644
--- a/.github/workflows/desktop-build.yml
+++ b/.github/workflows/desktop-build.yml
@@ -380,7 +380,7 @@ jobs:
uses: jurplel/install-qt-action@v4
with:
cache: true
- setup-python: false
+ setup-python: true
version: ${{matrix.qt_version}}
arch: win64_${{matrix.qt_arch}}
tools: ${{matrix.qt_tools}}
From 77d13090b57d1e5bcb6defc4057f3f9e347d7dc8 Mon Sep 17 00:00:00 2001
From: SlightlyCircuitous
<71394296+SlightlyCircuitous@users.noreply.github.com>
Date: Tue, 29 Apr 2025 22:18:18 -0400
Subject: [PATCH 08/19] Remove Fedora 40 build and Add Fedora 42 build (#5893)
* Create Fedora 42 Dockerfile
* Delete .ci/Fedora40 directory
* Update Fedora releases
* Update Fedora builds
---
.ci/{Fedora40 => Fedora42}/Dockerfile | 2 +-
.ci/release_template.md | 2 +-
.github/workflows/desktop-build.yml | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
rename .ci/{Fedora40 => Fedora42}/Dockerfile (95%)
diff --git a/.ci/Fedora40/Dockerfile b/.ci/Fedora42/Dockerfile
similarity index 95%
rename from .ci/Fedora40/Dockerfile
rename to .ci/Fedora42/Dockerfile
index 598bf7562..cf56ff604 100644
--- a/.ci/Fedora40/Dockerfile
+++ b/.ci/Fedora42/Dockerfile
@@ -1,4 +1,4 @@
-FROM fedora:40
+FROM fedora:42
RUN dnf install -y \
ccache \
diff --git a/.ci/release_template.md b/.ci/release_template.md
index c23e285c3..a7a2d31cb 100644
--- a/.ci/release_template.md
+++ b/.ci/release_template.md
@@ -23,8 +23,8 @@ Available pre-compiled binaries for installation:
• Ubuntu 20.04 LTS Focal Fossa
• Debian 12 Bookworm
• Debian 11 Bullseye
+ • Fedora 42
• Fedora 41
- • Fedora 40
We are also packaged in Arch Linux's official extra repository, courtesy of @FFY00.
General Linux support is available via a flatpak package at Flathub!
diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml
index df8ca010b..79bc1b815 100644
--- a/.github/workflows/desktop-build.yml
+++ b/.github/workflows/desktop-build.yml
@@ -101,12 +101,12 @@ jobs:
package: DEB
- distro: Fedora
- version: 40
+ version: 41
package: RPM
test: skip # Running tests on all distros is superfluous
- distro: Fedora
- version: 41
+ version: 42
package: RPM
- distro: Ubuntu
From 24e27d3c31c02bf42d0946ec6135994e469542f6 Mon Sep 17 00:00:00 2001
From: Basile Clement
Date: Fri, 2 May 2025 18:58:21 +0200
Subject: [PATCH 09/19] fix: Prevent dragged cards getting stuck (#5896)
* fix: Prevent dragged cards getting stuck
Update the position of the card even if it is not above any zone.
* Also update the currentZone
---
cockatrice/src/game/board/card_drag_item.cpp | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/cockatrice/src/game/board/card_drag_item.cpp b/cockatrice/src/game/board/card_drag_item.cpp
index d21e9168b..ab4431958 100644
--- a/cockatrice/src/game/board/card_drag_item.cpp
+++ b/cockatrice/src/game/board/card_drag_item.cpp
@@ -47,10 +47,25 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
cursorZone = zoneViewZone;
else if (cardZone)
cursorZone = cardZone;
- if (!cursorZone)
- return;
+
+ // Always update the current zone, even if its null, to cancel the drag
+ // instead of dropping cards into an non-intuitive location.
currentZone = cursorZone;
+ if (!cursorZone) {
+ // Avoid the cards getting stuck visually when not over
+ // any zone.
+ QPointF newPos = cursorScenePos - hotSpot;
+
+ if (newPos != pos()) {
+ for (int i = 0; i < childDrags.size(); i++)
+ childDrags[i]->setPos(newPos + childDrags[i]->getHotSpot());
+ setPos(newPos);
+ }
+
+ return;
+ }
+
QPointF zonePos = currentZone->scenePos();
QPointF cursorPosInZone = cursorScenePos - zonePos;
From 57c6f2716fe28c1d1d446f1232d35991badb037c Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Fri, 2 May 2025 10:00:32 -0700
Subject: [PATCH 10/19] Add setting for which deck editor tab to open deck in
(#5895)
* Create new setting
* Update settings dlg
* implement functionality
* Make setting into an enum
---
.../tabs/api/edhrec/tab_edhrec_main.cpp | 2 +-
cockatrice/src/client/tabs/tab_supervisor.cpp | 29 +++++++++++++++++--
cockatrice/src/client/tabs/tab_supervisor.h | 9 ++++++
.../tab_deck_storage_visual.cpp | 2 +-
cockatrice/src/dialogs/dlg_settings.cpp | 12 ++++++++
cockatrice/src/dialogs/dlg_settings.h | 2 ++
cockatrice/src/settings/cache_settings.cpp | 7 +++++
cockatrice/src/settings/cache_settings.h | 6 ++++
dbconverter/src/mocks.cpp | 3 ++
tests/carddatabase/mocks.cpp | 3 ++
10 files changed, 70 insertions(+), 5 deletions(-)
diff --git a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp
index 0bd4e843a..2eed749af 100644
--- a/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp
+++ b/cockatrice/src/client/tabs/api/edhrec/tab_edhrec_main.cpp
@@ -356,7 +356,7 @@ void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
{
EdhrecAverageDeckApiResponse deckData;
deckData.fromJson(reply);
- tabSupervisor->addVisualDeckEditorTab(deckData.deck.deckLoader);
+ tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader);
}
void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel)
diff --git a/cockatrice/src/client/tabs/tab_supervisor.cpp b/cockatrice/src/client/tabs/tab_supervisor.cpp
index 5d9600d11..4ed0e5a6b 100644
--- a/cockatrice/src/client/tabs/tab_supervisor.cpp
+++ b/cockatrice/src/client/tabs/tab_supervisor.cpp
@@ -590,7 +590,7 @@ void TabSupervisor::actTabDeckStorage(bool checked)
void TabSupervisor::openTabDeckStorage()
{
tabDeckStorage = new TabDeckStorage(this, client, userInfo);
- connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
+ connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tabDeckStorage, aTabDeckStorage);
connect(tabDeckStorage, &Tab::closed, this, [this] {
tabDeckStorage = nullptr;
@@ -691,7 +691,7 @@ void TabSupervisor::gameJoined(const Event_GameJoined &event)
auto *tab = new TabGame(this, QList() << client, event, roomGameTypes);
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openMessageDialog, this, &TabSupervisor::addMessageTab);
- connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
+ connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab);
@@ -701,7 +701,7 @@ void TabSupervisor::localGameJoined(const Event_GameJoined &event)
{
auto *tab = new TabGame(this, localClients, event, QMap());
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
- connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
+ connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab);
@@ -807,6 +807,29 @@ void TabSupervisor::talkLeft(TabMessage *tab)
removeTab(indexOf(tab));
}
+/**
+ * Creates a new deck editor tab and loads the deck into it.
+ * Creates either a classic or visual deck editor tab depending on settings
+ * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
+ */
+void TabSupervisor::openDeckInNewTab(const DeckLoader *deckToOpen)
+{
+ int type = SettingsCache::instance().getDefaultDeckEditorType();
+ switch (type) {
+ case ClassicDeckEditor:
+ addDeckEditorTab(deckToOpen);
+ break;
+ case VisualDeckEditor:
+ addVisualDeckEditorTab(deckToOpen);
+ break;
+ default:
+ qCWarning(TabSupervisorLog) << "Unknown DeckEditorType [" << type
+ << "]; opening ClassicDeckEditor as fallback";
+ addDeckEditorTab(deckToOpen);
+ break;
+ }
+}
+
/**
* Creates a new deck editor tab
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
diff --git a/cockatrice/src/client/tabs/tab_supervisor.h b/cockatrice/src/client/tabs/tab_supervisor.h
index 0ca13ac41..1c367043e 100644
--- a/cockatrice/src/client/tabs/tab_supervisor.h
+++ b/cockatrice/src/client/tabs/tab_supervisor.h
@@ -75,6 +75,14 @@ protected:
class TabSupervisor : public QTabWidget
{
Q_OBJECT
+
+public:
+ enum DeckEditorType
+ {
+ ClassicDeckEditor,
+ VisualDeckEditor
+ };
+
private:
ServerInfo_User *userInfo;
AbstractClient *client;
@@ -152,6 +160,7 @@ signals:
void showWindowIfHidden();
public slots:
+ void openDeckInNewTab(const DeckLoader *deckToOpen);
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen);
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();
diff --git a/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp b/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
index 38af03c59..65b4fcab0 100644
--- a/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
+++ b/cockatrice/src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
@@ -12,7 +12,7 @@
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
: Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this))
{
- connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::addVisualDeckEditorTab);
+ connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::openDeckInNewTab);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this,
&TabDeckStorageVisual::actOpenLocalDeck);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,
diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp
index 502662c14..cda737951 100644
--- a/cockatrice/src/dialogs/dlg_settings.cpp
+++ b/cockatrice/src/dialogs/dlg_settings.cpp
@@ -4,6 +4,7 @@
#include "../client/network/release_channel.h"
#include "../client/network/spoiler_background_updater.h"
#include "../client/sound_engine.h"
+#include "../client/tabs/tab_supervisor.h"
#include "../client/ui/picture_loader/picture_loader.h"
#include "../client/ui/theme_manager.h"
#include "../deck/custom_line_edit.h"
@@ -699,12 +700,20 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
index == visualDeckStoragePromptForConversionIndexAlways);
});
+ defaultDeckEditorTypeSelector.addItem(""); // these will be set in retranslateUI
+ defaultDeckEditorTypeSelector.addItem("");
+ defaultDeckEditorTypeSelector.setCurrentIndex(SettingsCache::instance().getDefaultDeckEditorType());
+ connect(&defaultDeckEditorTypeSelector, QOverload::of(&QComboBox::currentIndexChanged),
+ &SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType);
+
auto *deckEditorGrid = new QGridLayout;
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
+ deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
+ deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
deckEditorGroupBox = new QGroupBox;
deckEditorGroupBox->setLayout(deckEditorGrid);
@@ -774,6 +783,9 @@ void UserInterfaceSettingsPage::retranslateUi()
tr("ask to convert to .cod"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways,
tr("always convert to .cod"));
+ defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
+ defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
+ defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
replayGroupBox->setTitle(tr("Replay settings"));
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
rewindBufferingMsBox.setSuffix(" ms");
diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h
index 6c9b8b062..3975f8519 100644
--- a/cockatrice/src/dialogs/dlg_settings.h
+++ b/cockatrice/src/dialogs/dlg_settings.h
@@ -155,6 +155,8 @@ private:
QComboBox visualDeckStoragePromptForConversionSelector;
QCheckBox visualDeckStorageInGameCheckBox;
QCheckBox visualDeckStorageSelectionAnimationCheckBox;
+ QLabel defaultDeckEditorTypeLabel;
+ QComboBox defaultDeckEditorTypeSelector;
QLabel rewindBufferingMsLabel;
QSpinBox rewindBufferingMsBox;
QGroupBox *generalGroupBox;
diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp
index ba25590f3..395354751 100644
--- a/cockatrice/src/settings/cache_settings.cpp
+++ b/cockatrice/src/settings/cache_settings.cpp
@@ -285,6 +285,7 @@ SettingsCache::SettingsCache()
visualDeckStorageInGame = settings->value("interface/visualdeckstorageingame", true).toBool();
visualDeckStorageSelectionAnimation =
settings->value("interface/visualdeckstorageselectionanimation", true).toBool();
+ defaultDeckEditorType = settings->value("interface/defaultDeckEditorType", 1).toInt();
visualDatabaseDisplayFilterToMostRecentSetsEnabled =
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", true).toBool();
visualDatabaseDisplayFilterToMostRecentSetsAmount =
@@ -791,6 +792,12 @@ void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T va
emit visualDeckStorageSelectionAnimationChanged(visualDeckStorageSelectionAnimation);
}
+void SettingsCache::setDefaultDeckEditorType(int value)
+{
+ defaultDeckEditorType = value;
+ settings->setValue("interface/defaultDeckEditorType", defaultDeckEditorType);
+}
+
void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled)
{
visualDatabaseDisplayFilterToMostRecentSetsEnabled = _enabled;
diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h
index 78993ce4a..c922bb9e1 100644
--- a/cockatrice/src/settings/cache_settings.h
+++ b/cockatrice/src/settings/cache_settings.h
@@ -153,6 +153,7 @@ private:
bool visualDeckStorageAlwaysConvert;
bool visualDeckStorageInGame;
bool visualDeckStorageSelectionAnimation;
+ int defaultDeckEditorType;
bool visualDatabaseDisplayFilterToMostRecentSetsEnabled;
int visualDatabaseDisplayFilterToMostRecentSetsAmount;
bool horizontalHand;
@@ -488,6 +489,10 @@ public:
{
return visualDeckStorageSelectionAnimation;
}
+ int getDefaultDeckEditorType() const
+ {
+ return defaultDeckEditorType;
+ }
bool getVisualDatabaseDisplayFilterToMostRecentSetsEnabled() const
{
return visualDatabaseDisplayFilterToMostRecentSetsEnabled;
@@ -839,6 +844,7 @@ public slots:
void setVisualDeckStorageAlwaysConvert(bool _visualDeckStorageAlwaysConvert);
void setVisualDeckStorageInGame(QT_STATE_CHANGED_T value);
void setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T value);
+ void setDefaultDeckEditorType(int value);
void setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled);
void setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int _amount);
void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand);
diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp
index 93048d3cf..004bf1ab1 100644
--- a/dbconverter/src/mocks.cpp
+++ b/dbconverter/src/mocks.cpp
@@ -256,6 +256,9 @@ void SettingsCache::setVisualDeckStorageInGame(QT_STATE_CHANGED_T /* value */)
void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */)
{
}
+void SettingsCache::setDefaultDeckEditorType(int /* value */)
+{
+}
void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */)
{
}
diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp
index a2ca73fb1..4ad635949 100644
--- a/tests/carddatabase/mocks.cpp
+++ b/tests/carddatabase/mocks.cpp
@@ -260,6 +260,9 @@ void SettingsCache::setVisualDeckStorageInGame(QT_STATE_CHANGED_T /* value */)
void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */)
{
}
+void SettingsCache::setDefaultDeckEditorType(int /* value */)
+{
+}
void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */)
{
}
From 700feb68afb566e1df8eb1bfc9aa433847d28282 Mon Sep 17 00:00:00 2001
From: BruebachL <44814898+BruebachL@users.noreply.github.com>
Date: Fri, 2 May 2025 20:34:41 +0200
Subject: [PATCH 11/19] Don't require .svg for mana symbols. (#5897)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Lukas Brübach
---
.../ui/widgets/cards/additional_info/mana_symbol_widget.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cockatrice/src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp b/cockatrice/src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp
index e42e93444..b17b0ff47 100644
--- a/cockatrice/src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp
+++ b/cockatrice/src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp
@@ -66,7 +66,7 @@ void ManaSymbolWidget::loadManaIcon()
QString filename = "theme:icons/mana/";
if (symbol == "W" || symbol == "U" || symbol == "B" || symbol == "R" || symbol == "G") {
- filename += symbol + ".svg";
+ filename += symbol;
}
manaIcon = QPixmap(filename);
From baa7e25e30428dade3a39f62c70864ed996b162f Mon Sep 17 00:00:00 2001
From: Sebastian Di Luzio
Date: Sat, 3 May 2025 00:10:09 +0200
Subject: [PATCH 12/19] feat: build and release docker images using github
actions and container registry (#5807)
* feat: build and release docker images using github cicd
* fix: attempt to publish to specific image name
* fix: typo in pipeline step
* typo
* typo
* limit to certain paths for PRs & naming
* ci: configure image title and url
* docker: include only necessary files and directories
this should make caching more powerful
* docker: reorder COPY with best guess of what changes least
* build(docker): remove seemingly unnecessary files
* fix: clean up docker metadata
remove annotations, it seems they're applied from the labels already, add description
* fix(ci): add back docker image annotations
* Update desktop-build.yml
* Update desktop-lint.yml
* Update desktop-build.yml
* Update docker-release.yml
* fix: remove run on master and add affected files to PR trigger
* metadata
* ci: run pipeline on main
this will ensure the container can always build and keep caches ready for release. push should only happen on tag triggers
It also removes some files from the PR trigger that should never break the build, and would just invalidate cache.
* Update docker-release.yml
---------
Co-authored-by: tooomm
---
.github/workflows/desktop-build.yml | 1 +
.github/workflows/desktop-lint.yml | 1 +
.github/workflows/docker-release.yml | 71 ++++++++++++++++++++++++++++
Dockerfile | 6 ++-
README.md | 1 -
5 files changed, 78 insertions(+), 2 deletions(-)
create mode 100644 .github/workflows/docker-release.yml
diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml
index 79bc1b815..4d406c1b6 100644
--- a/.github/workflows/desktop-build.yml
+++ b/.github/workflows/desktop-build.yml
@@ -9,6 +9,7 @@ on:
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
+ - '.github/workflows/docker-release.yml'
tags:
- '*'
pull_request:
diff --git a/.github/workflows/desktop-lint.yml b/.github/workflows/desktop-lint.yml
index 4ebfd9b79..aee6e81d5 100644
--- a/.github/workflows/desktop-lint.yml
+++ b/.github/workflows/desktop-lint.yml
@@ -7,6 +7,7 @@ on:
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
+ - '.github/workflows/docker-release.yml'
jobs:
format:
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
new file mode 100644
index 000000000..439ea24cd
--- /dev/null
+++ b/.github/workflows/docker-release.yml
@@ -0,0 +1,71 @@
+name: Build Docker Container
+
+on:
+ push:
+ tags:
+ - '*Release*'
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '.github/workflows/docker-release.yml'
+ - 'CMakeLists.txt'
+ - 'Dockerfile'
+ - 'servatrice/**'
+ - 'common/**'
+ - 'cmake/**'
+ - '!**.md'
+
+jobs:
+ docker:
+ name: amd64 & arm64
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Docker metadata
+ id: metadata
+ uses: docker/metadata-action@v5
+ with:
+ images: |
+ ghcr.io/cockatrice/servatrice
+ labels: |
+ org.opencontainers.image.title=Servatrice
+ org.opencontainers.image.url=https://cockatrice.github.io/
+ org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
+ annotations: |
+ org.opencontainers.image.title=Servatrice
+ org.opencontainers.image.url=https://cockatrice.github.io/
+ org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to GitHub Container Registry
+ if: github.ref_type == 'tag'
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ github.token }}
+
+ - name: Build and push Docker Image
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ platforms: linux/amd64,linux/arm64
+ push: ${{ github.ref_type == 'tag' }}
+ tags: ${{ steps.metadata.outputs.tags }}
+ labels: ${{ steps.metadata.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
diff --git a/Dockerfile b/Dockerfile
index 330c51b4a..2a0d10eba 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,7 +16,11 @@ RUN apt-get update && apt-get install -y\
qt6-tools-dev \
qt6-tools-dev-tools
-COPY . /home/servatrice/code/
+COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
+COPY ./cmake /home/servatrice/code/cmake
+COPY ./common /home/servatrice/code/common
+COPY ./servatrice /home/servatrice/code/servatrice
+
WORKDIR /home/servatrice/code
WORKDIR build
diff --git a/README.md b/README.md
index 0a8058bba..48cacff78 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,6 @@ The following flags (with their non-default values) can be passed to `cmake`:
# Run
-
Cockatrice is the game client
Oracle fetches card data
Servatrice is the server
From 9ae6357c349d7d459b442b238d0cbb94a18a0934 Mon Sep 17 00:00:00 2001
From: BruebachL <44814898+BruebachL@users.noreply.github.com>
Date: Sun, 4 May 2025 02:03:53 +0200
Subject: [PATCH 13/19] Properly manage hover-zoom child widget in
CardInfoPictureWidget destructor. (#5900)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Lukas Brübach
---
.../ui/widgets/cards/card_info_picture_widget.cpp | 12 ++++++++++++
.../ui/widgets/cards/card_info_picture_widget.h | 2 ++
2 files changed, 14 insertions(+)
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp
index 4ee3de5eb..5e275f8e9 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp
@@ -64,6 +64,12 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT
});
}
+CardInfoPictureWidget::~CardInfoPictureWidget()
+{
+ enlargedPixmapWidget->hide();
+ enlargedPixmapWidget->deleteLater();
+}
+
/**
* @brief Sets the card to be displayed and updates the pixmap.
* @param card A shared pointer to the card information (CardInfoPtr).
@@ -341,6 +347,12 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
emit cardClicked();
}
+void CardInfoPictureWidget::hideEvent(QHideEvent *event)
+{
+ enlargedPixmapWidget->hide();
+ QWidget::hideEvent(event);
+}
+
QMenu *CardInfoPictureWidget::createRightClickMenu()
{
auto *cardMenu = new QMenu(this);
diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h
index 10cc422aa..79de74526 100644
--- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h
+++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.h
@@ -21,6 +21,7 @@ public:
explicit CardInfoPictureWidget(QWidget *parent = nullptr,
bool hoverToZoomEnabled = false,
bool raiseOnEnter = false);
+ ~CardInfoPictureWidget();
CardInfoPtr getInfo()
{
return info;
@@ -52,6 +53,7 @@ protected:
void moveEvent(QMoveEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
+ void hideEvent(QHideEvent *event) override;
void loadPixmap();
[[nodiscard]] const QPixmap &getResizedPixmap() const
{
From 2687a3401944e5215b6a19d4b39bb0189aa0f757 Mon Sep 17 00:00:00 2001
From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com>
Date: Sun, 4 May 2025 13:02:11 +0000
Subject: [PATCH 14/19] ci: temporarily remove run-vcpkg step (#5902)
There is an issue with run-vcpkg GHA not caching properly. This ends up wasting 20 minutes of redundant vcpkg depency compilation.
See https://github.com/lukka/run-vcpkg/issues/243
---
.github/workflows/desktop-build.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml
index 4d406c1b6..bd7fd37e2 100644
--- a/.github/workflows/desktop-build.yml
+++ b/.github/workflows/desktop-build.yml
@@ -387,7 +387,9 @@ jobs:
tools: ${{matrix.qt_tools}}
modules: ${{matrix.qt_modules}}
- - name: Run vcpkg
+ # TODO: re-enable when https://github.com/lukka/run-vcpkg/issues/243 is fixed
+ - if: false
+ name: Run vcpkg (disabled)
uses: lukka/run-vcpkg@v11
with:
runVcpkgInstall: true
From 69107f79e39e8446a9f7a740e661cc05f9442541 Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Sun, 4 May 2025 17:09:11 -0700
Subject: [PATCH 15/19] Add setting to auto focus search bar when opening card
view window (#5906)
* add new setting
* implement thing
* Rename setting
* fix build failure
---
cockatrice/src/dialogs/dlg_settings.cpp | 10 ++++++++--
cockatrice/src/dialogs/dlg_settings.h | 1 +
cockatrice/src/game/zones/view_zone_widget.cpp | 5 +++++
cockatrice/src/settings/cache_settings.cpp | 7 +++++++
cockatrice/src/settings/cache_settings.h | 6 ++++++
dbconverter/src/mocks.cpp | 3 +++
tests/carddatabase/mocks.cpp | 3 +++
7 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp
index cda737951..63901325f 100644
--- a/cockatrice/src/dialogs/dlg_settings.cpp
+++ b/cockatrice/src/dialogs/dlg_settings.cpp
@@ -630,6 +630,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setCloseEmptyCardView);
+ focusCardViewSearchBarCheckBox.setChecked(SettingsCache::instance().getFocusCardViewSearchBar());
+ connect(&focusCardViewSearchBarCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
+ &SettingsCache::setFocusCardViewSearchBar);
+
annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens());
connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setAnnotateTokens);
@@ -643,8 +647,9 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0);
generalGrid->addWidget(&playToStackCheckBox, 2, 0);
generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0);
- generalGrid->addWidget(&annotateTokensCheckBox, 4, 0);
- generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0);
+ generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 4, 0);
+ generalGrid->addWidget(&annotateTokensCheckBox, 5, 0);
+ generalGrid->addWidget(&useTearOffMenusCheckBox, 6, 0);
generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid);
@@ -763,6 +768,7 @@ void UserInterfaceSettingsPage::retranslateUi()
clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)"));
playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default"));
closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed"));
+ focusCardViewSearchBarCheckBox.setText(tr("Auto focus search bar when card view window is opened"));
annotateTokensCheckBox.setText(tr("Annotate card text on tokens"));
useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen"));
notificationsGroupBox->setTitle(tr("Notifications settings"));
diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h
index 3975f8519..54b2cbcd8 100644
--- a/cockatrice/src/dialogs/dlg_settings.h
+++ b/cockatrice/src/dialogs/dlg_settings.h
@@ -147,6 +147,7 @@ private:
QCheckBox clickPlaysAllSelectedCheckBox;
QCheckBox playToStackCheckBox;
QCheckBox closeEmptyCardViewCheckBox;
+ QCheckBox focusCardViewSearchBarCheckBox;
QCheckBox annotateTokensCheckBox;
QCheckBox useTearOffMenusCheckBox;
QCheckBox tapAnimationCheckBox;
diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp
index 1721831ac..f0430f549 100644
--- a/cockatrice/src/game/zones/view_zone_widget.cpp
+++ b/cockatrice/src/game/zones/view_zone_widget.cpp
@@ -56,6 +56,11 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(&searchEdit); });
+ if (SettingsCache::instance().getFocusCardViewSearchBar()) {
+ this->setActive(true);
+ searchEdit.setFocus();
+ }
+
QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget;
searchEditProxy->setWidget(&searchEdit);
searchEditProxy->setZValue(2000000007);
diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp
index 395354751..6507780fe 100644
--- a/cockatrice/src/settings/cache_settings.cpp
+++ b/cockatrice/src/settings/cache_settings.cpp
@@ -251,6 +251,7 @@ SettingsCache::SettingsCache()
cardViewInitialRowsMax = settings->value("interface/cardViewInitialRowsMax", 14).toInt();
cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt();
closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool();
+ focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool();
showShortcuts = settings->value("menu/showshortcuts", true).toBool();
displayCardNames = settings->value("cards/displaycardnames", true).toBool();
@@ -370,6 +371,12 @@ void SettingsCache::setCloseEmptyCardView(QT_STATE_CHANGED_T value)
settings->setValue("interface/closeEmptyCardView", closeEmptyCardView);
}
+void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T value)
+{
+ focusCardViewSearchBar = value;
+ settings->setValue("interface/focusCardViewSearchBar", focusCardViewSearchBar);
+}
+
void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures)
{
knownMissingFeatures = _knownMissingFeatures;
diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h
index c922bb9e1..bb4bed95f 100644
--- a/cockatrice/src/settings/cache_settings.h
+++ b/cockatrice/src/settings/cache_settings.h
@@ -184,6 +184,7 @@ private:
int cardViewInitialRowsMax;
int cardViewExpandedRowsMax;
bool closeEmptyCardView;
+ bool focusCardViewSearchBar;
int pixmapCacheSize;
int networkCacheSize;
int redirectCacheTtl;
@@ -699,6 +700,7 @@ public:
void setCardViewInitialRowsMax(int _cardViewInitialRowsMax);
void setCardViewExpandedRowsMax(int value);
void setCloseEmptyCardView(QT_STATE_CHANGED_T value);
+ void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value);
QString getClientID()
{
return clientID;
@@ -727,6 +729,10 @@ public:
{
return closeEmptyCardView;
}
+ bool getFocusCardViewSearchBar() const
+ {
+ return focusCardViewSearchBar;
+ }
ShortcutsSettings &shortcuts() const
{
return *shortcutsSettings;
diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp
index 004bf1ab1..890294870 100644
--- a/dbconverter/src/mocks.cpp
+++ b/dbconverter/src/mocks.cpp
@@ -64,6 +64,9 @@ void SettingsCache::setCardViewExpandedRowsMax(int /* value */)
void SettingsCache::setCloseEmptyCardView(const QT_STATE_CHANGED_T /* value */)
{
}
+void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T /* value */)
+{
+}
void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */)
{
}
diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp
index 4ad635949..b34c28727 100644
--- a/tests/carddatabase/mocks.cpp
+++ b/tests/carddatabase/mocks.cpp
@@ -68,6 +68,9 @@ void SettingsCache::setCardViewExpandedRowsMax(int /* value */)
void SettingsCache::setCloseEmptyCardView(QT_STATE_CHANGED_T /* value */)
{
}
+void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T /* value */)
+{
+}
void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */)
{
}
From bd8306bd33d1b77844d47614c9daea7d3d1ec14a Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Mon, 5 May 2025 06:38:46 -0700
Subject: [PATCH 16/19] Strip color escape codes in Debug Log window (#5915)
---
cockatrice/src/dialogs/dlg_view_log.cpp | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/cockatrice/src/dialogs/dlg_view_log.cpp b/cockatrice/src/dialogs/dlg_view_log.cpp
index dc569c92c..7b4e8c617 100644
--- a/cockatrice/src/dialogs/dlg_view_log.cpp
+++ b/cockatrice/src/dialogs/dlg_view_log.cpp
@@ -4,6 +4,7 @@
#include "../utility/logger.h"
#include
+#include
#include
DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
@@ -44,7 +45,12 @@ void DlgViewLog::loadInitialLogBuffer()
void DlgViewLog::logEntryAdded(QString message)
{
- logArea->appendPlainText(message);
+ static auto colorEscapeCodePattern = QRegularExpression("\033\\[\\d+m");
+
+ QString sanitizedMessage = message;
+ sanitizedMessage.replace(colorEscapeCodePattern, "");
+
+ logArea->appendPlainText(sanitizedMessage);
}
void DlgViewLog::closeEvent(QCloseEvent * /* event */)
From 4a54412d47e9b7811d2039ac63112c9011314dd2 Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Mon, 5 May 2025 06:46:29 -0700
Subject: [PATCH 17/19] Pass log messages by const ref (#5914)
* Pass log messages by const ref
* Rename method
---
cockatrice/src/dialogs/dlg_view_log.cpp | 6 +++---
cockatrice/src/dialogs/dlg_view_log.h | 2 +-
cockatrice/src/utility/logger.cpp | 4 ++--
cockatrice/src/utility/logger.h | 6 +++---
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/cockatrice/src/dialogs/dlg_view_log.cpp b/cockatrice/src/dialogs/dlg_view_log.cpp
index 7b4e8c617..35accf80f 100644
--- a/cockatrice/src/dialogs/dlg_view_log.cpp
+++ b/cockatrice/src/dialogs/dlg_view_log.cpp
@@ -28,7 +28,7 @@ DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
resize(800, 500);
loadInitialLogBuffer();
- connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::logEntryAdded);
+ connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::appendLogEntry);
}
void DlgViewLog::actCheckBoxChanged(bool abNewValue)
@@ -40,10 +40,10 @@ void DlgViewLog::loadInitialLogBuffer()
{
QList logBuffer = Logger::getInstance().getLogBuffer();
for (const QString &message : logBuffer)
- logEntryAdded(message);
+ appendLogEntry(message);
}
-void DlgViewLog::logEntryAdded(QString message)
+void DlgViewLog::appendLogEntry(const QString &message)
{
static auto colorEscapeCodePattern = QRegularExpression("\033\\[\\d+m");
diff --git a/cockatrice/src/dialogs/dlg_view_log.h b/cockatrice/src/dialogs/dlg_view_log.h
index 3845f0062..870c23fc5 100644
--- a/cockatrice/src/dialogs/dlg_view_log.h
+++ b/cockatrice/src/dialogs/dlg_view_log.h
@@ -22,7 +22,7 @@ private:
void loadInitialLogBuffer();
private slots:
- void logEntryAdded(QString message);
+ void appendLogEntry(const QString &message);
void actCheckBoxChanged(bool abNewValue);
};
diff --git a/cockatrice/src/utility/logger.cpp b/cockatrice/src/utility/logger.cpp
index 74ed90597..0184146ae 100644
--- a/cockatrice/src/utility/logger.cpp
+++ b/cockatrice/src/utility/logger.cpp
@@ -81,12 +81,12 @@ void Logger::closeLogfileSession()
fileHandle.close();
}
-void Logger::log(QtMsgType /* type */, const QMessageLogContext & /* ctx */, const QString message)
+void Logger::log(QtMsgType /* type */, const QMessageLogContext & /* ctx */, const QString &message)
{
QMetaObject::invokeMethod(this, "internalLog", Qt::QueuedConnection, Q_ARG(const QString &, message));
}
-void Logger::internalLog(QString message)
+void Logger::internalLog(const QString &message)
{
QMutexLocker locker(&mutex);
diff --git a/cockatrice/src/utility/logger.h b/cockatrice/src/utility/logger.h
index 246e9204a..b90644b36 100644
--- a/cockatrice/src/utility/logger.h
+++ b/cockatrice/src/utility/logger.h
@@ -28,7 +28,7 @@ public:
}
void logToFile(bool enabled);
- void log(QtMsgType type, const QMessageLogContext &ctx, QString message);
+ void log(QtMsgType type, const QMessageLogContext &ctx, const QString &message);
QString getClientVersion();
QString getClientOperatingSystem();
QString getSystemArchitecture();
@@ -57,10 +57,10 @@ protected:
void closeLogfileSession();
protected slots:
- void internalLog(QString message);
+ void internalLog(const QString &message);
signals:
- void logEntryAdded(QString message);
+ void logEntryAdded(const QString &message);
};
#endif
From 29d93fb9c1ad08dab9aeef135c1f581466ba2214 Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Mon, 5 May 2025 06:50:34 -0700
Subject: [PATCH 18/19] Delete CardDragItem when referenced CardItem is
destroyed (#5911)
---
cockatrice/src/game/board/abstract_card_drag_item.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/cockatrice/src/game/board/abstract_card_drag_item.cpp b/cockatrice/src/game/board/abstract_card_drag_item.cpp
index 0c7acd917..a7234c3d4 100644
--- a/cockatrice/src/game/board/abstract_card_drag_item.cpp
+++ b/cockatrice/src/game/board/abstract_card_drag_item.cpp
@@ -39,6 +39,8 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
prepareGeometryChange();
update();
});
+
+ connect(item, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater);
}
AbstractCardDragItem::~AbstractCardDragItem()
From a07c1badd8e72a58add5a3cc683f20209f3dd25a Mon Sep 17 00:00:00 2001
From: RickyRister <42636155+RickyRister@users.noreply.github.com>
Date: Mon, 5 May 2025 08:45:22 -0700
Subject: [PATCH 19/19] Add "copy to clipboard" button to Debug Log window
(#5913)
---
cockatrice/src/dialogs/dlg_view_log.cpp | 25 +++++++++++++++++++++++--
cockatrice/src/dialogs/dlg_view_log.h | 2 ++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/cockatrice/src/dialogs/dlg_view_log.cpp b/cockatrice/src/dialogs/dlg_view_log.cpp
index 35accf80f..b60a9767b 100644
--- a/cockatrice/src/dialogs/dlg_view_log.cpp
+++ b/cockatrice/src/dialogs/dlg_view_log.cpp
@@ -3,24 +3,40 @@
#include "../settings/cache_settings.h"
#include "../utility/logger.h"
+#include
#include
+#include
#include
#include
DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
{
-
logArea = new QPlainTextEdit;
logArea->setReadOnly(true);
auto *mainLayout = new QVBoxLayout;
+ mainLayout->setSpacing(3);
+ mainLayout->setContentsMargins(20, 20, 20, 6);
+
mainLayout->addWidget(logArea);
+ auto *bottomLayout = new QHBoxLayout;
+
coClearLog = new QCheckBox;
coClearLog->setText(tr("Clear log when closing"));
coClearLog->setChecked(SettingsCache::instance().servers().getClearDebugLogStatus(false));
connect(coClearLog, &QCheckBox::toggled, this, &DlgViewLog::actCheckBoxChanged);
- mainLayout->addWidget(coClearLog);
+
+ copyToClipboardButton = new QPushButton;
+ copyToClipboardButton->setText(tr("Copy to clipboard"));
+ copyToClipboardButton->setAutoDefault(false);
+ connect(copyToClipboardButton, &QPushButton::clicked, this, &DlgViewLog::actCopyToClipboard);
+
+ bottomLayout->addWidget(coClearLog);
+ bottomLayout->addStretch();
+ bottomLayout->addWidget(copyToClipboardButton);
+
+ mainLayout->addLayout(bottomLayout);
setLayout(mainLayout);
@@ -36,6 +52,11 @@ void DlgViewLog::actCheckBoxChanged(bool abNewValue)
SettingsCache::instance().servers().setClearDebugLogStatus(abNewValue);
}
+void DlgViewLog::actCopyToClipboard()
+{
+ QApplication::clipboard()->setText(logArea->toPlainText());
+}
+
void DlgViewLog::loadInitialLogBuffer()
{
QList logBuffer = Logger::getInstance().getLogBuffer();
diff --git a/cockatrice/src/dialogs/dlg_view_log.h b/cockatrice/src/dialogs/dlg_view_log.h
index 870c23fc5..26d6ad5e1 100644
--- a/cockatrice/src/dialogs/dlg_view_log.h
+++ b/cockatrice/src/dialogs/dlg_view_log.h
@@ -19,11 +19,13 @@ protected:
private:
QPlainTextEdit *logArea;
QCheckBox *coClearLog;
+ QPushButton *copyToClipboardButton;
void loadInitialLogBuffer();
private slots:
void appendLogEntry(const QString &message);
void actCheckBoxChanged(bool abNewValue);
+ void actCopyToClipboard();
};
#endif
\ No newline at end of file