diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 60ba06593..166b807d9 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -83,6 +83,7 @@ set(cockatrice_SOURCES src/game/game_state.cpp src/game_graphics/game_view.cpp src/game_graphics/hand_counter.cpp + src/game/selection_subtype_tally.cpp src/game_graphics/log/message_log_widget.cpp src/game/phase.cpp src/game_graphics/phases_toolbar.cpp @@ -98,8 +99,6 @@ set(cockatrice_SOURCES src/game_graphics/player/menu/say_menu.cpp src/game_graphics/player/menu/sideboard_menu.cpp src/game_graphics/player/menu/utility_menu.cpp - src/game_graphics/tally/subtype_tally.cpp - src/game_graphics/tally/tally.cpp src/game/player/player_actions.cpp src/game_graphics/player/player_area.cpp src/game_graphics/player/player_dialogs.cpp diff --git a/cockatrice/src/game_graphics/tally/subtype_tally.cpp b/cockatrice/src/game/selection_subtype_tally.cpp similarity index 71% rename from cockatrice/src/game_graphics/tally/subtype_tally.cpp rename to cockatrice/src/game/selection_subtype_tally.cpp index 804443b15..e9f87fab9 100644 --- a/cockatrice/src/game_graphics/tally/subtype_tally.cpp +++ b/cockatrice/src/game/selection_subtype_tally.cpp @@ -1,6 +1,6 @@ -#include "subtype_tally.h" +#include "selection_subtype_tally.h" -#include "../board/card_item.h" +#include "../game_graphics/board/card_item.h" #include #include @@ -19,19 +19,12 @@ QStringList extractSubtypesFromFace(const QString &faceType) return {}; } -/** @brief A single subtype (e.g., "Goblin", "Warrior") with its occurrence count. */ -struct SubtypeEntry -{ - QString name; - int count; -}; - } // anonymous namespace -namespace SubtypeTally +namespace SelectionSubtypeTally { -QList countSubtypes(const QList &cards) +QList countSubtypes(const QList &cards) { QMap subtypeCounts; @@ -65,13 +58,7 @@ QList countSubtypes(const QList &cards) return a.name < b.name; }); - // convert entries into TallyRows - QList rows; - rows.reserve(entries.size()); // for backwards compatibility with Qt5 - std::transform(entries.begin(), entries.end(), std::back_inserter(rows), - [](const SubtypeEntry &e) { return TallyRow{e.name, QString::number(e.count)}; }); - - return rows; + return entries; } -} // namespace SubtypeTally +} // namespace SelectionSubtypeTally diff --git a/cockatrice/src/game/selection_subtype_tally.h b/cockatrice/src/game/selection_subtype_tally.h new file mode 100644 index 000000000..9038653f6 --- /dev/null +++ b/cockatrice/src/game/selection_subtype_tally.h @@ -0,0 +1,36 @@ +#ifndef SELECTION_SUBTYPE_TALLY_H +#define SELECTION_SUBTYPE_TALLY_H + +#include +#include + +class CardItem; + +/** @brief A single subtype (e.g., "Goblin", "Warrior") with its occurrence count. */ +struct SubtypeEntry +{ + QString name; ///< The subtype name + int count; ///< Number of selected cards with this subtype + + bool operator==(const SubtypeEntry &other) const + { + return name == other.name && count == other.count; + } +}; + +/** + * @brief Extracts and tallies subtypes from selected cards. + */ +namespace SelectionSubtypeTally +{ +/** + * @brief Parses card type lines and counts each subtype occurrence. + * + * Skips face-down cards and cards without type info. + * @param cards The list of selected card items to analyze. + * @return Entries sorted by count ascending, then alphabetically. + */ +QList countSubtypes(const QList &cards); +} // namespace SelectionSubtypeTally + +#endif diff --git a/cockatrice/src/game_graphics/game_view.cpp b/cockatrice/src/game_graphics/game_view.cpp index db17aaead..c2d9b2b3b 100644 --- a/cockatrice/src/game_graphics/game_view.cpp +++ b/cockatrice/src/game_graphics/game_view.cpp @@ -1,6 +1,7 @@ #include "game_view.h" #include "../client/settings/cache_settings.h" +#include "../game/selection_subtype_tally.h" #include "game_scene.h" #include @@ -78,12 +79,12 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par totalCountLabel->setStyleSheet(totalCountLabelStyle); totalCountLabel->hide(); - tallyContainer = new QWidget(this); - tallyContainer->setStyleSheet(subtypeTallyLabelStyle); - tallyLayout = new QGridLayout(tallyContainer); - tallyLayout->setContentsMargins(2, 2, 2, 2); - tallyLayout->setSpacing(2); - tallyContainer->hide(); + subtypeTallyContainer = new QWidget(this); + subtypeTallyContainer->setStyleSheet(subtypeTallyLabelStyle); + subtypeTallyLayout = new QGridLayout(subtypeTallyContainer); + subtypeTallyLayout->setContentsMargins(2, 2, 2, 2); + subtypeTallyLayout->setSpacing(2); + subtypeTallyContainer->hide(); } void GameView::resizeEvent(QResizeEvent *event) @@ -176,14 +177,14 @@ void GameView::refreshShortcuts() SettingsCache::instance().shortcuts().getShortcut("Player/aCloseMostRecentZoneView")); } -void GameView::clearTallyLabels() +void GameView::clearSubtypeLabels() { - QtUtils::clearLayoutRec(tallyLayout); + QtUtils::clearLayoutRec(subtypeTallyLayout); } -QSize GameView::rebuildTallyLabels(const QList &entries) +QSize GameView::rebuildSubtypeLabels(const QList &entries) { - clearTallyLabels(); + clearSubtypeLabels(); const QString nameStyle = QStringLiteral("color: white; font-size: 12px; background: transparent;"); const QString countStyle = @@ -194,16 +195,16 @@ QSize GameView::rebuildTallyLabels(const QList &entries) int maxCountWidth = 0; int row = 0; - for (const TallyRow &entry : entries) { - auto *nameLabel = new QLabel(entry.name, tallyContainer); + for (const SubtypeEntry &entry : entries) { + auto *nameLabel = new QLabel(entry.name, subtypeTallyContainer); nameLabel->setStyleSheet(nameStyle); nameLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - tallyLayout->addWidget(nameLabel, row, 0); + subtypeTallyLayout->addWidget(nameLabel, row, 0); - auto *countLabel = new QLabel(entry.value, tallyContainer); + auto *countLabel = new QLabel(QString::number(entry.count), subtypeTallyContainer); countLabel->setStyleSheet(countStyle); countLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - tallyLayout->addWidget(countLabel, row, 1); + subtypeTallyLayout->addWidget(countLabel, row, 1); QSize nameSize = nameLabel->sizeHint(); QSize countSize = countLabel->sizeHint(); @@ -214,9 +215,9 @@ QSize GameView::rebuildTallyLabels(const QList &entries) ++row; } - int spacing = tallyLayout->spacing(); - int margins = tallyLayout->contentsMargins().left() + tallyLayout->contentsMargins().right(); - int verticalMargins = tallyLayout->contentsMargins().top() + tallyLayout->contentsMargins().bottom(); + int spacing = subtypeTallyLayout->spacing(); + int margins = subtypeTallyLayout->contentsMargins().left() + subtypeTallyLayout->contentsMargins().right(); + int verticalMargins = subtypeTallyLayout->contentsMargins().top() + subtypeTallyLayout->contentsMargins().bottom(); int width = maxNameWidth + spacing + maxCountWidth + margins; int height = totalHeight + (row - 1) * spacing + verticalMargins; @@ -246,26 +247,29 @@ void GameView::updateTotalSelectionCount(const QSize &viewSize) totalCountLabel->show(); } - TallyType tallyType = - SettingsCache::instance().getShowSubtypeSelectionTally() ? TallyType::Subtypes : TallyType::None; + if (!SettingsCache::instance().getShowSubtypeSelectionTally() || count <= 1) { + subtypeTallyContainer->hide(); + cachedSubtypeEntries.clear(); + return; + } GameScene *gameScene = static_cast(scene()); - QList entries = Tally::compute(gameScene->selectedCards(), tallyType); + QList entries = SelectionSubtypeTally::countSubtypes(gameScene->selectedCards()); - if (entries.isEmpty() || count <= 1) { - tallyContainer->hide(); - cachedTallyRows.clear(); + if (entries.isEmpty()) { + subtypeTallyContainer->hide(); + cachedSubtypeEntries.clear(); return; } // Only rebuild labels if entries changed QSize containerSize; - if (entries != cachedTallyRows) { - cachedTallyRows = entries; - containerSize = rebuildTallyLabels(entries); - tallyContainer->resize(containerSize); + if (entries != cachedSubtypeEntries) { + cachedSubtypeEntries = entries; + containerSize = rebuildSubtypeLabels(entries); + subtypeTallyContainer->resize(containerSize); } else { - containerSize = tallyContainer->size(); + containerSize = subtypeTallyContainer->size(); } int x = availableWidth - containerSize.width() - kMarginInPixels; @@ -279,8 +283,8 @@ void GameView::updateTotalSelectionCount(const QSize &viewSize) y = qMax(kMarginInPixels, y); - tallyContainer->move(x, y); - tallyContainer->show(); + subtypeTallyContainer->move(x, y); + subtypeTallyContainer->show(); } /** diff --git a/cockatrice/src/game_graphics/game_view.h b/cockatrice/src/game_graphics/game_view.h index 3f6b60dbc..4047c87ab 100644 --- a/cockatrice/src/game_graphics/game_view.h +++ b/cockatrice/src/game_graphics/game_view.h @@ -7,7 +7,7 @@ #ifndef GAMEVIEW_H #define GAMEVIEW_H -#include "tally/tally.h" +#include "../game/selection_subtype_tally.h" #include @@ -24,13 +24,13 @@ private: QRubberBand *rubberBand; QLabel *dragCountLabel; QLabel *totalCountLabel; - QWidget *tallyContainer; - QGridLayout *tallyLayout; + QWidget *subtypeTallyContainer; + QGridLayout *subtypeTallyLayout; QPointF selectionOrigin; - QList cachedTallyRows; ///< Cached entries to avoid redundant rebuilds + QList cachedSubtypeEntries; ///< Cached entries to avoid redundant rebuilds - QSize rebuildTallyLabels(const QList &entries); - void clearTallyLabels(); + QSize rebuildSubtypeLabels(const QList &entries); + void clearSubtypeLabels(); protected: void resizeEvent(QResizeEvent *event) override; diff --git a/cockatrice/src/game_graphics/tally/subtype_tally.h b/cockatrice/src/game_graphics/tally/subtype_tally.h deleted file mode 100644 index 9926528e0..000000000 --- a/cockatrice/src/game_graphics/tally/subtype_tally.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef COCKATRICE_SUBTYPE_TALLY_H -#define COCKATRICE_SUBTYPE_TALLY_H - -#include "tally.h" - -#include -#include - -class CardItem; - -/** - * @brief Extracts and tallies subtypes from selected cards. - */ -namespace SubtypeTally -{ -/** - * @brief Parses card type lines and counts each subtype occurrence. - * - * Skips face-down cards and cards without type info. - * @param cards The list of selected card items to analyze. - * @return Entries sorted by count ascending, then alphabetically. - */ -QList countSubtypes(const QList &cards); -} // namespace SubtypeTally - -#endif diff --git a/cockatrice/src/game_graphics/tally/tally.cpp b/cockatrice/src/game_graphics/tally/tally.cpp deleted file mode 100644 index 521417ae6..000000000 --- a/cockatrice/src/game_graphics/tally/tally.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "tally.h" - -#include "subtype_tally.h" - -QList Tally::compute(const QList &cards, const TallyType type) -{ - switch (type) { - case TallyType::None: - return {}; - case TallyType::Subtypes: - return SubtypeTally::countSubtypes(cards); - } - return {}; -} diff --git a/cockatrice/src/game_graphics/tally/tally.h b/cockatrice/src/game_graphics/tally/tally.h deleted file mode 100644 index 1ccb7939f..000000000 --- a/cockatrice/src/game_graphics/tally/tally.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef COCKATRICE_TALLY_H -#define COCKATRICE_TALLY_H -#include - -class CardItem; - -/** @brief A single row of the tally output. */ -struct TallyRow -{ - QString name; ///< The row name (displayed on the left) - QString value; ///< Value for the row (displayed on the right) - - bool operator==(const TallyRow &) const = default; -}; - -/** - * The tally type - */ -enum class TallyType -{ - None, - Subtypes, -}; - -namespace Tally -{ - -/** - * @brief Analyzes the selected cards according to the tally type and builds the resulting tally rows. - * This forwards the cards to the code for that tally type. - * - * @param cards The list of selected card items to analyze. - * @param type The type of tally to do - * @return Rows sorted in top-to-bottom display order - */ -QList compute(const QList &cards, TallyType type); - -} // namespace Tally - -#endif // COCKATRICE_TALLY_H diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp index 5c4a88974..11fd02d80 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp @@ -436,35 +436,103 @@ void UserContextMenu::showContextMenu(const QPoint &pos, QAction *actionClicked = menu->exec(pos); if (actionClicked == nullptr) { } else if (actionClicked == aDetails) { - execDetails(userName); + auto *infoWidget = + new UserInfoBox(client, false, static_cast(parent()), + Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); + infoWidget->setAttribute(Qt::WA_DeleteOnClose); + infoWidget->updateInfo(userName); } else if (actionClicked == aChat) { - execChat(userName); + emit openMessageDialog(userName, true); } else if (actionClicked == aShowGames) { - execShowGames(userName); + Command_GetGamesOfUser cmd; + cmd.set_user_name(userName.toStdString()); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::gamesOfUserReceived); + + client->sendCommand(pend); } else if (actionClicked == aAddToBuddyList) { - execAddToBuddy(userName); + Command_AddToList cmd; + cmd.set_list("buddy"); + cmd.set_user_name(userName.toStdString()); + + client->sendCommand(client->prepareSessionCommand(cmd)); } else if (actionClicked == aRemoveFromBuddyList) { - execRemoveFromBuddy(userName); + Command_RemoveFromList cmd; + cmd.set_list("buddy"); + cmd.set_user_name(userName.toStdString()); + + client->sendCommand(client->prepareSessionCommand(cmd)); } else if (actionClicked == aAddToIgnoreList) { - execAddToIgnore(userName); + Command_AddToList cmd; + cmd.set_list("ignore"); + cmd.set_user_name(userName.toStdString()); + + client->sendCommand(client->prepareSessionCommand(cmd)); } else if (actionClicked == aRemoveFromIgnoreList) { - execRemoveFromIgnore(userName); + Command_RemoveFromList cmd; + cmd.set_list("ignore"); + cmd.set_user_name(userName.toStdString()); + + client->sendCommand(client->prepareSessionCommand(cmd)); } else if (actionClicked == aKick) { - execKick(playerId); + auto result = QMessageBox::question(static_cast(parent()), tr("Kick Player"), + tr("Are you sure you want to kick this player from the game?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (result == QMessageBox::Yes) { + Command_KickFromGame cmd; + cmd.set_player_id(playerId); + + game->getGameEventHandler()->sendGameCommand(cmd); + } } else if (actionClicked == aBan) { - execBan(userName); + Command_GetUserInfo cmd; + cmd.set_user_name(userName.toStdString()); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::banUser_processUserInfoResponse); + client->sendCommand(pend); } else if (actionClicked == aPromoteToMod || actionClicked == aDemoteFromMod) { - execAdjustMod(userName, actionClicked == aPromoteToMod); + Command_AdjustMod cmd; + cmd.set_user_name(userName.toStdString()); + cmd.set_should_be_mod(actionClicked == aPromoteToMod); + + PendingCommand *pend = client->prepareAdminCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); + client->sendCommand(pend); } else if (actionClicked == aPromoteToJudge || actionClicked == aDemoteFromJudge) { - execAdjustJudge(userName, actionClicked == aPromoteToJudge); + Command_AdjustMod cmd; + cmd.set_user_name(userName.toStdString()); + cmd.set_should_be_judge(actionClicked == aPromoteToJudge); + + PendingCommand *pend = client->prepareAdminCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); + client->sendCommand(pend); } else if (actionClicked == aBanHistory) { - execBanHistory(userName); + Command_GetBanHistory cmd; + cmd.set_user_name(userName.toStdString()); + PendingCommand *pend = client->prepareModeratorCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::banUserHistory_processResponse); + client->sendCommand(pend); } else if (actionClicked == aWarnUser) { - execWarn(userName); + Command_GetUserInfo cmd; + cmd.set_user_name(userName.toStdString()); + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::warnUser_processUserInfoResponse); + client->sendCommand(pend); } else if (actionClicked == aWarnHistory) { - execWarnHistory(userName); + Command_GetWarnHistory cmd; + cmd.set_user_name(userName.toStdString()); + PendingCommand *pend = client->prepareModeratorCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::warnUserHistory_processResponse); + client->sendCommand(pend); } else if (actionClicked == aGetAdminNotes) { - execAdminNotes(userName); + Command_GetAdminNotes cmd; + cmd.set_user_name(userName.toStdString()); + auto *pend = client->prepareModeratorCommand(cmd); + connect(pend, &PendingCommand::finished, this, &UserContextMenu::getAdminNotes_processResponse); + client->sendCommand(pend); + } else if (actionClicked == aCopyToClipBoard) { QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setText(deckHash); @@ -529,19 +597,6 @@ void UserContextMenu::execRemoveFromIgnore(const QString &userName) client->sendCommand(client->prepareSessionCommand(cmd)); } -void UserContextMenu::execKick(int playerId) -{ - auto result = QMessageBox::question(static_cast(parent()), tr("Kick Player"), - tr("Are you sure you want to kick this player from the game?"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (result == QMessageBox::Yes) { - Command_KickFromGame cmd; - cmd.set_player_id(playerId); - - game->getGameEventHandler()->sendGameCommand(cmd); - } -} - void UserContextMenu::execBan(const QString &userName) { Command_GetUserInfo cmd; @@ -587,20 +642,11 @@ void UserContextMenu::execAdminNotes(const QString &userName) client->sendCommand(pend); } -void UserContextMenu::execAdjustMod(const QString &userName, bool shouldBeMod) +void UserContextMenu::execAdjustMod(const QString &userName, bool shouldBeMod, bool shouldBeJudge) { Command_AdjustMod cmd; cmd.set_user_name(userName.toStdString()); cmd.set_should_be_mod(shouldBeMod); - PendingCommand *pend = client->prepareAdminCommand(cmd); - connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); - client->sendCommand(pend); -} - -void UserContextMenu::execAdjustJudge(const QString &userName, bool shouldBeJudge) -{ - Command_AdjustMod cmd; - cmd.set_user_name(userName.toStdString()); cmd.set_should_be_judge(shouldBeJudge); PendingCommand *pend = client->prepareAdminCommand(cmd); connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); diff --git a/cockatrice/src/interface/widgets/server/user/user_context_menu.h b/cockatrice/src/interface/widgets/server/user/user_context_menu.h index 00fdc51fe..28173bfbc 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.h +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.h @@ -89,14 +89,12 @@ public: void execRemoveFromBuddy(const QString &userName); void execAddToIgnore(const QString &userName); void execRemoveFromIgnore(const QString &userName); - void execKick(int playerId); void execBan(const QString &userName); void execWarn(const QString &userName); void execBanHistory(const QString &userName); void execWarnHistory(const QString &userName); void execAdminNotes(const QString &userName); - void execAdjustMod(const QString &userName, bool shouldBeMod); - void execAdjustJudge(const QString &userName, bool shouldBeJudge); + void execAdjustMod(const QString &userName, bool shouldBeMod, bool shouldBeJudge); }; #endif diff --git a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp index 09480b1a1..32f46a79f 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp @@ -707,13 +707,13 @@ void UserListWidget::connectPopupSignals() connect(m_userInfoPopup, &UserInfoPopup::warnHistoryRequested, userContextMenu, &UserContextMenu::execWarnHistory); connect(m_userInfoPopup, &UserInfoPopup::adminNotesRequested, userContextMenu, &UserContextMenu::execAdminNotes); connect(m_userInfoPopup, &UserInfoPopup::promoteToModRequested, this, - [this](const QString &n) { userContextMenu->execAdjustMod(n, true); }); + [this](const QString &n) { userContextMenu->execAdjustMod(n, true, false); }); connect(m_userInfoPopup, &UserInfoPopup::demoteFromModRequested, this, - [this](const QString &n) { userContextMenu->execAdjustMod(n, false); }); + [this](const QString &n) { userContextMenu->execAdjustMod(n, false, false); }); connect(m_userInfoPopup, &UserInfoPopup::promoteToJudgeRequested, this, - [this](const QString &n) { userContextMenu->execAdjustJudge(n, true); }); + [this](const QString &n) { userContextMenu->execAdjustMod(n, false, true); }); connect(m_userInfoPopup, &UserInfoPopup::demoteFromJudgeRequested, this, - [this](const QString &n) { userContextMenu->execAdjustJudge(n, false); }); + [this](const QString &n) { userContextMenu->execAdjustMod(n, false, false); }); } bool UserListWidget::eventFilter(QObject *obj, QEvent *event)