Cockatrice/cockatrice/src/game/player/menu/player_menu.cpp
DawnFire42 f8b06f28d1
refactor: extract AbstractPlayerComponent interface for polymorphic player component management.
Non-QObject polymorphic interface with setShortcutsActive(), setShortcutsInactive(), and retranslateUi(). Uses regular multiple inheritance to avoid diamond inheritance with Qt's MOC.

All zone menus, SayMenu, and AbstractCounter implement this interface. PlayerMenu manages them via a managedComponents list with two template helpers (addManagedMenu/registerManagedComponent), replacing individual if-guarded lifecycle calls with a single polymorphic loop.

SayMenu now owns its shortcut and translation lifecycle instead of having PlayerMenu manage its title and shortcuts externally.
Counters are iterated via Player::getCounters() rather than managedComponents to avoid duplicating the authoritative owner's map.
2026-03-17 00:24:04 -04:00

149 lines
4.7 KiB
C++

#include "player_menu.h"
#include "../../../interface/widgets/tabs/tab_game.h"
#include "../../board/card_item.h"
#include "../../zones/hand_zone.h"
#include "../../zones/pile_zone.h"
#include "../../zones/table_zone.h"
#include "card_menu.h"
#include "hand_menu.h"
#include <libcockatrice/protocol/pb/command_reveal_cards.pb.h>
PlayerMenu::PlayerMenu(Player *_player) : player(_player)
{
playerMenu = new TearOffMenu();
if (player->getPlayerInfo()->getLocalOrJudge()) {
handMenu = addManagedMenu<HandMenu>(player, player->getPlayerActions(), playerMenu);
libraryMenu = addManagedMenu<LibraryMenu>(player, playerMenu);
} else {
handMenu = nullptr;
libraryMenu = nullptr;
}
graveMenu = addManagedMenu<GraveyardMenu>(player, playerMenu);
rfgMenu = addManagedMenu<RfgMenu>(player, playerMenu);
if (player->getPlayerInfo()->getLocalOrJudge()) {
sideboardMenu = addManagedMenu<SideboardMenu>(player, playerMenu);
customZonesMenu = addManagedMenu<CustomZoneMenu>(player);
playerMenu->addSeparator();
countersMenu = playerMenu->addMenu(QString());
utilityMenu = createManagedComponent<UtilityMenu>(player, playerMenu);
} else {
sideboardMenu = nullptr;
customZonesMenu = nullptr;
countersMenu = nullptr;
utilityMenu = nullptr;
}
if (player->getPlayerInfo()->getLocal()) {
sayMenu = addManagedMenu<SayMenu>(player);
} else {
sayMenu = nullptr;
}
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
&PlayerMenu::refreshShortcuts);
refreshShortcuts();
retranslateUi();
}
void PlayerMenu::setMenusForGraphicItems()
{
player->getGraphicsItem()->getTableZoneGraphicsItem()->setMenu(playerMenu);
player->getGraphicsItem()->getGraveyardZoneGraphicsItem()->setMenu(graveMenu, graveMenu->aViewGraveyard);
player->getGraphicsItem()->getRfgZoneGraphicsItem()->setMenu(rfgMenu, rfgMenu->aViewRfg);
if (player->getPlayerInfo()->getLocalOrJudge()) {
player->getGraphicsItem()->getHandZoneGraphicsItem()->setMenu(handMenu);
player->getGraphicsItem()->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard);
player->getGraphicsItem()->getSideboardZoneGraphicsItem()->setMenu(sideboardMenu);
}
}
QMenu *PlayerMenu::updateCardMenu(const CardItem *card)
{
if (!card) {
emit cardMenuUpdated(nullptr);
return nullptr;
}
// If is spectator (as spectators don't need card menus), return
// only update the menu if the card is actually selected
if ((player->getGame()->getPlayerManager()->isSpectator() && !player->getGame()->getPlayerManager()->isJudge()) ||
player->getGame()->getActiveCard() != card) {
return nullptr;
}
QMenu *menu = new CardMenu(player, card, shortcutsActive);
emit cardMenuUpdated(menu);
return menu;
}
void PlayerMenu::retranslateUi()
{
playerMenu->setTitle(tr("Player \"%1\"").arg(player->getPlayerInfo()->getName()));
for (auto *component : managedComponents) {
component->retranslateUi();
}
if (countersMenu) {
countersMenu->setTitle(tr("&Counters"));
}
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
while (counterIterator.hasNext()) {
counterIterator.next().value()->retranslateUi();
}
}
void PlayerMenu::refreshShortcuts()
{
if (shortcutsActive) {
// Judges get access to every player's menus but only want shortcuts to be set for their own.
if (player->getPlayerInfo()->getLocalOrJudge() && !player->getPlayerInfo()->getLocal()) {
setShortcutsInactive();
} else {
setShortcutsActive();
}
} else {
setShortcutsInactive();
}
}
void PlayerMenu::setShortcutsActive()
{
shortcutsActive = true;
for (auto *component : managedComponents) {
component->setShortcutsActive();
}
// Counters implement AbstractPlayerComponent but are iterated via Player::counters
// (the authoritative source) rather than managedComponents to avoid a redundant
// list that must stay in sync with the map.
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
while (counterIterator.hasNext()) {
counterIterator.next().value()->setShortcutsActive();
}
}
void PlayerMenu::setShortcutsInactive()
{
shortcutsActive = false;
for (auto *component : managedComponents) {
component->setShortcutsInactive();
}
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
while (counterIterator.hasNext()) {
counterIterator.next().value()->setShortcutsInactive();
}
}