diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 91f588870..ac85522d2 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -162,7 +162,7 @@ jobs: - name: "Restore compiler cache (ccache)" id: ccache_restore - uses: actions/cache/restore@v6 + uses: actions/cache/restore@v5 env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} with: @@ -215,7 +215,7 @@ jobs: - name: "Save updated compiler cache (ccache)" if: github.ref == 'refs/heads/master' - uses: actions/cache/save@v6 + uses: actions/cache/save@v5 with: key: ${{ steps.ccache_restore.outputs.cache-primary-key }} path: ${{ env.CACHE }} @@ -365,7 +365,7 @@ jobs: - name: "[macOS] Restore compiler cache (ccache)" if: matrix.os == 'macOS' && matrix.use_ccache == true id: ccache_restore - uses: actions/cache/restore@v6 + uses: actions/cache/restore@v5 env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} with: @@ -387,7 +387,7 @@ jobs: - name: "[macOS] Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }} libraries" if: matrix.os == 'macOS' id: restore_qt - uses: actions/cache/restore@v6 + uses: actions/cache/restore@v5 with: key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }} path: ${{ github.workspace }}/Qt @@ -410,7 +410,7 @@ jobs: - name: "[macOS] Cache thin Qt libraries" if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true' - uses: actions/cache/save@v6 + uses: actions/cache/save@v5 with: key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }} path: ${{ github.workspace }}/Qt @@ -473,7 +473,7 @@ jobs: - name: "[macOS] Save updated compiler cache (ccache)" if: matrix.os == 'macOS' && matrix.use_ccache == true && github.ref == 'refs/heads/master' - uses: actions/cache/save@v6 + uses: actions/cache/save@v5 with: key: ${{ steps.ccache_restore.outputs.cache-primary-key }} path: ${{ env.CCACHE_DIR }} diff --git a/.github/workflows/translations-pull.yml b/.github/workflows/translations-pull.yml index a3db5f86d..57df31bf0 100644 --- a/.github/workflows/translations-pull.yml +++ b/.github/workflows/translations-pull.yml @@ -23,8 +23,6 @@ jobs: uses: actions/checkout@v7 - name: "Pull translated strings from Transifex" - # Do not run this step for PR's from forks, they don't have access to the secret - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false uses: transifex/cli-action@v2 with: # Used config file: https://github.com/Cockatrice/Cockatrice/blob/master/.tx/config diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 166b807d9..bd99d08bf 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -83,7 +83,6 @@ 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 @@ -237,14 +236,10 @@ set(cockatrice_SOURCES src/interface/widgets/server/handle_public_servers.cpp src/interface/widgets/server/remote/remote_decklist_tree_widget.cpp src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp - src/interface/widgets/server/user/user_avatar_provider.cpp - src/interface/widgets/server/user/user_card_art_provider.cpp - src/interface/widgets/server/user/user_card_settings_dialog.cpp src/interface/widgets/server/user/user_context_menu.cpp src/interface/widgets/server/user/user_info_box.cpp src/interface/widgets/server/user/user_info_connection.cpp src/interface/widgets/server/user/user_list_manager.cpp - src/interface/widgets/server/user/user_list_painter.cpp src/interface/widgets/server/user/user_list_widget.cpp src/interface/widgets/settings_page/appearance_settings_page.cpp src/interface/widgets/settings_page/deck_editor_settings_page.cpp @@ -332,7 +327,6 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/tab.cpp src/interface/widgets/tabs/tab_account.cpp src/interface/widgets/tabs/tab_admin.cpp - src/interface/widgets/tabs/tab_card_art_rules.cpp src/interface/widgets/tabs/tab_deck_editor.cpp src/interface/widgets/tabs/tab_deck_storage.cpp src/interface/widgets/tabs/tab_game.cpp @@ -355,8 +349,6 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h src/interface/widgets/utility/compact_push_button.cpp src/interface/widgets/utility/compact_push_button.h - src/interface/widgets/server/user/user_info_popup.cpp - src/interface/widgets/server/user/user_info_popup.h ) add_subdirectory(sounds) diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index b6bc8a47d..cc34e1707 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -313,7 +313,6 @@ SettingsCache::SettingsCache() showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool(); showTotalSelectionCount = settings->value("interface/showpersistentselectioncount", true).toBool(); - showSubtypeSelectionTally = settings->value("interface/showsubtypeselectiontally", true).toBool(); showShortcuts = settings->value("menu/showshortcuts", true).toBool(); showGameSelectorFilterToolbar = settings->value("menu/showgameselectorfiltertoolbar", true).toBool(); @@ -372,7 +371,6 @@ SettingsCache::SettingsCache() openDeckInNewTab = settings->value("editor/openDeckInNewTab", false).toBool(); rewindBufferingMs = settings->value("replay/rewindBufferingMs", 200).toInt(); - styleUserList = settings->value("appearance/styleUserList", true).toBool(); chatMention = settings->value("chat/mention", true).toBool(); chatMentionCompleter = settings->value("chat/mentioncompleter", true).toBool(); chatMentionForeground = settings->value("chat/mentionforeground", true).toBool(); @@ -1047,13 +1045,6 @@ void SettingsCache::setRewindBufferingMs(int _rewindBufferingMs) settings->setValue("replay/rewindBufferingMs", rewindBufferingMs); } -void SettingsCache::setStyleUserList(QT_STATE_CHANGED_T _styleUserList) -{ - styleUserList = static_cast(_styleUserList); - settings->setValue("appearance/styleUserList", styleUserList); - emit styleUserListChanged(); -} - void SettingsCache::setChatMention(QT_STATE_CHANGED_T _chatMention) { chatMention = static_cast(_chatMention); @@ -1396,12 +1387,6 @@ void SettingsCache::setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSele settings->setValue("interface/showpersistentselectioncount", showTotalSelectionCount); } -void SettingsCache::setShowSubtypeSelectionTally(QT_STATE_CHANGED_T _showSubtypeSelectionTally) -{ - showSubtypeSelectionTally = static_cast(_showSubtypeSelectionTally); - settings->setValue("interface/showsubtypeselectiontally", showSubtypeSelectionTally); -} - void SettingsCache::loadPaths() { QString dataPath = getDataPath(); diff --git a/cockatrice/src/client/settings/cache_settings.h b/cockatrice/src/client/settings/cache_settings.h index 29af89587..a166917c1 100644 --- a/cockatrice/src/client/settings/cache_settings.h +++ b/cockatrice/src/client/settings/cache_settings.h @@ -190,7 +190,6 @@ signals: void cardPictureLoaderCacheMethodChanged(int cardPictureLoaderCacheMethod); void localCardImageStorageNamingSchemeChanged(int localCardImageStorageNamingScheme); void masterVolumeChanged(int value); - void styleUserListChanged(); void chatMentionCompleterChanged(); void downloadSpoilerTimeIndexChanged(); void downloadSpoilerStatusChanged(); @@ -285,7 +284,6 @@ private: bool autoRotateSidewaysLayoutCards; bool openDeckInNewTab; int rewindBufferingMs; - bool styleUserList; bool chatMention; bool chatMentionCompleter; QString chatMentionColor; @@ -355,7 +353,6 @@ private: bool showStatusBar; bool showDragSelectionCount; bool showTotalSelectionCount; - bool showSubtypeSelectionTally; public: SettingsCache(); @@ -479,10 +476,6 @@ public: { return showTotalSelectionCount; } - [[nodiscard]] bool getShowSubtypeSelectionTally() const - { - return showSubtypeSelectionTally; - } [[nodiscard]] bool getNotificationsEnabled() const { return notificationsEnabled; @@ -745,10 +738,6 @@ public: { return rewindBufferingMs; } - [[nodiscard]] bool getStyleUserList() const - { - return styleUserList; - } [[nodiscard]] bool getChatMention() const { return chatMention; @@ -1124,7 +1113,6 @@ public slots: void setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T _autoRotateSidewaysLayoutCards); void setOpenDeckInNewTab(QT_STATE_CHANGED_T _openDeckInNewTab); void setRewindBufferingMs(int _rewindBufferingMs); - void setStyleUserList(QT_STATE_CHANGED_T _styleUserList); void setChatMention(QT_STATE_CHANGED_T _chatMention); void setChatMentionCompleter(QT_STATE_CHANGED_T _chatMentionCompleter); void setChatMentionForeground(QT_STATE_CHANGED_T _chatMentionForeground); @@ -1181,6 +1169,5 @@ public slots: void setRoundCardCorners(bool _roundCardCorners); void setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount); void setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSelectionCount); - void setShowSubtypeSelectionTally(QT_STATE_CHANGED_T _showSubtypeSelectionTally); }; #endif diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index c9e8628f4..fffd23ccf 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -1018,9 +1018,8 @@ void PlayerActions::actCreateAllRelatedCards() if (!cardRelationAll->getDoesAttach() && !cardRelationAll->getIsVariable()) { dbName = cardRelationAll->getName(); bool persistent = cardRelationAll->getIsPersistent(); - bool faceDown = cardRelationAll->getIsFaceDown(); for (int i = 0; i < cardRelationAll->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent, faceDown); + createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); } ++tokensTypesCreated; if (tokensTypesCreated == 1) { @@ -1035,9 +1034,8 @@ void PlayerActions::actCreateAllRelatedCards() if (!cardRelationNotExcluded->getDoesAttach() && !cardRelationNotExcluded->getIsVariable()) { dbName = cardRelationNotExcluded->getName(); bool persistent = cardRelationNotExcluded->getIsPersistent(); - bool faceDown = cardRelationNotExcluded->getIsFaceDown(); for (int i = 0; i < cardRelationNotExcluded->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent, faceDown); + createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); } ++tokensTypesCreated; if (tokensTypesCreated == 1) { @@ -1075,7 +1073,6 @@ bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, const QString dbName = cardRelation->getName(); const bool persistent = cardRelation->getIsPersistent(); - const bool faceDown = cardRelation->getIsFaceDown(); // Variable relations always use DoesNotAttach, regardless of the count the user // entered. @@ -1084,7 +1081,7 @@ bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, return false; } for (int i = 0; i < variableCount; ++i) { - createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent, faceDown); + createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); } return true; } @@ -1093,7 +1090,7 @@ bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, if (count > 1) { for (int i = 0; i < count; ++i) { - createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent, faceDown); + createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); } return true; } @@ -1113,7 +1110,7 @@ bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, playCardToTable(sourceCard, false); } - createCard(sourceCard, dbName, attachType, persistent, faceDown); + createCard(sourceCard, dbName, attachType, persistent); return true; } @@ -1140,8 +1137,7 @@ void PlayerActions::onRelatedCardCreated(const CardItem *sourceCard, const CardR void PlayerActions::createCard(const CardItem *sourceCard, const QString &dbCardName, CardRelationType attachType, - bool persistent, - bool faceDown) + bool persistent) { CardInfoPtr cardInfo = CardDatabaseManager::query()->getCardInfo(dbCardName); @@ -1176,7 +1172,6 @@ void PlayerActions::createCard(const CardItem *sourceCard, cmd.set_destroy_on_zone_change(!persistent); cmd.set_x(gridPoint.x()); cmd.set_y(gridPoint.y()); - cmd.set_face_down(faceDown); ExactCard relatedCard = CardDatabaseManager::query()->getCardFromSameSet(cardInfo->getName(), sourceCard->getCard().getPrinting()); diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index fa4d54110..3f1960892 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -240,8 +240,7 @@ private: void createCard(const CardItem *sourceCard, const QString &dbCardName, CardRelationType attach = CardRelationType::DoesNotAttach, - bool persistent = false, - bool faceDown = false); + bool persistent = false); void playSelectedCards(QList selectedCards, bool faceDown = false); diff --git a/cockatrice/src/game/selection_subtype_tally.cpp b/cockatrice/src/game/selection_subtype_tally.cpp deleted file mode 100644 index e9f87fab9..000000000 --- a/cockatrice/src/game/selection_subtype_tally.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "selection_subtype_tally.h" - -#include "../game_graphics/board/card_item.h" - -#include -#include - -namespace -{ - -/** @brief Extracts subtypes from a single card face's type line. */ -QStringList extractSubtypesFromFace(const QString &faceType) -{ - // Card type format: "Creature — Goblin Warrior" or "Legendary Enchantment — Saga" - QStringList parts = faceType.split(QStringLiteral(" — ")); - if (parts.size() > 1) { - return parts[1].split(QStringLiteral(" "), Qt::SkipEmptyParts); - } - return {}; -} - -} // anonymous namespace - -namespace SelectionSubtypeTally -{ - -QList countSubtypes(const QList &cards) -{ - QMap subtypeCounts; - - for (CardItem *card : cards) { - if (card->getFaceDown() || card->getCard().isEmpty()) { - continue; - } - - QString cardType = card->getCardInfo().getCardType(); - // Handle double-faced cards: "Creature — Human // Creature — Werewolf" - QStringList cardFaces = cardType.split(QStringLiteral(" // ")); - - for (const QString &face : cardFaces) { - QStringList subtypes = extractSubtypesFromFace(face); - for (const QString &subtype : subtypes) { - subtypeCounts[subtype]++; - } - } - } - - QList entries; - for (auto it = subtypeCounts.constBegin(); it != subtypeCounts.constEnd(); ++it) { - entries.append({it.key(), it.value()}); - } - - // Sort by count ascending, then alphabetically (lowest counts at bottom of display) - std::sort(entries.begin(), entries.end(), [](const SubtypeEntry &a, const SubtypeEntry &b) { - if (a.count != b.count) { - return a.count < b.count; - } - return a.name < b.name; - }); - - return entries; -} - -} // namespace SelectionSubtypeTally diff --git a/cockatrice/src/game/selection_subtype_tally.h b/cockatrice/src/game/selection_subtype_tally.h deleted file mode 100644 index 9038653f6..000000000 --- a/cockatrice/src/game/selection_subtype_tally.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 c2d9b2b3b..41befd9a4 100644 --- a/cockatrice/src/game_graphics/game_view.cpp +++ b/cockatrice/src/game_graphics/game_view.cpp @@ -1,16 +1,12 @@ #include "game_view.h" #include "../client/settings/cache_settings.h" -#include "../game/selection_subtype_tally.h" #include "game_scene.h" #include -#include #include -#include #include #include -#include // QRubberBand calls raise() in showEvent() and changeEvent() to stay on top of siblings. // This subclass disables that behavior so dragCountLabel can appear above it. @@ -59,40 +55,31 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par refreshShortcuts(); rubberBand = new SelectionRubberBand(QRubberBand::Rectangle, this); - const QString baseProperties = "color: white; " - "font-family: monospace; " - "background-color: rgba(0, 0, 0, 160); " - "border-radius: 3px; " - "padding: 1px 2px; " - "white-space: pre;"; - - const QString dragCountLabelStyle = baseProperties + "font-size: 14px; font-weight: bold;"; - const QString totalCountLabelStyle = baseProperties + "font-size: 16px; font-weight: bold;"; - const QString subtypeTallyLabelStyle = baseProperties + "font-size: 12px;"; + const QString countLabelStyle = "color: white; " + "font-size: 14px; " + "font-weight: bold; " + "background-color: rgba(0, 0, 0, 160); " + "border-radius: 3px; " + "padding: 1px 2px;"; dragCountLabel = new QLabel(this); - dragCountLabel->setStyleSheet(dragCountLabelStyle); + dragCountLabel->setStyleSheet(countLabelStyle); dragCountLabel->hide(); dragCountLabel->raise(); totalCountLabel = new QLabel(this); - totalCountLabel->setStyleSheet(totalCountLabelStyle); + totalCountLabel->setStyleSheet(countLabelStyle); totalCountLabel->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) { QGraphicsView::resizeEvent(event); - GameScene *s = static_cast(scene()); - s->processViewSizeChange(event->size()); + GameScene *s = dynamic_cast(scene()); + if (s) { + s->processViewSizeChange(event->size()); + } updateSceneRect(scene()->sceneRect()); updateTotalSelectionCount(event->size()); @@ -177,114 +164,29 @@ void GameView::refreshShortcuts() SettingsCache::instance().shortcuts().getShortcut("Player/aCloseMostRecentZoneView")); } -void GameView::clearSubtypeLabels() -{ - QtUtils::clearLayoutRec(subtypeTallyLayout); -} - -QSize GameView::rebuildSubtypeLabels(const QList &entries) -{ - clearSubtypeLabels(); - - const QString nameStyle = QStringLiteral("color: white; font-size: 12px; background: transparent;"); - const QString countStyle = - QStringLiteral("color: white; font-size: 14px; font-weight: bold; background: transparent;"); - - int totalHeight = 0; - int maxNameWidth = 0; - int maxCountWidth = 0; - - int row = 0; - for (const SubtypeEntry &entry : entries) { - auto *nameLabel = new QLabel(entry.name, subtypeTallyContainer); - nameLabel->setStyleSheet(nameStyle); - nameLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - subtypeTallyLayout->addWidget(nameLabel, row, 0); - - auto *countLabel = new QLabel(QString::number(entry.count), subtypeTallyContainer); - countLabel->setStyleSheet(countStyle); - countLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - subtypeTallyLayout->addWidget(countLabel, row, 1); - - QSize nameSize = nameLabel->sizeHint(); - QSize countSize = countLabel->sizeHint(); - maxNameWidth = qMax(maxNameWidth, nameSize.width()); - maxCountWidth = qMax(maxCountWidth, countSize.width()); - totalHeight += qMax(nameSize.height(), countSize.height()); - - ++row; - } - - 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; - - return QSize(width, height); -} - void GameView::updateTotalSelectionCount(const QSize &viewSize) { - constexpr int kMarginInPixels = 10; - constexpr int kSpacingBetweenLabels = 4; - - int availableWidth = viewSize.isValid() ? viewSize.width() : viewport()->width(); - int availableHeight = viewSize.isValid() ? viewSize.height() : viewport()->height(); + if (!SettingsCache::instance().getShowTotalSelectionCount()) { + totalCountLabel->hide(); + return; + } int count = scene()->selectedItems().count(); - if (!SettingsCache::instance().getShowTotalSelectionCount() || count <= 1) { - totalCountLabel->hide(); - } else { + if (count > 1) { totalCountLabel->setText(QString::number(count)); totalCountLabel->adjustSize(); + constexpr int kMarginInPixels = 10; + int availableWidth = viewSize.isValid() ? viewSize.width() : viewport()->width(); + int availableHeight = viewSize.isValid() ? viewSize.height() : viewport()->height(); int x = availableWidth - totalCountLabel->width() - kMarginInPixels; int y = availableHeight - totalCountLabel->height() - kMarginInPixels; totalCountLabel->move(x, y); totalCountLabel->show(); - } - - if (!SettingsCache::instance().getShowSubtypeSelectionTally() || count <= 1) { - subtypeTallyContainer->hide(); - cachedSubtypeEntries.clear(); - return; - } - - GameScene *gameScene = static_cast(scene()); - QList entries = SelectionSubtypeTally::countSubtypes(gameScene->selectedCards()); - - if (entries.isEmpty()) { - subtypeTallyContainer->hide(); - cachedSubtypeEntries.clear(); - return; - } - - // Only rebuild labels if entries changed - QSize containerSize; - if (entries != cachedSubtypeEntries) { - cachedSubtypeEntries = entries; - containerSize = rebuildSubtypeLabels(entries); - subtypeTallyContainer->resize(containerSize); } else { - containerSize = subtypeTallyContainer->size(); + totalCountLabel->hide(); } - - int x = availableWidth - containerSize.width() - kMarginInPixels; - int y; - - if (totalCountLabel->isVisible()) { - y = totalCountLabel->y() - containerSize.height() - kSpacingBetweenLabels; - } else { - y = availableHeight - containerSize.height() - kMarginInPixels; - } - - y = qMax(kMarginInPixels, y); - - 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 4047c87ab..80e8e96b5 100644 --- a/cockatrice/src/game_graphics/game_view.h +++ b/cockatrice/src/game_graphics/game_view.h @@ -7,12 +7,9 @@ #ifndef GAMEVIEW_H #define GAMEVIEW_H -#include "../game/selection_subtype_tally.h" - #include class GameScene; -class QGridLayout; class QLabel; class QRubberBand; @@ -24,13 +21,7 @@ private: QRubberBand *rubberBand; QLabel *dragCountLabel; QLabel *totalCountLabel; - QWidget *subtypeTallyContainer; - QGridLayout *subtypeTallyLayout; QPointF selectionOrigin; - QList cachedSubtypeEntries; ///< Cached entries to avoid redundant rebuilds - - QSize rebuildSubtypeLabels(const QList &entries); - void clearSubtypeLabels(); protected: void resizeEvent(QResizeEvent *event) override; diff --git a/cockatrice/src/interface/theme_manager.cpp b/cockatrice/src/interface/theme_manager.cpp index 4ba35a00e..086845fe6 100644 --- a/cockatrice/src/interface/theme_manager.cpp +++ b/cockatrice/src/interface/theme_manager.cpp @@ -271,9 +271,6 @@ void ThemeManager::applyStyleAndPalette(const QString &themeName, const PaletteConfig &palCfg, const QString &activeScheme) { -#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) - Q_UNUSED(activeScheme) -#endif QString styleName = themeCfg.styleName; if (styleName.isEmpty() || styleName.compare("Default", Qt::CaseInsensitive) == 0) { if (themeName == FUSION_THEME_NAME) { diff --git a/cockatrice/src/interface/widgets/server/user/user_avatar_provider.cpp b/cockatrice/src/interface/widgets/server/user/user_avatar_provider.cpp deleted file mode 100644 index c115caa47..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_avatar_provider.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "user_avatar_provider.h" - -#include -#include -#include - -UserAvatarProvider::UserAvatarProvider(AbstractClient *client, QObject *parent) : QObject(parent), client(client) -{ -} - -const QMap &UserAvatarProvider::cache() const -{ - return avatarCache; -} - -void UserAvatarProvider::requestAvatar(const QString &userName) -{ - if (avatarCache.contains(userName) || pending.contains(userName)) { - return; - } - - pending.insert(userName); - - Command_GetUserInfo cmd; - cmd.set_user_name(userName.toStdString()); - - PendingCommand *pend = client->prepareSessionCommand(cmd); - - connect(pend, &PendingCommand::finished, this, [this, userName](const Response &r) { - pending.remove(userName); - - const auto &response = r.GetExtension(Response_GetUserInfo::ext); - const auto &user = response.user_info(); - const std::string &bmp = user.avatar_bmp(); - - QPixmap avatar; - if (!bmp.empty() && - avatar.loadFromData(reinterpret_cast(bmp.data()), static_cast(bmp.size()))) { - avatarCache.insert(userName, avatar); - } else { - avatarCache.insert(userName, QPixmap()); - } - - emit avatarUpdated(userName); - }); - - client->sendCommand(pend); -} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_avatar_provider.h b/cockatrice/src/interface/widgets/server/user/user_avatar_provider.h deleted file mode 100644 index 44491e544..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_avatar_provider.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef COCKATRICE_USER_AVATAR_PROVIDER_H -#define COCKATRICE_USER_AVATAR_PROVIDER_H - -#include -#include -#include -#include - -class AbstractClient; - -class UserAvatarProvider : public QObject -{ - Q_OBJECT - -public: - explicit UserAvatarProvider(AbstractClient *client, QObject *parent = nullptr); - - void requestAvatar(const QString &userName); - const QMap &cache() const; - -signals: - void avatarUpdated(const QString &userName); - -private: - AbstractClient *client; - QMap avatarCache; - QSet pending; -}; - -#endif // COCKATRICE_USER_AVATAR_PROVIDER_H \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp b/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp deleted file mode 100644 index 67fb4f684..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "user_card_art_provider.h" - -#include "../../../card_picture_loader/card_picture_loader.h" - -#include -#include - -static QString makeKey(const QString &user, const QString &card, const QString &providerId) -{ - return user + u'|' + card + u'|' + providerId; -} - -UserCardArtProvider::UserCardArtProvider(QObject *parent) : QObject(parent) -{ - dbReady = (CardDatabaseManager::getInstance()->getLoadStatus() == LoadStatus::Ok); - - if (!dbReady) { - connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this, - &UserCardArtProvider::onDatabaseReady); - } -} - -void UserCardArtProvider::onDatabaseReady() -{ - dbReady = true; - processQueue(); -} - -const QMap &UserCardArtProvider::cache() const -{ - return cardArtCache; -} - -void UserCardArtProvider::requestCardArt(const QString &userName, const QString &cardName, const QString &providerId) -{ - if (cardName.isEmpty()) { - return; - } - - const QString key = makeKey(userName, cardName, providerId); - - if (cardArtCache.contains(key) || pending.contains(key)) { - return; - } - - pending.insert(key); - queue.enqueue(key); - - processQueue(); -} - -QPixmap UserCardArtProvider::cropCardArt(const QPixmap &fullRes) -{ - const QSize sz = fullRes.size(); - const int marginX = sz.width() * 0.07; - const int topMargin = sz.height() * 0.11; - const int bottomMargin = sz.height() * 0.45; - - const QRect foilRect(marginX, topMargin, sz.width() - 2 * marginX, sz.height() - topMargin - bottomMargin); - - return fullRes.copy(foilRect.intersected(fullRes.rect())); -} - -void UserCardArtProvider::insertIntoCache(const QString &key, const QPixmap &pixmap) -{ - if (!cardArtCache.contains(key)) { - cacheInsertionOrder.append(key); - while (cacheInsertionOrder.size() > MaxCacheEntries) { - const QString evicted = cacheInsertionOrder.takeFirst(); - cardArtCache.remove(evicted); - } - } - cardArtCache.insert(key, pixmap); -} - -void UserCardArtProvider::processQueue() -{ - if (!dbReady) { - return; - } - - while (!queue.isEmpty()) { - const QString key = queue.dequeue(); - - const QStringList parts = key.split(u'|'); - if (parts.size() != 3) { - pending.remove(key); - continue; - } - - const QString userName = parts.at(0); - const QString cardName = parts.at(1); - const QString providerId = parts.at(2); - - ExactCard card = CardDatabaseManager::query()->getCard({cardName, providerId}); - - if (!card) { - pending.remove(key); - continue; - } - - QPixmap fullRes; - CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040)); - - // Synchronous hit (already loaded/on disk) - if (!fullRes.isNull()) { - insertIntoCache(key, cropCardArt(fullRes)); - pending.remove(key); - - emit cardArtUpdated(userName); - continue; - } - - // Async load required. - QPointer self(this); - - auto conn = std::make_shared(); - - *conn = connect(card.getCardPtr().data(), &CardInfo::pixmapUpdated, this, - [self, key, userName, card, conn]() mutable { - if (!self) { - return; - } - - QObject::disconnect(*conn); - - QPixmap fullRes; - CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040)); - - if (!fullRes.isNull()) { - self->insertIntoCache(key, self->cropCardArt(fullRes)); - } else { - self->insertIntoCache(key, QPixmap()); - } - - self->pending.remove(key); - - emit self->cardArtUpdated(userName); - - // Resume processing remaining queued items. - self->processQueue(); - }); - - // Stop here. We'll continue when the async load finishes. - return; - } -} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h b/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h deleted file mode 100644 index fb2f37812..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_card_art_provider.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef COCKATRICE_USER_CARD_ART_PROVIDER_H -#define COCKATRICE_USER_CARD_ART_PROVIDER_H - -#include -#include -#include -#include -#include - -class UserCardArtProvider : public QObject -{ - Q_OBJECT - -public: - explicit UserCardArtProvider(QObject *parent = nullptr); - - void requestCardArt(const QString &userName, const QString &cardName, const QString &providerId); - const QMap &cache() const; - static QPixmap cropCardArt(const QPixmap &fullRes); - -signals: - void cardArtUpdated(const QString &userName); - -public slots: - void onDatabaseReady(); - -private: - bool dbReady = false; - static constexpr int MaxCacheEntries = 300; - QList cacheInsertionOrder; // FIFO eviction - QMap cardArtCache; - QSet pending; - QQueue queue; - - void processQueue(); - void insertIntoCache(const QString &key, const QPixmap &pixmap); -}; - -#endif // COCKATRICE_USER_CARD_ART_PROVIDER_H \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp b/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp deleted file mode 100644 index 56c9600a0..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.cpp +++ /dev/null @@ -1,339 +0,0 @@ -#include "user_card_settings_dialog.h" - -#include "../../../card_picture_loader/card_picture_loader.h" -#include "card/card_completer_proxy_model.h" -#include "card/card_search_model.h" -#include "card_database_display_model.h" -#include "card_database_model.h" -#include "user_card_art_provider.h" -#include "user_list_painter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -CardArtPreviewWidget::CardArtPreviewWidget(QWidget *parent) : QWidget(parent) -{ - setMinimumSize(400, 72); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); -} - -void CardArtPreviewWidget::setPixmap(const QPixmap &pixmap) -{ - sourcePixmap = pixmap; - update(); -} - -void CardArtPreviewWidget::setParams(const CardArtParams &p) -{ - params = p; - update(); -} - -void CardArtPreviewWidget::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); - - const QRect rect = this->rect(); - - const QColor accentColor(100, 116, 139); - const QRectF cardRect = QRectF(rect).adjusted(3, 2, -3, -2); - - QLinearGradient bg(cardRect.topLeft(), cardRect.topRight()); - bg.setColorAt(0, accentColor.darker(320)); - bg.setColorAt(1, QColor(18, 22, 30)); - painter.setPen(Qt::NoPen); - painter.setBrush(bg); - painter.drawRoundedRect(cardRect, 6, 6); - painter.setBrush(accentColor); - painter.drawRoundedRect(QRectF(cardRect.left(), cardRect.top(), 3, cardRect.height()), 2, 2); - - if (sourcePixmap.isNull()) { - painter.setPen(QColor(150, 150, 150)); - painter.drawText(rect, Qt::AlignCenter, tr("No card selected")); - return; - } - - UserListPainter::drawCardArt(&painter, rect, rect.right() - 4, - QString(), // userName not needed for override path - nullptr, // no cache - params, - &sourcePixmap // direct pixmap - ); - - // Avatar placeholder so the left-margin interaction is visible - const int avatarX = rect.left() + 14; - const int avatarY = rect.top() + (rect.height() - 36) / 2; - const QRect avatarRect(avatarX, avatarY, 36, 36); - - QPainterPath clip; - clip.addEllipse(avatarRect); - painter.save(); - painter.setClipPath(clip); - painter.setBrush(accentColor.darker(200)); - painter.setPen(Qt::NoPen); - painter.drawEllipse(avatarRect); - painter.restore(); - - painter.setPen(QPen(QColor(70, 80, 95), 2)); - painter.setBrush(Qt::NoBrush); - painter.drawEllipse(avatarRect.adjusted(-1, -1, 1, 1)); -} - -UserCardArtSettingsDialog::UserCardArtSettingsDialog(const CardArtParams &initial, QWidget *parent) - : QDialog(parent), currentParams(initial) -{ - setWindowTitle(tr("Card Art Settings")); - setMinimumWidth(500); - setupUi(); - - // Seed UI from initial params - if (!initial.cardName.isEmpty()) { - searchBar->setText(initial.cardName); - onCardNameChanged(initial.cardName); - } - marginLSpin->setValue(initial.marginPctL); - marginRSpin->setValue(initial.marginPctR); - verticalOffsetSpin->setValue(initial.verticalOffset); - zoomSpin->setValue(initial.zoom); -} - -CardArtParams UserCardArtSettingsDialog::params() const -{ - return currentParams; -} - -QDoubleSpinBox *UserCardArtSettingsDialog::makeSpinBox(double min, double max, double value, double step) -{ - auto *spin = new QDoubleSpinBox; - spin->setRange(min, max); - spin->setSingleStep(step); - spin->setDecimals(3); - spin->setValue(value); - return spin; -} - -void UserCardArtSettingsDialog::initializeSearchBar() -{ - searchBar = new QLineEdit; - searchBar->setPlaceholderText(tr("Type a card name...")); - - cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this); - cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this); - cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel); - searchModel = new CardSearchModel(cardDatabaseDisplayModel, this); - - proxyModel = new CardCompleterProxyModel(this); - proxyModel->setSourceModel(searchModel); - proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - proxyModel->setFilterRole(Qt::DisplayRole); - - completer = new QCompleter(proxyModel, this); - completer->setCompletionRole(Qt::DisplayRole); - completer->setCompletionMode(QCompleter::PopupCompletion); - completer->setCaseSensitivity(Qt::CaseInsensitive); - completer->setFilterMode(Qt::MatchContains); - completer->setMaxVisibleItems(15); - searchBar->setCompleter(completer); - - connect(searchBar, &QLineEdit::textEdited, searchModel, &CardSearchModel::updateSearchResults); - connect(searchBar, &QLineEdit::textEdited, this, [this](const QString &text) { - const QString pattern = ".*" + QRegularExpression::escape(text) + ".*"; - proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption)); - if (!text.isEmpty()) { - completer->complete(); - } - }); - - connect(completer, static_cast(&QCompleter::activated), this, - [this](const QString &completion) { - if (searchBar->text() != completion) { - searchBar->setText(completion); - searchBar->setCursorPosition(searchBar->text().length()); - } - onCardNameChanged(completion); - }); - - // Also trigger a load when the user hits Return on a typed name - connect(searchBar, &QLineEdit::returnPressed, this, [this]() { onCardNameChanged(searchBar->text()); }); -} - -void UserCardArtSettingsDialog::setupUi() -{ - initializeSearchBar(); - - providerComboBox = new QComboBox; - connect(providerComboBox, &QComboBox::currentIndexChanged, this, [this]() { - currentParams.cardProviderId = providerComboBox->currentData().toString(); - reloadPreview(); - onParamChanged(); - }); - - marginLSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctL, 0.01); - marginRSpin = makeSpinBox(0.0, 0.95, currentParams.marginPctR, 0.01); - verticalOffsetSpin = makeSpinBox(0.0, 1.0, currentParams.verticalOffset, 0.01); - zoomSpin = makeSpinBox(0.1, 4.0, currentParams.zoom, 0.05); - - auto *form = new QFormLayout; - form->addRow(tr("Card name:"), searchBar); - form->addRow(tr("Card ProviderId:"), providerComboBox); - form->addRow(tr("Left margin (%):"), marginLSpin); - form->addRow(tr("Right margin (%):"), marginRSpin); - form->addRow(tr("Vertical offset:"), verticalOffsetSpin); - form->addRow(tr("Zoom:"), zoomSpin); - - auto *controlsGroup = new QGroupBox(tr("Parameters")); - controlsGroup->setLayout(form); - - preview = new CardArtPreviewWidget; - - auto *previewLayout = new QVBoxLayout; - previewLayout->addWidget(preview); - auto *previewGroup = new QGroupBox(tr("Preview")); - previewGroup->setLayout(previewLayout); - - auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - auto *removeBtn = new QPushButton(tr("Remove Banner Card")); - buttons->addButton(removeBtn, QDialogButtonBox::ResetRole); - - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(removeBtn, &QPushButton::clicked, this, [this]() { - currentParams = CardArtParams{}; // empty cardName signals removal - accept(); - }); - - auto *root = new QVBoxLayout; - root->addWidget(controlsGroup); - root->addWidget(previewGroup); - root->addWidget(buttons); - setLayout(root); - - connect(marginLSpin, &QDoubleSpinBox::valueChanged, this, &UserCardArtSettingsDialog::onParamChanged); - connect(marginRSpin, &QDoubleSpinBox::valueChanged, this, &UserCardArtSettingsDialog::onParamChanged); - connect(verticalOffsetSpin, &QDoubleSpinBox::valueChanged, this, &UserCardArtSettingsDialog::onParamChanged); - connect(zoomSpin, &QDoubleSpinBox::valueChanged, this, &UserCardArtSettingsDialog::onParamChanged); -} - -void UserCardArtSettingsDialog::populateProviderCombo(const QString &cardName) -{ - providerComboBox->clear(); - - auto card = CardDatabaseManager::query()->getCard({cardName}); - - const auto &sets = card.getInfo().getSets(); - - for (const auto &printings : sets) { - for (const auto &p : printings) { - - QString setName = p.getSet()->getLongName(); - QString collector = p.getProperty("num"); - QString uuid = p.getUuid(); - - QString label = setName; - - if (!collector.isEmpty()) { - label += " #" + collector; - } - - providerComboBox->addItem(label, uuid); - } - } -} - -void UserCardArtSettingsDialog::onCardNameChanged(const QString &name) -{ - if (name.isEmpty()) { - currentPixmap = QPixmap(); - preview->setPixmap(currentPixmap); - return; - } - - const ExactCard card = CardDatabaseManager::query()->getCard({name}); - if (!card) { - currentPixmap = QPixmap(); - preview->setPixmap(currentPixmap); - providerComboBox->clear(); - return; - } - - currentParams.cardName = name; - - populateProviderCombo(name); - - if (providerComboBox->count() == 0) { - // No printings found for this card; nothing to preview. - currentPixmap = QPixmap(); - preview->setPixmap(currentPixmap); - currentParams.cardProviderId.clear(); - return; - } - - currentParams.cardProviderId = providerComboBox->currentData().toString(); - reloadPreview(); -} - -void UserCardArtSettingsDialog::reloadPreview() -{ - if (currentParams.cardName.isEmpty()) { - return; - } - - ExactCard card = CardDatabaseManager::query()->getCard({currentParams.cardName, currentParams.cardProviderId}); - if (!card) { - return; - } - - // CardPictureLoader::getPixmap() is async on a cache miss: it enqueues a - // background download and returns a null pixmap immediately. When that - // download finishes, CardPictureLoader::imageLoaded() caches the result - // and calls card.emitPixmapUpdated(), which emits pixmapUpdated() on the - // underlying CardInfo (see exact_card.h). Listen for that, scoped to - // whichever CardInfo we just asked for, so the preview catches up once - // the image actually arrives instead of staying on the placeholder. - // - // Disconnect any previous listener first -- otherwise switching cards - // repeatedly stacks up connections to old CardInfo objects, each of - // which would still fire reloadPreview() (harmlessly, but wastefully) - // whenever ITS art finishes loading later. - disconnect(pixmapUpdatedConnection); - - QPixmap fullRes; - CardPictureLoader::getPixmap(fullRes, card, QSize(745, 1040)); - - if (fullRes.isNull()) { - // Not loaded yet -- wait for the signal instead of giving up. - // card.getCardPtr() is a CardInfoPtr (QSharedPointer); - // .data() gives the raw QObject* needed for connect(). - CardInfo *cardInfo = card.getCardPtr().data(); - if (cardInfo) { - pixmapUpdatedConnection = connect(cardInfo, &CardInfo::pixmapUpdated, this, [this]() { reloadPreview(); }); - } - return; - } - - currentPixmap = UserCardArtProvider::cropCardArt(fullRes); - preview->setPixmap(currentPixmap); - preview->setParams(currentParams); -} - -void UserCardArtSettingsDialog::onParamChanged() -{ - currentParams.marginPctL = marginLSpin->value(); - currentParams.marginPctR = marginRSpin->value(); - currentParams.verticalOffset = verticalOffsetSpin->value(); - currentParams.zoom = zoomSpin->value(); - preview->setParams(currentParams); -} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h b/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h deleted file mode 100644 index 018043278..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_card_settings_dialog.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef COCKATRICE_USER_CARD_ART_SETTINGS_DIALOG_H -#define COCKATRICE_USER_CARD_ART_SETTINGS_DIALOG_H - -#include "user_list_painter.h" - -#include -#include -#include - -class QCompleter; -class QLineEdit; -class QDoubleSpinBox; -class CardDatabaseModel; -class CardDatabaseDisplayModel; -class CardSearchModel; -class CardCompleterProxyModel; - -class CardArtPreviewWidget : public QWidget -{ - Q_OBJECT - -public: - explicit CardArtPreviewWidget(QWidget *parent = nullptr); - - void setPixmap(const QPixmap &pixmap); - void setParams(const CardArtParams ¶ms); - -protected: - void paintEvent(QPaintEvent *event) override; - -private: - QPixmap sourcePixmap; - CardArtParams params; -}; - -class UserCardArtSettingsDialog : public QDialog -{ - Q_OBJECT - -public: - explicit UserCardArtSettingsDialog(const CardArtParams &initial = {}, QWidget *parent = nullptr); - - CardArtParams params() const; - -private slots: - void onCardNameChanged(const QString &name); - void reloadPreview(); - void onParamChanged(); - -private: - void setupUi(); - void populateProviderCombo(const QString &cardName); - void initializeSearchBar(); - QDoubleSpinBox *makeSpinBox(double min, double max, double value, double step); - - QLineEdit *searchBar; - QCompleter *completer; - CardDatabaseModel *cardDatabaseModel; - CardDatabaseDisplayModel *cardDatabaseDisplayModel; - CardSearchModel *searchModel; - CardCompleterProxyModel *proxyModel; - - QComboBox *providerComboBox; - - QMetaObject::Connection pixmapUpdatedConnection; - - QDoubleSpinBox *marginLSpin; - QDoubleSpinBox *marginRSpin; - QDoubleSpinBox *verticalOffsetSpin; - QDoubleSpinBox *zoomSpin; - CardArtPreviewWidget *preview; - - QPixmap currentPixmap; - CardArtParams currentParams; -}; - -#endif // COCKATRICE_USER_CARD_ART_SETTINGS_DIALOG_H \ No newline at end of file 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 11fd02d80..faa96fa1f 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.cpp @@ -542,113 +542,3 @@ void UserContextMenu::showContextMenu(const QPoint &pos, delete menu; } - -void UserContextMenu::execChat(const QString &userName) -{ - emit openMessageDialog(userName, true); -} - -void UserContextMenu::execDetails(const QString &userName) -{ - auto *w = new UserInfoBox(client, false, static_cast(parent()), - Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); - w->setAttribute(Qt::WA_DeleteOnClose); - w->updateInfo(userName); -} - -void UserContextMenu::execShowGames(const QString &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); -} - -void UserContextMenu::execAddToBuddy(const QString &userName) -{ - Command_AddToList cmd; - cmd.set_list("buddy"); - cmd.set_user_name(userName.toStdString()); - client->sendCommand(client->prepareSessionCommand(cmd)); -} - -void UserContextMenu::execRemoveFromBuddy(const QString &userName) -{ - Command_RemoveFromList cmd; - cmd.set_list("buddy"); - cmd.set_user_name(userName.toStdString()); - client->sendCommand(client->prepareSessionCommand(cmd)); -} - -void UserContextMenu::execAddToIgnore(const QString &userName) -{ - Command_AddToList cmd; - cmd.set_list("ignore"); - cmd.set_user_name(userName.toStdString()); - client->sendCommand(client->prepareSessionCommand(cmd)); -} - -void UserContextMenu::execRemoveFromIgnore(const QString &userName) -{ - Command_RemoveFromList cmd; - cmd.set_list("ignore"); - cmd.set_user_name(userName.toStdString()); - client->sendCommand(client->prepareSessionCommand(cmd)); -} - -void UserContextMenu::execBan(const QString &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); -} - -void UserContextMenu::execWarn(const QString &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); -} - -void UserContextMenu::execBanHistory(const QString &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); -} - -void UserContextMenu::execWarnHistory(const QString &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); -} - -void UserContextMenu::execAdminNotes(const QString &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); -} - -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); - cmd.set_should_be_judge(shouldBeJudge); - PendingCommand *pend = client->prepareAdminCommand(cmd); - connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse); - client->sendCommand(pend); -} \ No newline at end of file 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 28173bfbc..b0ff89816 100644 --- a/cockatrice/src/interface/widgets/server/user/user_context_menu.h +++ b/cockatrice/src/interface/widgets/server/user/user_context_menu.h @@ -74,27 +74,6 @@ public: int playerId, const QString &deckHash, ChatView *chatView = nullptr); - - const UserListProxy *getUserListProxy() const - { - return userListProxy; - } - - // Individual action entry points — used by UserInfoPopup to trigger - // actions without re-running the full context menu flow. - void execChat(const QString &userName); - void execDetails(const QString &userName); - void execShowGames(const QString &userName); - void execAddToBuddy(const QString &userName); - void execRemoveFromBuddy(const QString &userName); - void execAddToIgnore(const QString &userName); - void execRemoveFromIgnore(const QString &userName); - 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, bool shouldBeJudge); }; #endif diff --git a/cockatrice/src/interface/widgets/server/user/user_info_box.cpp b/cockatrice/src/interface/widgets/server/user/user_info_box.cpp index 416cd42e3..e41ae6e75 100644 --- a/cockatrice/src/interface/widgets/server/user/user_info_box.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_info_box.cpp @@ -5,7 +5,6 @@ #include "../../interface/widgets/dialogs/dlg_edit_password.h" #include "../../interface/widgets/dialogs/dlg_edit_user.h" #include "../../interface/widgets/utility/get_text_with_max.h" -#include "user_card_settings_dialog.h" #include #include @@ -62,13 +61,11 @@ UserInfoBox::UserInfoBox(AbstractClient *_client, bool _editable, QWidget *paren buttonsLayout->addWidget(&editButton); buttonsLayout->addWidget(&passwordButton); buttonsLayout->addWidget(&avatarButton); - buttonsLayout->addWidget(&bannerCardButton); mainLayout->addLayout(buttonsLayout, 7, 0, 1, 3); connect(&editButton, &QPushButton::clicked, this, &UserInfoBox::actEdit); connect(&passwordButton, &QPushButton::clicked, this, &UserInfoBox::actPassword); connect(&avatarButton, &QPushButton::clicked, this, &UserInfoBox::actAvatar); - connect(&bannerCardButton, &QPushButton::clicked, this, &UserInfoBox::actBannerCard); } setWindowTitle(tr("User Information")); @@ -86,15 +83,11 @@ void UserInfoBox::retranslateUi() editButton.setText(tr("Edit")); passwordButton.setText(tr("Change password")); avatarButton.setText(tr("Change avatar")); - bannerCardButton.setText(tr("Edit Banner Card")); } void UserInfoBox::updateInfo(const ServerInfo_User &user) { - currentUserInfo = user; - hasUserInfo = true; - - const UserLevelFlags userLevel(user.user_level()); + userLevel = UserLevelFlags(user.user_level()); pawnColors = user.pawn_colors(); privLevel = QString::fromStdString(user.privlevel()); @@ -313,49 +306,6 @@ void UserInfoBox::actAvatar() client->sendCommand(pend); } -void UserInfoBox::actBannerCard() -{ - CardArtParams initial; - if (hasUserInfo && currentUserInfo.has_card_art_params()) { - const auto &cap = currentUserInfo.card_art_params(); - initial.cardName = QString::fromStdString(cap.card_name()); - initial.marginPctL = cap.margin_pct_l(); - initial.marginPctR = cap.margin_pct_r(); - initial.verticalOffset = cap.vertical_offset(); - initial.zoom = cap.zoom(); - } - - UserCardArtSettingsDialog dlg(initial, this); - if (dlg.exec() != QDialog::Accepted) { - return; - } - - const CardArtParams p = dlg.params(); - - Command_SetCardArtParams cmd; - cmd.set_card_name(p.cardName.toStdString()); - if (!p.cardName.isEmpty()) { - cmd.set_card_provider_id(p.cardProviderId.toStdString()); - cmd.set_margin_pct_l(p.marginPctL); - cmd.set_margin_pct_r(p.marginPctR); - cmd.set_vertical_offset(p.verticalOffset); - cmd.set_zoom(p.zoom); - } - - PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, [p, this](const Response &r) { - if (r.response_code() != Response::RespOk) { - QMessageBox::critical(this, tr("Error"), - tr("The selected card is blacklisted on this server or another error occurred.")); - } else { - updateInfo(nameLabel.text()); // re-fetch so currentUserInfo reflects the change - QMessageBox::information(this, tr("Information"), - p.cardName.isEmpty() ? tr("Banner card removed.") : tr("Banner card updated.")); - } - }); - client->sendCommand(pend); -} - void UserInfoBox::processEditResponse(const Response &r) { switch (r.response_code()) { diff --git a/cockatrice/src/interface/widgets/server/user/user_info_box.h b/cockatrice/src/interface/widgets/server/user/user_info_box.h index 955cb9d3d..055ac0096 100644 --- a/cockatrice/src/interface/widgets/server/user/user_info_box.h +++ b/cockatrice/src/interface/widgets/server/user/user_info_box.h @@ -12,7 +12,6 @@ #include #include #include -#include #include class AbstractClient; @@ -26,11 +25,9 @@ private: bool editable; QLabel avatarPic, userLevelIcon, nameLabel, realNameLabel1, realNameLabel2, countryLabel1, countryLabel2, countryLabel3, userLevelLabel1, userLevelLabel2, accountAgeLabel1, accountAgeLabel2; - QPushButton editButton, passwordButton, avatarButton, bannerCardButton; + QPushButton editButton, passwordButton, avatarButton; QPixmap avatarPixmap; bool hasAvatar; - ServerInfo_User currentUserInfo; - bool hasUserInfo = false; UserLevelFlags userLevel; ServerInfo_User::PawnColorsOverride pawnColors; QString privLevel; @@ -40,7 +37,6 @@ private: public: UserInfoBox(AbstractClient *_client, bool editable, QWidget *parent = nullptr, Qt::WindowFlags flags = {}); void retranslateUi(); - private slots: void processResponse(const Response &r); void processEditResponse(const Response &r); @@ -51,7 +47,6 @@ private slots: void actEditInternal(const Response &r); void actPassword(); void actAvatar(); - void actBannerCard(); public slots: void updateInfo(const ServerInfo_User &user); void updateInfo(const QString &userName); diff --git a/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp b/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp deleted file mode 100644 index 2b4dcb8ed..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_info_popup.cpp +++ /dev/null @@ -1,656 +0,0 @@ -#include "user_info_popup.h" - -#include "../../interface/pixel_map_generator.h" -#include "../../interface/widgets/tabs/tab_supervisor.h" -#include "user_list_painter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ── Compact game row delegate ───────────────────────────────────────────────── - -class PopupGameDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - - QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override - { - return QSize(0, 38); - } - - void paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const override - { - const QVariant var = index.data(PopupRoles::GameData); - if (!var.isValid()) { - QStyledItemDelegate::paint(p, option, index); - return; - } - - p->save(); - p->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); - - const QRect rect = option.rect; - const ServerInfo_Game game = var.value(); - const bool selected = option.state & QStyle::State_Selected; - - p->fillRect(rect, selected ? QColor(35, 45, 62) : QColor(14, 18, 26)); - - // State colour dot - const QColor dot = game.started() ? QColor(239, 68, 68) - : (game.player_count() >= game.max_players()) ? QColor(249, 115, 22) - : game.with_password() ? QColor(59, 130, 246) - : QColor(34, 197, 94); - p->setPen(Qt::NoPen); - p->setBrush(dot); - p->drawEllipse(QRectF(rect.left() + 9, rect.top() + (rect.height() - 8) / 2.0, 8, 8)); - - // Game title (bold, elided) - QFont tf = option.font; - tf.setBold(true); - p->setFont(tf); - p->setPen(QColor(205, 215, 230)); - const int textX = rect.left() + 26; - const int countW = 52; - const int titleW = rect.width() - textX - countW - 6; - p->drawText(QRect(textX, rect.top(), titleW, rect.height()), Qt::AlignVCenter | Qt::AlignLeft, - QFontMetrics(tf).elidedText(QString::fromStdString(game.description()), Qt::ElideRight, titleW)); - - // Player count - const bool full = game.player_count() >= game.max_players(); - p->setFont(option.font); - p->setPen(full ? QColor(249, 115, 22) : QColor(110, 128, 150)); - p->drawText(QRect(rect.right() - countW - 4, rect.top(), countW, rect.height()), - Qt::AlignVCenter | Qt::AlignRight, - QStringLiteral("%1/%2").arg(game.player_count()).arg(game.max_players())); - - // Row separator - p->setPen(QColor(24, 32, 44)); - p->drawLine(rect.bottomLeft(), rect.bottomRight()); - - p->restore(); - } -}; - -// ── UserInfoHeaderWidget ────────────────────────────────────────────────────── - -UserInfoHeaderWidget::UserInfoHeaderWidget(QWidget *parent) : QWidget(parent) -{ - setFixedHeight(HeaderHeight); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); -} - -void UserInfoHeaderWidget::setUserData(const ServerInfo_User &user, - bool online, - const QPixmap &avatar, - const QPixmap &cardArt, - const CardArtParams ¶ms) -{ - m_user = user; - m_online = online; - m_avatar = avatar; - m_cardArt = cardArt; - m_params = params; - update(); -} - -void UserInfoHeaderWidget::paintEvent(QPaintEvent *) -{ - QPainter p(this); - p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); - - const QRect rect = this->rect(); - const UserLevelFlags level(m_user.user_level()); - const QString userName = QString::fromStdString(m_user.name()); - const QString privLevel = QString::fromStdString(m_user.privlevel()); - - // Dark base - p.fillRect(rect, QColor(14, 18, 26)); - - // ── Card art background ─────────────────────────────────────────────────── - if (!m_cardArt.isNull()) { - const int w = rect.width(); - const int h = rect.height(); - const int mL = qRound(w * m_params.marginPctL); - const int mR = qRound(w * m_params.marginPctR); - const int dW = w - mL - mR; - - const double base = qMax(double(dW) / m_cardArt.width(), double(h) / m_cardArt.height()); - const double scale = base * m_params.zoom; - const int sW = qRound(m_cardArt.width() * scale); - const int sH = qRound(m_cardArt.height() * scale); - - const QPixmap scaled = m_cardArt.scaled(sW, sH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - const int srcX = (sW - dW) / 2; - const int srcY = qBound(0, qRound((sH - h) * m_params.verticalOffset), qMax(0, sH - h)); - - QImage img = scaled.copy(srcX, srcY, dW, h).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); - { - QPainter mask(&img); - mask.setCompositionMode(QPainter::CompositionMode_DestinationIn); - QLinearGradient g(0, 0, img.width(), 0); - g.setColorAt(0.00, Qt::transparent); - g.setColorAt(0.18, Qt::white); - g.setColorAt(0.82, Qt::white); - g.setColorAt(1.00, Qt::transparent); - mask.fillRect(img.rect(), g); - } - p.setOpacity(0.48); - p.drawImage(mL, 0, img); - p.setOpacity(1.0); - } - - // Bottom gradient overlay so avatar and text are always legible - { - QLinearGradient ov(0, 0, 0, rect.height()); - ov.setColorAt(0.0, QColor(14, 18, 26, 0)); - ov.setColorAt(0.55, QColor(14, 18, 26, 110)); - ov.setColorAt(1.0, QColor(14, 18, 26, 230)); - p.fillRect(rect, ov); - } - - // ── Avatar ──────────────────────────────────────────────────────────────── - const QColor accent = [&]() -> QColor { - if (level.testFlag(ServerInfo_User::IsAdmin)) { - return QColor(245, 158, 11); - } - if (level.testFlag(ServerInfo_User::IsModerator)) { - return QColor(59, 130, 246); - } - if (level.testFlag(ServerInfo_User::IsJudge)) { - return QColor(168, 85, 247); - } - return QColor(100, 116, 139); - }(); - - const int ax = LeftPad; - const int ay = rect.height() - AvatarSize - 10; - const QRect ar(ax, ay, AvatarSize, AvatarSize); - - QPainterPath clip; - clip.addEllipse(ar); - p.save(); - p.setClipPath(clip); - - if (!m_avatar.isNull()) { - p.drawPixmap(ar, m_avatar.scaled(ar.size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); - } else { - p.setPen(Qt::NoPen); - p.setBrush(accent.darker(200)); - p.drawEllipse(ar); - const QPixmap pawn = - UserLevelPixmapGenerator::generatePixmap(AvatarPawnSize, level, m_user.pawn_colors(), false, privLevel); - p.drawPixmap(ar.center().x() - AvatarPawnSize / 2, ar.center().y() - AvatarPawnSize / 2, pawn); - } - p.restore(); - - // Status ring - p.setPen(QPen(m_online ? QColor(34, 197, 94) : QColor(70, 80, 95), 2.5)); - p.setBrush(Qt::NoBrush); - p.drawEllipse(QRectF(ar).adjusted(-1.25, -1.25, 1.25, 1.25)); - - // ── Username + badge ────────────────────────────────────────────────────── - const int tx = ax + AvatarSize + AvatarToTextGap; - const int tw = rect.width() - tx - 8; - - QFont nf = font(); - nf.setBold(true); - nf.setPointSizeF(nf.pointSizeF() * 1.12); - p.setFont(nf); - p.setPen(m_online ? QColor(220, 228, 240) : QColor(90, 100, 115)); - p.drawText(QRect(tx, ay, tw, AvatarSize / 2 + 4), Qt::AlignBottom | Qt::AlignLeft, - QFontMetrics(nf).elidedText(userName, Qt::ElideRight, tw)); - - // Level / priv badge - struct - { - QString text; - QColor color; - } badge; - if (level.testFlag(ServerInfo_User::IsAdmin)) { - badge = {"ADMIN", QColor(245, 158, 11)}; - } else if (level.testFlag(ServerInfo_User::IsModerator)) { - badge = {"MOD", QColor(59, 130, 246)}; - } else if (level.testFlag(ServerInfo_User::IsJudge)) { - badge = {"JUDGE", QColor(168, 85, 247)}; - } else if (privLevel == "VIP") { - badge = {"VIP", QColor(20, 184, 166)}; - } else if (privLevel == "DONATOR") { - badge = {"DONATOR", QColor(249, 115, 22)}; - } - - if (!badge.text.isEmpty()) { - QFont bf = font(); - bf.setPointSizeF(bf.pointSizeF() * 0.70); - bf.setBold(true); - p.setFont(bf); - const QFontMetrics bfm(bf); - const int bw = bfm.horizontalAdvance(badge.text) + 10; - const QRect br(tx, ay + AvatarSize / 2 + 6, bw, 15); - p.setPen(Qt::NoPen); - p.setBrush(badge.color.darker(160)); - p.drawRoundedRect(br, 3, 3); - p.setPen(badge.color.lighter(150)); - p.drawText(br, Qt::AlignCenter, badge.text); - } -} - -// ── UserInfoPopup ───────────────────────────────────────────────────────────── - -UserInfoPopup::UserInfoPopup(TabSupervisor *ts, - AbstractClient *client, - const QMap *avatarCache, - const QMap *cardArtCache, - const QMap *cardArtParamsMap, - QWidget *parent) - : QFrame(parent, Qt::Tool | Qt::FramelessWindowHint), m_ts(ts), m_client(client), m_avatarCache(avatarCache), - m_cardArtCache(cardArtCache), m_cardArtParamsMap(cardArtParamsMap) -{ - setAttribute(Qt::WA_ShowWithoutActivating); - setFixedWidth(PopupWidth); - setFrameShape(QFrame::NoFrame); - buildUi(); -} - -void UserInfoPopup::buildUi() -{ - setStyleSheet(QStringLiteral("UserInfoPopup {" - " background:#0e1218;" - " border:1px solid #1e2838;" - " border-radius:8px;" - "}")); - - auto *root = new QVBoxLayout(this); - root->setContentsMargins(0, 0, 0, 0); - root->setSpacing(0); - - // Header - m_header = new UserInfoHeaderWidget(this); - root->addWidget(m_header); - - // Action area — rebuilt per user - m_actionArea = new QWidget(this); - m_actionArea->setStyleSheet(QStringLiteral("background:#0e1218;")); - root->addWidget(m_actionArea); - - // Thin separator - auto *sep = new QFrame(this); - sep->setFrameShape(QFrame::HLine); - sep->setStyleSheet(QStringLiteral("color:#1a2434; margin: 0 8px;")); - root->addWidget(sep); - - // Games header row - auto *gh = new QHBoxLayout; - gh->setContentsMargins(10, 4, 8, 2); - auto *gl = new QLabel(tr("Games"), this); - gl->setStyleSheet(QStringLiteral("color:#6882a0; font-size:11px; font-weight:bold; background:transparent;")); - gh->addWidget(gl); - gh->addStretch(); - m_refreshBtn = new QPushButton(QStringLiteral("↻"), this); - m_refreshBtn->setFixedSize(20, 20); - m_refreshBtn->setFlat(true); - m_refreshBtn->setStyleSheet( - QStringLiteral("QPushButton{color:#6882a0;border:none;font-size:14px;background:transparent;}" - "QPushButton:hover{color:white;}")); - connect(m_refreshBtn, &QPushButton::clicked, this, &UserInfoPopup::refreshGames); - gh->addWidget(m_refreshBtn); - root->addLayout(gh); - - // Status label - m_gamesStatus = new QLabel(this); - m_gamesStatus->setAlignment(Qt::AlignCenter); - m_gamesStatus->setStyleSheet( - QStringLiteral("color:#3a4a5e; font-size:11px; padding:10px; background:transparent;")); - root->addWidget(m_gamesStatus); - - // Games list - m_gamesModel = new QStandardItemModel(this); - m_gamesView = new QListView(this); - m_gamesView->setModel(m_gamesModel); - m_gamesView->setItemDelegate(new PopupGameDelegate(m_gamesView)); - m_gamesView->setFrameShape(QFrame::NoFrame); - m_gamesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_gamesView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_gamesView->setMaximumHeight(220); - m_gamesView->setStyleSheet(QStringLiteral("QListView{background:#0e1218;border:none;}" - "QListView::item:selected{background:#232e42;}")); - m_gamesView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_gamesView, &QListView::customContextMenuRequested, this, &UserInfoPopup::onGamesContextMenu); - - root->addWidget(m_gamesView); - - // Close button — positioned absolutely in the top-right corner - m_closeBtn = new QPushButton(QStringLiteral("✕"), this); - m_closeBtn->setFixedSize(22, 22); - m_closeBtn->setFlat(true); - m_closeBtn->setStyleSheet(QStringLiteral("QPushButton{background:rgba(14,18,26,180);color:#607080;" - "border:none;border-radius:11px;font-size:10px;}" - "QPushButton:hover{color:white;background:rgba(200,50,50,200);}")); - connect(m_closeBtn, &QPushButton::clicked, this, &UserInfoPopup::closeRequested); -} - -// ── Action button factory ───────────────────────────────────────────────────── - -static QPushButton *makeBtn(const QString &label, const QString &tip, QWidget *p) -{ - auto *b = new QPushButton(label, p); - b->setToolTip(tip); - b->setFixedHeight(26); - b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - b->setStyleSheet(QStringLiteral("QPushButton{" - " background:#192030;color:#b8c8de;border:1px solid #263040;" - " border-radius:4px;font-size:11px;padding:0 4px;" - "}" - "QPushButton:hover{background:#223050;color:white;}" - "QPushButton:pressed{background:#162030;}" - "QPushButton:disabled{color:#384858;border-color:#192030;}")); - return b; -} - -void UserInfoPopup::rebuildActionButtons(const ServerInfo_User &userInfo, bool online, bool isBuddy, bool isIgnored) -{ - // Clear previous contents - delete m_actionArea->layout(); - const auto old = m_actionArea->findChildren(QString{}, Qt::FindDirectChildrenOnly); - for (auto *w : old) { - w->deleteLater(); - } - - const QString name = QString::fromStdString(userInfo.name()); - const auto ownLevel = UserLevelFlags(m_ts->getUserInfo()->user_level()); - const bool isSelf = (name == QString::fromStdString(m_ts->getUserInfo()->name())); - const bool isMod = ownLevel.testFlag(ServerInfo_User::IsModerator); - const bool isAdmin = ownLevel.testFlag(ServerInfo_User::IsAdmin); - const auto their = UserLevelFlags(userInfo.user_level()); - const bool isReg = their.testFlag(ServerInfo_User::IsRegistered); - - auto *grid = new QGridLayout(m_actionArea); - grid->setContentsMargins(8, 6, 8, 6); - grid->setSpacing(4); - - int row = 0, col = 0; - const int cols = 3; - auto add = [&](QPushButton *btn) { - grid->addWidget(btn, row, col); - if (++col == cols) { - col = 0; - ++row; - } - }; - - // ── Always visible ──────────────────────────────────────────────────────── - auto *chat = makeBtn(tr("Chat"), tr("Open private chat"), m_actionArea); - chat->setEnabled(!isSelf && online); - connect(chat, &QPushButton::clicked, this, [this, name] { emit chatRequested(name); }); - add(chat); - - auto *prof = makeBtn(tr("Profile"), tr("View user profile"), m_actionArea); - connect(prof, &QPushButton::clicked, this, [this, name] { emit detailsRequested(name); }); - add(prof); - - auto *games = makeBtn(tr("Games"), tr("Show this user's games"), m_actionArea); - games->setEnabled(!isSelf && online); - connect(games, &QPushButton::clicked, this, [this, name] { emit showGamesRequested(name); }); - add(games); - - // ── Buddy / ignore (registered users only) ──────────────────────────────── - if (!isSelf && isReg) { - if (isBuddy) { - auto *b = makeBtn(tr("− Buddy"), tr("Remove from buddy list"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit removeBuddyRequested(name); }); - add(b); - } else { - auto *b = makeBtn(tr("+ Buddy"), tr("Add to buddy list"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit addBuddyRequested(name); }); - add(b); - } - if (isIgnored) { - auto *b = makeBtn(tr("− Ignore"), tr("Remove from ignore list"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit removeIgnoreRequested(name); }); - add(b); - } else { - auto *b = makeBtn(tr("+ Ignore"), tr("Add to ignore list"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit addIgnoreRequested(name); }); - add(b); - } - } - - // ── Moderator actions ───────────────────────────────────────────────────── - if (!isSelf && (isMod || isAdmin)) { - if (col != 0) { - ++row; - col = 0; - } // start mod section on a fresh row - - auto *ban = makeBtn(tr("Ban"), tr("Ban from server"), m_actionArea); - auto *warn = makeBtn(tr("Warn"), tr("Warn user"), m_actionArea); - auto *bLog = makeBtn(tr("Ban log"), tr("View ban history"), m_actionArea); - auto *wLog = makeBtn(tr("Warn log"), tr("View warning history"), m_actionArea); - connect(ban, &QPushButton::clicked, this, [this, name] { emit banRequested(name); }); - connect(warn, &QPushButton::clicked, this, [this, name] { emit warnRequested(name); }); - connect(bLog, &QPushButton::clicked, this, [this, name] { emit banHistoryRequested(name); }); - connect(wLog, &QPushButton::clicked, this, [this, name] { emit warnHistoryRequested(name); }); - add(ban); - add(warn); - add(bLog); - add(wLog); - } - - // ── Admin actions ───────────────────────────────────────────────────────── - if (!isSelf && isAdmin) { - auto *notes = makeBtn(tr("Notes"), tr("View admin notes"), m_actionArea); - connect(notes, &QPushButton::clicked, this, [this, name] { emit adminNotesRequested(name); }); - add(notes); - - if (their.testFlag(ServerInfo_User::IsModerator)) { - auto *b = makeBtn(tr("− Mod"), tr("Demote from moderator"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit demoteFromModRequested(name); }); - add(b); - } else if (isReg) { - auto *b = makeBtn(tr("+ Mod"), tr("Promote to moderator"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit promoteToModRequested(name); }); - add(b); - } - if (their.testFlag(ServerInfo_User::IsJudge)) { - auto *b = makeBtn(tr("− Judge"), tr("Demote from judge"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit demoteFromJudgeRequested(name); }); - add(b); - } else if (isReg) { - auto *b = makeBtn(tr("+ Judge"), tr("Promote to judge"), m_actionArea); - connect(b, &QPushButton::clicked, this, [this, name] { emit promoteToJudgeRequested(name); }); - add(b); - } - } - - m_actionArea->adjustSize(); -} - -void UserInfoPopup::updateActionButtons(const ServerInfo_User &userInfo, bool online, bool isBuddy, bool isIgnored) -{ - rebuildActionButtons(userInfo, online, isBuddy, isIgnored); - adjustSize(); -} - -void UserInfoPopup::onGamesContextMenu(const QPoint &pos) -{ - const QModelIndex idx = m_gamesView->indexAt(pos); - if (!idx.isValid()) { - return; - } - - const QVariant var = idx.data(PopupRoles::GameData); - if (!var.isValid()) { - return; - } - const ServerInfo_Game game = var.value(); - - QMenu menu(this); - menu.setStyleSheet( - QStringLiteral("QMenu{background:#12182a;color:#c8d8ec;border:1px solid #1e2838;border-radius:4px;}" - "QMenu::item:selected{background:#223050;}")); - - const bool canJoin = !game.started() && game.player_count() < game.max_players(); - QAction *join = menu.addAction(tr("Join game")); - join->setEnabled(canJoin); - - QAction *spec = nullptr; - if (game.spectators_allowed()) { - spec = menu.addAction(tr("Spectate")); - } - - const QAction *chosen = menu.exec(m_gamesView->viewport()->mapToGlobal(pos)); - if (!chosen) { - return; - } - - if (chosen == join) { - emit joinGameRequested(game.game_id(), game.room_id(), false); - } else if (spec && chosen == spec) { - emit joinGameRequested(game.game_id(), game.room_id(), true); - } -} - -// ── showForUser ─────────────────────────────────────────────────────────────── - -void UserInfoPopup::showForUser(const QString &userName, - const ServerInfo_User &userInfo, - bool online, - bool isBuddy, - bool isIgnored) -{ - m_currentUser = userName; - m_currentUserInfo = userInfo; - m_currentOnline = online; - - // Header - const QPixmap avatar = m_avatarCache ? m_avatarCache->value(userName) : QPixmap{}; - const CardArtParams params = (m_cardArtParamsMap && m_cardArtParamsMap->contains(userName)) - ? m_cardArtParamsMap->value(userName) - : CardArtParams{}; - const QString artKey = userName + u'|' + params.cardName + u'|' + params.cardProviderId; - const QPixmap cardArt = (m_cardArtCache && !params.cardName.isEmpty()) ? m_cardArtCache->value(artKey) : QPixmap{}; - m_header->setUserData(userInfo, online, avatar, cardArt, params); - - // Actions - rebuildActionButtons(userInfo, online, isBuddy, isIgnored); - - // Games list reset - m_gamesModel->clear(); - m_gamesView->hide(); - m_gamesStatus->setText(tr("Loading games…")); - m_gamesStatus->show(); - - // Close button — top-right corner, above everything - m_closeBtn->move(PopupWidth - m_closeBtn->width() - 6, 6); - m_closeBtn->raise(); - - adjustSize(); - fetchGames(); -} - -// ── Games fetch ─────────────────────────────────────────────────────────────── - -void UserInfoPopup::fetchGames() -{ - if (!m_client || m_currentUser.isEmpty()) { - return; - } - - Command_GetGamesOfUser cmd; - cmd.set_user_name(m_currentUser.toStdString()); - - const QString snapshot = m_currentUser; - PendingCommand *pend = m_client->prepareSessionCommand(cmd); - connect(pend, &PendingCommand::finished, this, - [this, snapshot](const Response &r) { onGamesReceived(r, snapshot); }); - m_client->sendCommand(pend); -} - -void UserInfoPopup::onGamesReceived(const Response &r, const QString &forUser) -{ - if (forUser != m_currentUser) { - return; // stale response — different user showing now - } - - m_gamesModel->clear(); - - if (r.response_code() != Response::RespOk) { - m_gamesStatus->setText(tr("Could not load games.")); - m_gamesStatus->show(); - m_gamesView->hide(); - return; - } - - const auto &resp = r.GetExtension(Response_GetGamesOfUser::ext); - if (resp.game_list_size() == 0) { - m_gamesStatus->setText(tr("No active games.")); - m_gamesStatus->show(); - m_gamesView->hide(); - return; - } - - for (int i = 0; i < resp.game_list_size(); ++i) { - auto *item = new QStandardItem; - item->setData(QVariant::fromValue(resp.game_list(i)), PopupRoles::GameData); - item->setEditable(false); - m_gamesModel->appendRow(item); - } - - m_gamesStatus->hide(); - m_gamesView->show(); - - // Fit exactly to the number of visible rows, scroll when more than 5 - constexpr int rowH = 38; // must match PopupGameDelegate::sizeHint - constexpr int maxRows = 5; - const int count = m_gamesModel->rowCount(); - const int visible = qMin(count, maxRows); - m_gamesView->setFixedHeight(visible * rowH + 2); - m_gamesView->setVerticalScrollBarPolicy(count > maxRows ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff); - - adjustSize(); -} - -void UserInfoPopup::refreshGames() -{ - m_gamesModel->clear(); - m_gamesView->hide(); - m_gamesStatus->setText(tr("Loading games…")); - m_gamesStatus->show(); - fetchGames(); -} - -// ── Mouse events ────────────────────────────────────────────────────────────── - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -void UserInfoPopup::enterEvent(QEnterEvent *e) -{ - QFrame::enterEvent(e); - emit mouseEnteredPopup(); -} -#else -void UserInfoPopup::enterEvent(QEvent *e) -{ - QFrame::enterEvent(e); - emit mouseEnteredPopup(); -} -#endif -void UserInfoPopup::leaveEvent(QEvent *e) -{ - QFrame::leaveEvent(e); - emit mouseLeftPopup(); -} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_info_popup.h b/cockatrice/src/interface/widgets/server/user/user_info_popup.h deleted file mode 100644 index 0e03147c4..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_info_popup.h +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef COCKATRICE_USER_INFO_POPUP_H -#define COCKATRICE_USER_INFO_POPUP_H - -#include "../../interface/widgets/server/game_type_map.h" -#include "user_list_painter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class AbstractClient; -class QLabel; -class QPushButton; -class TabSupervisor; - -// ── Roles ───────────────────────────────────────────────────────────────────── - -namespace PopupRoles -{ -constexpr int GameData = Qt::UserRole + 10; -} - -// ── Header widget ───────────────────────────────────────────────────────────── - -/** - * @class UserInfoHeaderWidget - * @brief Paints the enlarged banner card art + circular avatar section at the - * top of the UserInfoPopup. - * - * Layout mirrors UserListPainter but at a larger scale: the card art fills the - * full width as a semi-transparent background, a bottom gradient ensures the - * avatar and username text remain legible, and the status ring colour matches - * the UserListPainter convention. - */ -class UserInfoHeaderWidget : public QWidget -{ - Q_OBJECT - - static constexpr int HeaderHeight = 130; - static constexpr int AvatarSize = 68; - static constexpr int AvatarPawnSize = 46; - static constexpr int LeftPad = 14; - static constexpr int AvatarToTextGap = 10; - -public: - explicit UserInfoHeaderWidget(QWidget *parent = nullptr); - - void setUserData(const ServerInfo_User &user, - bool online, - const QPixmap &avatar, - const QPixmap &cardArt, - const CardArtParams ¶ms); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - ServerInfo_User m_user; - bool m_online = false; - QPixmap m_avatar; - QPixmap m_cardArt; - CardArtParams m_params; -}; - -// ── Main popup ──────────────────────────────────────────────────────────────── - -/** - * @class UserInfoPopup - * @brief Floating panel showing an enlarged user card, quick action buttons, - * and a live scrollable games list. - * - * Lifecycle (mirrors DeckEditorDeckDockWidget): - * - showForUser() — populate, position externally, call show() - * - mouseEnteredPopup / mouseLeftPopup — caller manages hide timer - * - closeRequested() — emitted by the internal close button - * - * The popup is a Qt::Tool frameless child so windowOpacity animations and - * move() in screen coordinates work identically to CardInfoPictureEnlargedWidget. - * - * Action signals map 1-to-1 to UserContextMenu::exec*() methods so all action - * logic stays in one place. - */ -class UserInfoPopup : public QFrame -{ - Q_OBJECT - - static constexpr int PopupWidth = 316; - -public: - explicit UserInfoPopup(TabSupervisor *tabSupervisor, - AbstractClient *client, - const QMap *avatarCache, - const QMap *cardArtCache, - const QMap *cardArtParamsMap, - QWidget *parent); - - /** - * Populate the popup for @p userName and kick off a game list fetch. - * Call show() / move() externally after this. - */ - void - showForUser(const QString &userName, const ServerInfo_User &userInfo, bool online, bool isBuddy, bool isIgnored); - void fetchGames(); - - [[nodiscard]] QString currentUser() const - { - return m_currentUser; - } - - /** Called when buddy/ignore status changes externally while popup is open. */ - void updateActionButtons(const ServerInfo_User &userInfo, bool online, bool isBuddy, bool isIgnored); - -signals: - void mouseEnteredPopup(); - void mouseLeftPopup(); - void closeRequested(); - - /** Emitted when the user requests joining or spectating a game in the list. */ - void joinGameRequested(int gameId, int roomId, bool asSpectator); - - // ── Action signals — connect to UserContextMenu::exec*() ────────────────── - void chatRequested(const QString &userName); - void detailsRequested(const QString &userName); - void showGamesRequested(const QString &userName); - void addBuddyRequested(const QString &userName); - void removeBuddyRequested(const QString &userName); - void addIgnoreRequested(const QString &userName); - void removeIgnoreRequested(const QString &userName); - void banRequested(const QString &userName); - void warnRequested(const QString &userName); - void banHistoryRequested(const QString &userName); - void warnHistoryRequested(const QString &userName); - void adminNotesRequested(const QString &userName); - void promoteToModRequested(const QString &userName); - void demoteFromModRequested(const QString &userName); - void promoteToJudgeRequested(const QString &userName); - void demoteFromJudgeRequested(const QString &userName); - -protected: -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - void enterEvent(QEnterEvent *e) override; -#else - void enterEvent(QEvent *e) override; -#endif - void leaveEvent(QEvent *e) override; - -private slots: - void refreshGames(); - void onGamesReceived(const Response &r, const QString &forUser); - void onGamesContextMenu(const QPoint &pos); - -private: - void buildUi(); - void rebuildActionButtons(const ServerInfo_User &userInfo, bool online, bool isBuddy, bool isIgnored); - - TabSupervisor *m_ts; - AbstractClient *m_client; - const QMap *m_avatarCache; - const QMap *m_cardArtCache; - const QMap *m_cardArtParamsMap; - - QString m_currentUser; - ServerInfo_User m_currentUserInfo; - bool m_currentOnline = false; - - UserInfoHeaderWidget *m_header; - QWidget *m_actionArea; ///< rebuilt per user - QListView *m_gamesView; - QStandardItemModel *m_gamesModel; - QLabel *m_gamesStatus; - QPushButton *m_closeBtn; - QPushButton *m_refreshBtn; -}; - -#endif // COCKATRICE_USER_INFO_POPUP_H \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_list_manager.cpp b/cockatrice/src/interface/widgets/server/user/user_list_manager.cpp index 216420006..4bc2c84d6 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_manager.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_manager.cpp @@ -42,9 +42,6 @@ void UserListManager::handleDisconnect() delete ownUserInfo; ownUserInfo = nullptr; - - // Full rebuild — all lists are gone - emit listReset(); } void UserListManager::setOwnUserInfo(const ServerInfo_User &userInfo) @@ -66,77 +63,74 @@ void UserListManager::processListUsersResponse(const Response &response) const int userListSize = resp.user_list_size(); for (int i = 0; i < userListSize; ++i) { const ServerInfo_User &info = resp.user_list(i); - onlineUsers.insert(QString::fromStdString(info.name()), info); + const QString &userName = QString::fromStdString(info.name()); + onlineUsers.insert(userName, info); } - - // Bulk load complete — widgets rebuild once from the now-populated map - emit listReset(); } void UserListManager::processUserJoinedEvent(const Event_UserJoined &event) { const auto &info = event.user_info(); - const QString name = QString::fromStdString(info.name()); - onlineUsers.insert(name, info); - - emit userJoinedOnline(info); + const QString &userName = QString::fromStdString(info.name()); + onlineUsers.insert(userName, info); } void UserListManager::processUserLeftEvent(const Event_UserLeft &event) { - const QString name = QString::fromStdString(event.name()); - onlineUsers.remove(name); - - emit userLeftOnline(name); + const auto &userName = QString::fromStdString(event.name()); + onlineUsers.remove(userName); } void UserListManager::buddyListReceived(const QList &_buddyList) { for (const auto &user : _buddyList) { - buddyUsers.insert(QString::fromStdString(user.name()), user); + const auto &userName = QString::fromStdString(user.name()); + buddyUsers.insert(userName, user); } - - // Bulk load — one reset covers all newly added entries - emit listReset(); } void UserListManager::ignoreListReceived(const QList &_ignoreList) { for (const auto &user : _ignoreList) { - ignoredUsers.insert(QString::fromStdString(user.name()), user); + const auto &userName = QString::fromStdString(user.name()); + ignoredUsers.insert(userName, user); } - - // Bulk load — one reset covers all newly added entries - emit listReset(); } void UserListManager::processAddToListEvent(const Event_AddToList &event) { const auto &user = event.user_info(); - const QString userName = QString::fromStdString(user.name()); - const QString listType = QString::fromStdString(event.list_name()); + const auto &userName = QString::fromStdString(user.name()); - if (listType == "buddy") { - buddyUsers.insert(userName, user); - emit addedToBuddyList(user); - } else if (listType == "ignore") { - ignoredUsers.insert(userName, user); - emit addedToIgnoreList(user); + const auto &userListType = QString::fromStdString(event.list_name()); + + QMap *userMap; + if (userListType == "buddy") { + userMap = &buddyUsers; + } else if (userListType == "ignore") { + userMap = &ignoredUsers; + } else { + return; } + + userMap->insert(userName, user); } void UserListManager::processRemoveFromListEvent(const Event_RemoveFromList &event) { - const QString listType = QString::fromStdString(event.list_name()); - const QString userName = QString::fromStdString(event.user_name()); + const auto &userListType = QString::fromStdString(event.list_name()); + const auto &userName = QString::fromStdString(event.user_name()); - if (listType == "buddy") { - buddyUsers.remove(userName); - emit removedFromBuddyList(userName); - } else if (listType == "ignore") { - ignoredUsers.remove(userName); - emit removedFromIgnoreList(userName); + QMap *userMap; + if (userListType == "buddy") { + userMap = &buddyUsers; + } else if (userListType == "ignore") { + userMap = &ignoredUsers; + } else { + return; } + + userMap->remove(userName); } bool UserListManager::isOwnUserRegistered() const @@ -161,9 +155,16 @@ bool UserListManager::isUserIgnored(const QString &userName) const const ServerInfo_User *UserListManager::getOnlineUser(const QString &userName) const { - const QString lower = userName.toLower(); - const auto it = std::find_if(onlineUsers.begin(), onlineUsers.end(), [&lower](const ServerInfo_User &user) { - return lower == QString::fromStdString(user.name()).toLower(); - }); - return it != onlineUsers.end() ? &*it : nullptr; + const QString &userNameToMatchLower = userName.toLower(); + + const auto it = + std::find_if(onlineUsers.begin(), onlineUsers.end(), [&userNameToMatchLower](const ServerInfo_User &user) { + return userNameToMatchLower == QString::fromStdString(user.name()).toLower(); + }); + + if (it != onlineUsers.end()) { + return &*it; + } + + return nullptr; } \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_list_manager.h b/cockatrice/src/interface/widgets/server/user/user_list_manager.h index 6238f0799..f09284bd0 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_manager.h +++ b/cockatrice/src/interface/widgets/server/user/user_list_manager.h @@ -47,17 +47,15 @@ public: explicit UserListManager(AbstractClient *_client, QObject *parent = nullptr); ~UserListManager() override; - [[nodiscard]] const QMap &getAllUsersList() const + [[nodiscard]] QMap getAllUsersList() const { return onlineUsers; } - - [[nodiscard]] const QMap &getBuddyList() const + [[nodiscard]] QMap getBuddyList() const { return buddyUsers; } - - [[nodiscard]] const QMap &getIgnoreList() const + [[nodiscard]] QMap getIgnoreList() const { return ignoredUsers; } @@ -73,26 +71,8 @@ public slots: void handleDisconnect(); signals: - /** - * The entire list needs to be rebuilt from scratch. - * Fired on disconnect, reconnect, and initial bulk loads - * (Command_ListUsers response, initial buddy/ignore lists). - */ - void listReset(); - - // ── Online user presence ────────────────────────────────────────────────── - /** A user came online (or joined the room). Full ServerInfo_User available. */ - void userJoinedOnline(const ServerInfo_User &user); - /** A user went offline (or left the room). */ - void userLeftOnline(const QString &userName); - - // ── Buddy list mutations (individual, post-login) ───────────────────────── - void addedToBuddyList(const ServerInfo_User &user); - void removedFromBuddyList(const QString &userName); - - // ── Ignore list mutations (individual, post-login) ──────────────────────── - void addedToIgnoreList(const ServerInfo_User &user); - void removedFromIgnoreList(const QString &userName); + void userLeft(const QString &userName); + void userJoined(const ServerInfo_User &userInfo); }; #endif // COCKATRICE_USER_LIST_MANAGER_H diff --git a/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp b/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp deleted file mode 100644 index 8891ff268..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_list_painter.cpp +++ /dev/null @@ -1,342 +0,0 @@ -#include "user_list_painter.h" - -#include "../../interface/pixel_map_generator.h" - -#include -#include -#include -#include -#include -#include - -static constexpr int RowHeight = 72; -static constexpr int AvatarSize = 36; -static constexpr int LeftPadding = 14; -static constexpr int TextSpacing = 10; - -QSize UserListPainter::sizeHint() -{ - return QSize(0, RowHeight); -} - -QColor UserListPainter::getAccentColor(const UserLevelFlags &userLevel, bool online) -{ - QColor accentColor; - - if (userLevel.testFlag(ServerInfo_User::IsAdmin)) { - accentColor = QColor(245, 158, 11); - } else if (userLevel.testFlag(ServerInfo_User::IsModerator)) { - accentColor = QColor(59, 130, 246); - } else if (userLevel.testFlag(ServerInfo_User::IsJudge)) { - accentColor = QColor(168, 85, 247); - } else { - accentColor = QColor(100, 116, 139); - } - - if (!online) { - accentColor = accentColor.darker(160); - } - - return accentColor; -} - -int UserListPainter::getCardRight(const QStyleOptionViewItem &option, const QRect &rect) -{ - int scrollBarWidth = 0; - - if (const auto *scrollArea = qobject_cast(option.widget)) { - const QScrollBar *sb = scrollArea->verticalScrollBar(); - if (sb && sb->isVisible()) { - scrollBarWidth = sb->width(); - } - } - - const int viewportRight = option.widget ? option.widget->width() - scrollBarWidth : rect.right(); - - return qMin(rect.right(), viewportRight - 4); -} - -void UserListPainter::drawBackground(QPainter *painter, - const QRectF &cardRect, - const QColor &accentColor, - bool selected) -{ - QLinearGradient bg(cardRect.topLeft(), cardRect.topRight()); - bg.setColorAt(0, selected ? accentColor.darker(130) : accentColor.darker(320)); - bg.setColorAt(1, selected ? QColor(40, 48, 60) : QColor(18, 22, 30)); - - painter->setPen(Qt::NoPen); - painter->setBrush(bg); - painter->drawRoundedRect(cardRect, 6, 6); - - painter->setBrush(accentColor); - painter->drawRoundedRect(QRectF(cardRect.left(), cardRect.top(), 3, cardRect.height()), 2, 2); -} - -static QString makeKey(const QString &user, const QString &card, const QString &providerId) -{ - return user + u'|' + card + u'|' + providerId; -} - -void UserListPainter::drawCardArt(QPainter *painter, - const QRect &rect, - int cardRight, - const QString &userName, - const QMap *cardArtCache, - const CardArtParams ¶ms, - const QPixmap *overridePixmap = nullptr) -{ - QPixmap art; - - if (overridePixmap && !overridePixmap->isNull()) { - art = *overridePixmap; - } else { - if (!cardArtCache) { - return; - } - - const QString key = makeKey(userName, params.cardName, params.cardProviderId); - - if (!cardArtCache->contains(key)) { - return; - } - - art = cardArtCache->value(key); - } - - if (art.isNull()) { - return; - } - - const int cardH = rect.height() - 4; - const int totalW = cardRight - rect.left(); - const int marginL = qRound(totalW * params.marginPctL); - const int marginR = qRound(totalW * params.marginPctR); - const int drawW = totalW - marginL - marginR; - - const double basescale = qMax(double(drawW) / art.width(), double(cardH) / art.height()); - const double scale = basescale * params.zoom; - - const int scaledW = qRound(art.width() * scale); - const int scaledH = qRound(art.height() * scale); - - const QPixmap scaled = art.scaled(scaledW, scaledH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - - const int srcX = (scaledW - drawW) / 2; - const int srcY = qRound((scaledH - cardH) * params.verticalOffset); - - // Clamp srcY so we never copy outside the pixmap bounds - const int safeSrcY = qBound(0, srcY, qMax(0, scaledH - cardH)); - - QImage img = - scaled.copy(srcX, safeSrcY, drawW, cardH).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); - - { - QPainter mask(&img); - mask.setCompositionMode(QPainter::CompositionMode_DestinationIn); - - QLinearGradient grad(0, 0, img.width(), 0); - grad.setColorAt(0.00, Qt::transparent); - grad.setColorAt(0.22, Qt::white); - grad.setColorAt(0.78, Qt::white); - grad.setColorAt(1.00, Qt::transparent); - - mask.fillRect(img.rect(), grad); - } - - painter->setOpacity(0.55); - painter->drawImage(rect.left() + marginL, rect.top() + 2, img); - painter->setOpacity(1.0); -} - -QRect UserListPainter::getAvatarRect(const QRect &rect) -{ - const int avatarX = rect.left() + LeftPadding; - const int avatarY = rect.top() + (rect.height() - AvatarSize) / 2; - return QRect(avatarX, avatarY, AvatarSize, AvatarSize); -} - -void UserListPainter::drawAvatar(QPainter *painter, - const QRect &avatarRect, - const QString &userName, - const QColor &accentColor, - const UserLevelFlags &userLevel, - const ServerInfo_User &userInfo, - const QString &privLevel, - const QMap *avatarCache) -{ - QPainterPath clipPath; - clipPath.addEllipse(avatarRect); - - painter->save(); - painter->setClipPath(clipPath); - - bool drewAvatar = false; - - if (avatarCache && avatarCache->contains(userName)) { - const QPixmap &avatar = avatarCache->value(userName); - if (!avatar.isNull()) { - painter->drawPixmap( - avatarRect, avatar.scaled(avatarRect.size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); - drewAvatar = true; - } - } - - if (!drewAvatar) { - painter->setBrush(accentColor.darker(200)); - painter->setPen(Qt::NoPen); - painter->drawEllipse(avatarRect); - - const QPixmap pawn = - UserLevelPixmapGenerator::generatePixmap(24, userLevel, userInfo.pawn_colors(), false, privLevel); - - painter->drawPixmap(avatarRect.center().x() - 12, avatarRect.center().y() - 12, pawn); - } - - painter->restore(); -} - -void UserListPainter::drawStatusRing(QPainter *painter, const QRect &avatarRect, bool online) -{ - const QColor statusColor = online ? QColor(34, 197, 94) : QColor(70, 80, 95); - - painter->setPen(QPen(statusColor, 2)); - painter->setBrush(Qt::NoBrush); - painter->drawEllipse(avatarRect.adjusted(-1, -1, 1, 1)); -} - -void UserListPainter::drawUserName(QPainter *painter, - const QStyleOptionViewItem &option, - const QRect &rect, - int cardRight, - int textX, - const QString &userName, - bool online, - bool selected) -{ - QFont nameFont = option.font; - nameFont.setBold(true); - painter->setFont(nameFont); - - const QRect nameRect(textX, rect.top() + 8, cardRight - textX - 10, 20); - const QString elidedName = QFontMetrics(nameFont).elidedText(userName, Qt::ElideRight, cardRight - textX - 10); - - painter->setPen(QColor(0, 0, 0, 200)); - painter->drawText(nameRect.translated(1, 1), Qt::AlignVCenter | Qt::AlignLeft, elidedName); - - painter->setPen(online ? (selected ? Qt::white : QColor(226, 232, 240)) : QColor(90, 100, 115)); - painter->drawText(nameRect, Qt::AlignVCenter | Qt::AlignLeft, elidedName); -} - -void UserListPainter::drawCountryFlag(QPainter *painter, const QRect &rect, int textX, const ServerInfo_User &userInfo) -{ - const QPixmap flag = CountryPixmapGenerator::generatePixmap(13, QString::fromStdString(userInfo.country())); - if (!flag.isNull()) { - painter->drawPixmap(textX, rect.top() + 46, flag); - } -} - -QList UserListPainter::buildBadges(const UserLevelFlags &userLevel, const QString &privLevel) -{ - QList badges; - - if (userLevel.testFlag(ServerInfo_User::IsAdmin)) { - badges << Badge{"ADMIN", QColor(245, 158, 11)}; - } else if (userLevel.testFlag(ServerInfo_User::IsModerator)) { - badges << Badge{"MOD", QColor(59, 130, 246)}; - } else if (userLevel.testFlag(ServerInfo_User::IsJudge)) { - badges << Badge{"JUDGE", QColor(168, 85, 247)}; - } - - if (privLevel == "VIP") { - badges << Badge{"VIP", QColor(20, 184, 166)}; - } else if (privLevel == "DONATOR") { - badges << Badge{"DONATOR", QColor(249, 115, 22)}; - } - - return badges; -} - -void UserListPainter::drawBadges(QPainter *painter, - const QStyleOptionViewItem &option, - const QRect &rect, - int cardRight, - const QList &badges, - bool online) -{ - if (badges.isEmpty()) { - return; - } - - QFont badgeFont = option.font; - badgeFont.setPointSizeF(badgeFont.pointSizeF() * 0.68); - badgeFont.setBold(true); - painter->setFont(badgeFont); - - QFontMetrics fm(badgeFont); - - int totalBadgeW = 0; - for (const Badge &b : badges) { - totalBadgeW += fm.horizontalAdvance(b.text) + 8 + 4; - } - totalBadgeW -= 4; - - int bx = cardRight - 6 - totalBadgeW; - - for (const Badge &b : badges) { - const QColor col = online ? b.color : b.color.darker(180); - const int bw = fm.horizontalAdvance(b.text) + 8; - const QRect br(bx, rect.top() + 44, bw, 13); - - painter->setPen(Qt::NoPen); - painter->setBrush(col.darker(online ? 160 : 220)); - painter->drawRoundedRect(br, 3, 3); - - painter->setPen(col.lighter(online ? 160 : 100)); - painter->drawText(br, Qt::AlignCenter, b.text); - - bx += bw + 4; - } -} - -void UserListPainter::paint(QPainter *painter, - const QStyleOptionViewItem &option, - const QModelIndex &index, - const ServerInfo_User &userInfo, - const QMap *avatarCache, - const QMap *cardArtCache, - const QMap *cardArtParamsMap) -{ - painter->save(); - painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); - - const QRect rect = option.rect; - const bool online = index.data(Qt::UserRole + 1).toBool(); - const bool selected = option.state & QStyle::State_Selected; - const UserLevelFlags userLevel(userInfo.user_level()); - const QString userName = QString::fromStdString(userInfo.name()); - const QString privLevel = QString::fromStdString(userInfo.privlevel()); - const QColor accentColor = getAccentColor(userLevel, online); - const QRectF cardRect = QRectF(rect).adjusted(3, 2, -3, -2); - const int cardRight = getCardRight(option, rect); - - const CardArtParams params = (cardArtParamsMap && cardArtParamsMap->contains(userName)) - ? cardArtParamsMap->value(userName) - : CardArtParams{}; - - drawBackground(painter, cardRect, accentColor, selected); - drawCardArt(painter, rect, cardRight, userName, cardArtCache, params); - - const QRect avatarRect = getAvatarRect(rect); - drawAvatar(painter, avatarRect, userName, accentColor, userLevel, userInfo, privLevel, avatarCache); - drawStatusRing(painter, avatarRect, online); - - const int textX = avatarRect.right() + TextSpacing; - drawUserName(painter, option, rect, cardRight, textX, userName, online, selected); - drawCountryFlag(painter, rect, textX, userInfo); - - const QList badges = buildBadges(userLevel, privLevel); - drawBadges(painter, option, rect, cardRight, badges, online); - - painter->restore(); -} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/server/user/user_list_painter.h b/cockatrice/src/interface/widgets/server/user/user_list_painter.h deleted file mode 100644 index 28cab9675..000000000 --- a/cockatrice/src/interface/widgets/server/user/user_list_painter.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef COCKATRICE_USER_LIST_PAINTER_H -#define COCKATRICE_USER_LIST_PAINTER_H - -#include "user_level.h" - -#include -#include -#include -#include -#include -#include - -class QPainter; -class QModelIndex; -class QStyleOptionViewItem; -class ServerInfo_User; - -struct CardArtParams -{ - QString cardName = ""; - QString cardProviderId = ""; - double marginPctL = 0.33; - double marginPctR = 0.02; - double verticalOffset = 0.35; - double zoom = 1.0; -}; - -class UserListPainter -{ -public: - static void paint(QPainter *painter, - const QStyleOptionViewItem &option, - const QModelIndex &index, - const ServerInfo_User &userInfo, - const QMap *avatarCache, - const QMap *cardArtCache, - const QMap *cardArtParamsMap); - - static QSize sizeHint(); - - static void drawCardArt(QPainter *painter, - const QRect &rect, - int cardRight, - const QString &userName, - const QMap *cardArtCache, - const CardArtParams ¶ms, - const QPixmap *overridePixmap); - -private: - struct Badge - { - QString text; - QColor color; - }; - - static QColor getAccentColor(const UserLevelFlags &userLevel, bool online); - static int getCardRight(const QStyleOptionViewItem &option, const QRect &rect); - static void drawBackground(QPainter *painter, const QRectF &cardRect, const QColor &accentColor, bool selected); - static QRect getAvatarRect(const QRect &rect); - static void drawAvatar(QPainter *painter, - const QRect &avatarRect, - const QString &userName, - const QColor &accentColor, - const UserLevelFlags &userLevel, - const ServerInfo_User &userInfo, - const QString &privLevel, - const QMap *avatarCache); - static void drawStatusRing(QPainter *painter, const QRect &avatarRect, bool online); - static void drawUserName(QPainter *painter, - const QStyleOptionViewItem &option, - const QRect &rect, - int cardRight, - int textX, - const QString &userName, - bool online, - bool selected); - static void drawCountryFlag(QPainter *painter, const QRect &rect, int textX, const ServerInfo_User &userInfo); - static QList buildBadges(const UserLevelFlags &userLevel, const QString &privLevel); - static void drawBadges(QPainter *painter, - const QStyleOptionViewItem &option, - const QRect &rect, - int cardRight, - const QList &badges, - bool online); -}; - -#endif // COCKATRICE_USER_LIST_PAINTER_H \ No newline at end of file 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 c094f8a6b..11c9b60eb 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.cpp @@ -1,13 +1,10 @@ #include "user_list_widget.h" -#include "../../../../client/settings/cache_settings.h" -#include "../../../card_picture_loader/card_picture_loader.h" #include "../../interface/pixel_map_generator.h" #include "../../interface/widgets/tabs/tab_account.h" #include "../../interface/widgets/tabs/tab_supervisor.h" #include "../game_selector.h" #include "user_context_menu.h" -#include "user_list_painter.h" #include #include @@ -18,18 +15,13 @@ #include #include #include -#include -#include #include #include #include #include #include -#include #include #include -#include -#include #include BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent) : QDialog(parent) @@ -316,18 +308,7 @@ QString AdminNotesDialog::getNotes() const return notes->toPlainText(); } -namespace UserListRoles -{ -constexpr int Online = Qt::UserRole + 1; -constexpr int UserInfo = Qt::UserRole + 2; -} // namespace UserListRoles - -UserListItemDelegate::UserListItemDelegate(QObject *const parent, - const QMap *avatarCache, - const QMap *cardArtCache, - const QMap *cardArtParamsMap) - : QStyledItemDelegate(parent), avatarCache(avatarCache), cardArtCache(cardArtCache), - cardArtParamsMap(cardArtParamsMap) +UserListItemDelegate::UserListItemDelegate(QObject *const parent) : QStyledItemDelegate(parent) { } @@ -350,32 +331,6 @@ bool UserListItemDelegate::editorEvent(QEvent *event, return QStyledItemDelegate::editorEvent(event, model, option, index); } -QSize UserListItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (!SettingsCache::instance().getStyleUserList()) { - return QStyledItemDelegate::sizeHint(option, index); - } - return UserListPainter::sizeHint(); -} - -void UserListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (!SettingsCache::instance().getStyleUserList()) { - QStyledItemDelegate::paint(painter, option, index); - return; - } - - const QVariant var = index.data(UserListRoles::UserInfo); - - if (!var.isValid()) { - QStyledItemDelegate::paint(painter, option, index); - return; - } - - UserListPainter::paint(painter, option, index, var.value(), avatarCache, cardArtCache, - cardArtParamsMap); -} - UserListTWI::UserListTWI(const ServerInfo_User &_userInfo) : QTreeWidgetItem(Type) { setUserInfo(_userInfo); @@ -392,12 +347,11 @@ void UserListTWI::setUserInfo(const ServerInfo_User &_userInfo) setData(2, Qt::UserRole, QString::fromStdString(userInfo.name())); setData(2, Qt::DisplayRole, QString::fromStdString(userInfo.name())); setData(3, Qt::InitialSortOrderRole, QString::fromStdString(userInfo.privlevel())); - setData(0, UserListRoles::UserInfo, QVariant::fromValue(userInfo)); } void UserListTWI::setOnline(bool online) { - setData(0, UserListRoles::Online, online); + setData(0, Qt::UserRole + 1, online); setData(2, Qt::ForegroundRole, online ? qApp->palette().brush(QPalette::WindowText) : QBrush(Qt::gray)); } @@ -416,8 +370,8 @@ void UserListTWI::setOnline(bool online) bool UserListTWI::operator<(const QTreeWidgetItem &other) const { // Sort by online/offline - if (data(0, UserListRoles::Online) != other.data(0, UserListRoles::Online)) { - return data(0, UserListRoles::Online).toBool(); + if (data(0, Qt::UserRole + 1) != other.data(0, Qt::UserRole + 1)) { + return data(0, Qt::UserRole + 1).toBool(); } const auto &lhsUserLevelFlags = UserLevelFlags(data(0, Qt::UserRole).toInt()); @@ -464,100 +418,20 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor, QWidget *parent) : QGroupBox(parent), tabSupervisor(_tabSupervisor), client(_client), type(_type), onlineCount(0) { - avatarProvider = new UserAvatarProvider(client, this); - cardArtProvider = new UserCardArtProvider(this); - - itemDelegate = - new UserListItemDelegate(this, &avatarProvider->cache(), &cardArtProvider->cache(), &cardArtParamsMap); - + itemDelegate = new UserListItemDelegate(this); userContextMenu = new UserContextMenu(tabSupervisor, this); connect(userContextMenu, &UserContextMenu::openMessageDialog, this, &UserListWidget::openMessageDialog); userTree = new QTreeWidget; - userTree->setColumnCount(4); // 0=display, 1=flag(hidden), 2=name(hidden), 3=privlevel(hidden) - userTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + userTree->setColumnCount(3); + userTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents); userTree->header()->setMinimumSectionSize(0); userTree->setHeaderHidden(true); userTree->setRootIsDecorated(false); userTree->setIconSize(QSize(20, 18)); userTree->setItemDelegate(itemDelegate); userTree->setAlternatingRowColors(true); - userTree->hideColumn(1); - userTree->hideColumn(2); - userTree->hideColumn(3); connect(userTree, &QTreeWidget::itemActivated, this, &UserListWidget::userClicked); - userTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - userTree->header()->setStretchLastSection(true); - - // ── Hover popup ─────────────────────────────────────────────────────────── - m_userInfoPopup = new UserInfoPopup(tabSupervisor, tabSupervisor->getClient(), &avatarProvider->cache(), - &cardArtProvider->cache(), &cardArtParamsMap, - window()); // parented to main window so it floats above siblings - - m_userInfoPopup->hide(); - m_userInfoPopup->setWindowOpacity(0.0); - m_userInfoPopup->installEventFilter(this); - - connectPopupSignals(); - - m_showPopupTimer = new QTimer(this); - m_showPopupTimer->setSingleShot(true); - m_showPopupTimer->setInterval(280); - connect(m_showPopupTimer, &QTimer::timeout, this, [this] { - if (!m_hoveredUser.isEmpty()) { - showPopupForUser(m_hoveredUser); - } - }); - - m_hidePopupTimer = new QTimer(this); - m_hidePopupTimer->setSingleShot(true); - m_hidePopupTimer->setInterval(160); - connect(m_hidePopupTimer, &QTimer::timeout, this, [this] { - if (!m_popupPinned && !m_userInfoPopup->underMouse() && !userTree->underMouse()) { - hidePopup(); - } - }); - - userTree->setMouseTracking(true); - userTree->viewport()->setMouseTracking(true); - userTree->viewport()->installEventFilter(this); - - // Pin on item click - connect(userTree, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem *item, int) { - if (!SettingsCache::instance().getStyleUserList()) { - return; - } - const QString name = static_cast(item)->getUserInfo().name().c_str(); - m_popupPinned = false; // reset so showPopupForUser can update - showPopupForUser(name); - m_popupPinned = true; // pin after showing - }); - - connect(userTree->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this](const QItemSelection &sel, const QItemSelection &) { - // if (m_rebuildingTree) return; - if (sel.isEmpty() && m_popupPinned) { - m_popupPinned = false; - hidePopup(); - } - }); - - // Hide popup when list scrolls (reference row has moved) - connect(userTree->verticalScrollBar(), &QScrollBar::valueChanged, this, [this] { - m_showPopupTimer->stop(); - hidePopup(true); - }); - - // Forward join requests from popup upward - connect(m_userInfoPopup, &UserInfoPopup::joinGameRequested, this, &UserListWidget::joinGameRequested); - - connect(avatarProvider, &UserAvatarProvider::avatarUpdated, this, - [this](const QString &) { userTree->viewport()->update(); }); - connect(cardArtProvider, &UserCardArtProvider::cardArtUpdated, this, - [this](const QString &) { userTree->viewport()->update(); }); - - connect(&SettingsCache::instance(), &SettingsCache::styleUserListChanged, this, &UserListWidget::applyDisplayMode); - applyDisplayMode(); QVBoxLayout *vbox = new QVBoxLayout; vbox->addWidget(userTree); @@ -567,296 +441,6 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor, retranslateUi(); } -void UserListWidget::bind(UserListManager *mgr) -{ - manager = mgr; - - // ── Full rebuild: disconnect / reconnect / bulk initial load ────────────── - connect(manager, &UserListManager::listReset, this, &UserListWidget::rebuild); - - // ── Online users list (AllUsersList / RoomList) ─────────────────────────── - if (type == AllUsersList || type == RoomList) { - connect(manager, &UserListManager::userJoinedOnline, this, - [this](const ServerInfo_User &user) { processUserInfo(user, true); }); - connect(manager, &UserListManager::userLeftOnline, this, [this](const QString &name) { deleteUser(name); }); - } - - // ── Buddy list ──────────────────────────────────────────────────────────── - if (type == BuddyList) { - connect(manager, &UserListManager::addedToBuddyList, this, [this](const ServerInfo_User &user) { - const QString name = QString::fromStdString(user.name()); - processUserInfo(user, manager->getOnlineUser(name) != nullptr); - }); - connect(manager, &UserListManager::removedFromBuddyList, this, - [this](const QString &name) { deleteUser(name); }); - // Track online presence changes for buddies already in the tree - connect(manager, &UserListManager::userJoinedOnline, this, [this](const ServerInfo_User &user) { - const QString name = QString::fromStdString(user.name()); - if (users.contains(name)) { - users[name]->setUserInfo(user); - setUserOnline(name, true); - } - }); - connect(manager, &UserListManager::userLeftOnline, this, [this](const QString &name) { - if (users.contains(name)) { - setUserOnline(name, false); - } - }); - } - - // ── Ignore list ─────────────────────────────────────────────────────────── - if (type == IgnoreList) { - connect(manager, &UserListManager::addedToIgnoreList, this, [this](const ServerInfo_User &user) { - const QString name = QString::fromStdString(user.name()); - processUserInfo(user, manager->getOnlineUser(name) != nullptr); - }); - connect(manager, &UserListManager::removedFromIgnoreList, this, - [this](const QString &name) { deleteUser(name); }); - } - - // ── Popup button refresh ────────────────────────────────────────────────── - // Any buddy/ignore mutation while the popup is open refreshes its buttons - auto refreshIfPopupOpen = [this](const QString &name) { - if (m_userInfoPopup && m_userInfoPopup->isVisible() && m_userInfoPopup->currentUser() == name) { - refreshPopupButtons(name); - } - }; - auto refreshCurrentPopup = [refreshIfPopupOpen](const ServerInfo_User &u) { - refreshIfPopupOpen(QString::fromStdString(u.name())); - }; - - connect(manager, &UserListManager::addedToBuddyList, this, refreshCurrentPopup); - connect(manager, &UserListManager::removedFromBuddyList, this, refreshIfPopupOpen); - connect(manager, &UserListManager::addedToIgnoreList, this, refreshCurrentPopup); - connect(manager, &UserListManager::removedFromIgnoreList, this, refreshIfPopupOpen); - connect(manager, &UserListManager::userJoinedOnline, this, refreshCurrentPopup); - connect(manager, &UserListManager::userLeftOnline, this, refreshIfPopupOpen); - - rebuild(); -} - -void UserListWidget::refreshPopupButtons(const QString &userName) -{ - UserListTWI *item = users.value(userName); - if (!item) { - return; - } - - const UserListProxy *proxy = tabSupervisor->getUserListManager(); - const bool online = item->data(0, UserListRoles::Online).toBool(); - const bool isBuddy = proxy->isUserBuddy(userName); - const bool isIgn = proxy->isUserIgnored(userName); - - m_userInfoPopup->updateActionButtons(item->getUserInfo(), online, isBuddy, isIgn); - positionPopup(userName); // height may have changed — reposition -} - -void UserListWidget::hideEvent(QHideEvent *e) -{ - QGroupBox::hideEvent(e); - m_showPopupTimer->stop(); - m_hidePopupTimer->stop(); - hidePopup(true); -} - -void UserListWidget::applyDisplayMode() -{ - const bool styled = SettingsCache::instance().getStyleUserList(); - - if (styled) { - userTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - userTree->hideColumn(1); - userTree->hideColumn(2); - userTree->hideColumn(3); - } else { - userTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - userTree->showColumn(1); - userTree->showColumn(2); - userTree->hideColumn(3); - } - - userTree->viewport()->update(); -} - -void UserListWidget::connectPopupSignals() -{ - connect(m_userInfoPopup, &UserInfoPopup::closeRequested, this, [this] { - m_popupPinned = false; - hidePopup(true); - }); - connect(m_userInfoPopup, &UserInfoPopup::mouseEnteredPopup, m_hidePopupTimer, &QTimer::stop); - connect(m_userInfoPopup, &UserInfoPopup::mouseLeftPopup, this, [this] { - if (!m_popupPinned) { - m_hidePopupTimer->start(); - } - }); - - // Wire all action signals to UserContextMenu::exec*() - connect(m_userInfoPopup, &UserInfoPopup::chatRequested, userContextMenu, &UserContextMenu::execChat); - connect(m_userInfoPopup, &UserInfoPopup::detailsRequested, userContextMenu, &UserContextMenu::execDetails); - connect(m_userInfoPopup, &UserInfoPopup::showGamesRequested, userContextMenu, &UserContextMenu::execShowGames); - connect(m_userInfoPopup, &UserInfoPopup::addBuddyRequested, userContextMenu, &UserContextMenu::execAddToBuddy); - connect(m_userInfoPopup, &UserInfoPopup::removeBuddyRequested, userContextMenu, - &UserContextMenu::execRemoveFromBuddy); - connect(m_userInfoPopup, &UserInfoPopup::addIgnoreRequested, userContextMenu, &UserContextMenu::execAddToIgnore); - connect(m_userInfoPopup, &UserInfoPopup::removeIgnoreRequested, userContextMenu, - &UserContextMenu::execRemoveFromIgnore); - connect(m_userInfoPopup, &UserInfoPopup::banRequested, userContextMenu, &UserContextMenu::execBan); - connect(m_userInfoPopup, &UserInfoPopup::warnRequested, userContextMenu, &UserContextMenu::execWarn); - connect(m_userInfoPopup, &UserInfoPopup::banHistoryRequested, userContextMenu, &UserContextMenu::execBanHistory); - 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, false); }); - connect(m_userInfoPopup, &UserInfoPopup::demoteFromModRequested, this, - [this](const QString &n) { userContextMenu->execAdjustMod(n, false, false); }); - connect(m_userInfoPopup, &UserInfoPopup::promoteToJudgeRequested, this, - [this](const QString &n) { userContextMenu->execAdjustMod(n, false, true); }); - connect(m_userInfoPopup, &UserInfoPopup::demoteFromJudgeRequested, this, - [this](const QString &n) { userContextMenu->execAdjustMod(n, false, false); }); -} - -bool UserListWidget::eventFilter(QObject *obj, QEvent *event) -{ - if (obj == userTree->viewport()) { - if (event->type() == QEvent::MouseMove) { - if (!SettingsCache::instance().getStyleUserList()) { - return QGroupBox::eventFilter(obj, event); - } - auto *me = static_cast(event); - auto *twi = static_cast(userTree->itemAt(me->pos())); - const QString hovName = twi ? QString::fromStdString(twi->getUserInfo().name()) : QString{}; - - if (hovName != m_hoveredUser) { - m_hoveredUser = hovName; - if (!hovName.isEmpty()) { - m_hidePopupTimer->stop(); - if (!m_popupPinned) { - m_showPopupTimer->start(); - } - } else { - m_showPopupTimer->stop(); - if (!m_popupPinned) { - m_hidePopupTimer->start(); - } - } - } - } else if (event->type() == QEvent::Leave) { - m_hoveredUser.clear(); - m_showPopupTimer->stop(); - if (!m_popupPinned) { - m_hidePopupTimer->start(); - } - } - } - - return QGroupBox::eventFilter(obj, event); -} - -void UserListWidget::showPopupForUser(const QString &userName) -{ - UserListTWI *item = users.value(userName); - if (!item) { - return; - } - - const ServerInfo_User &info = item->getUserInfo(); - const bool online = item->data(0, UserListRoles::Online).toBool(); - const bool isBuddy = userContextMenu->getUserListProxy()->isUserBuddy(userName); - const bool isIgn = userContextMenu->getUserListProxy()->isUserIgnored(userName); - - m_userInfoPopup->showForUser(userName, info, online, isBuddy, isIgn); - - // Realize the native window at opacity 0 before positioning so that: - // 1) move() applies to an existing native handle (not overridden by Qt's - // default centering logic on first show) - // 2) adjustSize() inside positionPopup() can measure the final laid-out - // geometry correctly - m_userInfoPopup->setWindowOpacity(0.0); - m_userInfoPopup->show(); - m_userInfoPopup->raise(); - - positionPopup(userName); // geometry is now accurate; move() sticks - - auto *fade = new QPropertyAnimation(m_userInfoPopup, "windowOpacity", m_userInfoPopup); - fade->setDuration(120); - fade->setStartValue(0.0); - fade->setEndValue(1.0); - fade->start(QAbstractAnimation::DeleteWhenStopped); -} - -void UserListWidget::positionPopup(const QString &userName) -{ - UserListTWI *item = users.value(userName); - if (!item) { - return; - } - - QWidget *vp = userTree->viewport(); - const QRect itemR = userTree->visualItemRect(item); - const QPoint itemTL = vp->mapToGlobal(itemR.topLeft()); - const QPoint vpTL = vp->mapToGlobal(vp->rect().topLeft()); - const QPoint vpTR = vp->mapToGlobal(vp->rect().topRight()); - - m_userInfoPopup->adjustSize(); - const int popW = m_userInfoPopup->width(); - const int popH = m_userInfoPopup->height(); - const int margin = 12; - - const QRect screen = QGuiApplication::primaryScreen()->availableGeometry(); - - // ── X: prefer the side with more space ─────────────────────────────────── - const int spaceLeft = vpTL.x() - screen.left() - margin; - const int spaceRight = screen.right() - vpTR.x() - margin; - int x; - if (spaceLeft >= spaceRight) { - x = (spaceLeft >= popW) ? (vpTL.x() - margin - popW) : (vpTR.x() + margin); - } else { - x = (spaceRight >= popW) ? (vpTR.x() + margin) : (vpTL.x() - margin - popW); - } - x = qBound(screen.left() + margin, x, screen.right() - popW - margin); - - // ── Y: grow down if there's room, otherwise grow up ─────────────────────── - const int itemTopY = itemTL.y(); - const int spaceBelow = screen.bottom() - itemTopY - margin; - const int spaceAbove = itemTopY - screen.top() - margin; - - int y; - if (spaceBelow >= popH) { - y = itemTopY; // top edges align, popup grows downward - } else if (spaceAbove >= popH) { - y = itemTopY - popH; // bottom of popup meets top of item, grows upward - } else { - // Neither side fits cleanly — pick the roomier side and let clamp handle the rest - y = (spaceBelow >= spaceAbove) ? itemTopY : (itemTopY - popH); - } - y = qBound(screen.top() + margin, y, screen.bottom() - popH - margin); - - m_userInfoPopup->move(x, y); -} - -void UserListWidget::hidePopup(bool immediate) -{ - m_showPopupTimer->stop(); - m_hidePopupTimer->stop(); - if (!m_userInfoPopup->isVisible()) { - return; - } - - if (immediate) { - m_userInfoPopup->hide(); - return; - } - - // Fade out - auto *fade = new QPropertyAnimation(m_userInfoPopup, "windowOpacity", m_userInfoPopup); - fade->setDuration(100); - fade->setStartValue(m_userInfoPopup->windowOpacity()); - fade->setEndValue(0.0); - connect(fade, &QPropertyAnimation::finished, m_userInfoPopup, &QWidget::hide); - fade->start(QAbstractAnimation::DeleteWhenStopped); -} - void UserListWidget::retranslateUi() { userContextMenu->retranslateUi(); @@ -877,60 +461,9 @@ void UserListWidget::retranslateUi() updateCount(); } -void UserListWidget::rebuild() -{ - userTree->clear(); - users.clear(); - cardArtParamsMap.clear(); - onlineCount = 0; - - if (!manager) { - return; - } - - const QMap *source = nullptr; - - switch (type) { - case AllUsersList: - case RoomList: - source = &manager->getAllUsersList(); - break; - case BuddyList: - source = &manager->getBuddyList(); - break; - case IgnoreList: - source = &manager->getIgnoreList(); - break; - } - - for (auto it = source->cbegin(); it != source->cend(); ++it) { - processUserInfo(it.value(), manager->getOnlineUser(it.key()) != nullptr); - } - - sortItems(); -} - void UserListWidget::processUserInfo(const ServerInfo_User &user, bool online) { const QString userName = QString::fromStdString(user.name()); - - // Always update params from the latest ServerInfo_User, whether the - // item is new or existing, so a live server-push refreshes the rendering. - if (user.has_card_art_params()) { - const auto &cap = user.card_art_params(); - CardArtParams params; - params.cardName = QString::fromStdString(cap.card_name()); - params.cardProviderId = QString::fromStdString(cap.card_provider_id()); - params.marginPctL = cap.margin_pct_l(); - params.marginPctR = cap.margin_pct_r(); - params.verticalOffset = cap.vertical_offset(); - params.zoom = cap.zoom(); - cardArtParamsMap.insert(userName, params); - cardArtProvider->requestCardArt(userName, params.cardName, params.cardProviderId); - } else { - cardArtParamsMap.remove(userName); // clear stale params on removal - } - UserListTWI *item = users.value(userName); if (item) { item->setUserInfo(user); @@ -942,28 +475,25 @@ void UserListWidget::processUserInfo(const ServerInfo_User &user, bool online) ++onlineCount; } updateCount(); - avatarProvider->requestAvatar(userName); } item->setOnline(online); - sortItems(); - userTree->viewport()->update(); } bool UserListWidget::deleteUser(const QString &userName) { UserListTWI *twi = users.value(userName); - if (!twi) { - return false; + if (twi) { + users.remove(userName); + userTree->takeTopLevelItem(userTree->indexOfTopLevelItem(twi)); + if (twi->data(0, Qt::UserRole + 1).toBool()) { + --onlineCount; + } + delete twi; + updateCount(); + return true; } - users.remove(userName); - userTree->takeTopLevelItem(userTree->indexOfTopLevelItem(twi)); - if (twi->data(0, Qt::UserRole + 1).toBool()) { - --onlineCount; - } - delete twi; - updateCount(); - return true; + return false; } void UserListWidget::setUserOnline(const QString &userName, bool online) @@ -1007,5 +537,5 @@ void UserListWidget::showContextMenu(const QPoint &pos, const QModelIndex &index void UserListWidget::sortItems() { - userTree->sortItems(0, Qt::AscendingOrder); + userTree->sortItems(1, Qt::AscendingOrder); } diff --git a/cockatrice/src/interface/widgets/server/user/user_list_widget.h b/cockatrice/src/interface/widgets/server/user/user_list_widget.h index d70cdfbbd..5a8c00d10 100644 --- a/cockatrice/src/interface/widgets/server/user/user_list_widget.h +++ b/cockatrice/src/interface/widgets/server/user/user_list_widget.h @@ -7,17 +7,9 @@ #ifndef USERLIST_H #define USERLIST_H -#include "../../cards/card_info_picture_art_crop_widget.h" -#include "user_avatar_provider.h" -#include "user_card_art_provider.h" -#include "user_info_popup.h" -#include "user_list_manager.h" -#include "user_list_painter.h" - #include #include #include -#include #include #include #include @@ -102,21 +94,12 @@ public: class UserListItemDelegate : public QStyledItemDelegate { - const QMap *avatarCache; - const QMap *cardArtCache; - const QMap *cardArtParamsMap; - public: - explicit UserListItemDelegate(QObject *const parent, - const QMap *avatarCache, - const QMap *cardArtCache, - const QMap *cardArtParamsMap); + explicit UserListItemDelegate(QObject *const parent); bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; class UserListTWI : public QTreeWidgetItem @@ -148,22 +131,6 @@ public: }; private: - UserListManager *manager = nullptr; - UserAvatarProvider *avatarProvider = nullptr; - UserCardArtProvider *cardArtProvider = nullptr; - QMap cardArtParamsMap; - // ── Hover popup ─────────────────────────────────────────────────────────── - UserInfoPopup *m_userInfoPopup = nullptr; - QTimer *m_showPopupTimer = nullptr; - QTimer *m_hidePopupTimer = nullptr; - QString m_hoveredUser; - bool m_popupPinned = false; - - void showPopupForUser(const QString &userName); - void hidePopup(bool immediate = false); - void positionPopup(const QString &userName); - void connectPopupSignals(); - QMap users; TabSupervisor *tabSupervisor; AbstractClient *client; @@ -174,7 +141,6 @@ private: int onlineCount; QString titleStr; void updateCount(); - void refreshPopupButtons(const QString &userName); private slots: void userClicked(QTreeWidgetItem *item, int column); signals: @@ -183,18 +149,13 @@ signals: void removeBuddy(const QString &userName); void addIgnore(const QString &userName); void removeIgnore(const QString &userName); - void joinGameRequested(int gameId, int roomId, bool asSpectator); public: UserListWidget(TabSupervisor *_tabSupervisor, AbstractClient *_client, UserListType _type, QWidget *parent = nullptr); - void bind(UserListManager *mgr); - void applyDisplayMode(); - bool eventFilter(QObject *obj, QEvent *event) override; void retranslateUi(); - void rebuild(); void processUserInfo(const ServerInfo_User &user, bool online); bool deleteUser(const QString &userName); void setUserOnline(const QString &userName, bool online); @@ -204,9 +165,6 @@ public: } void showContextMenu(const QPoint &pos, const QModelIndex &index); void sortItems(); - -protected: - void hideEvent(QHideEvent *e) override; }; #endif diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp index e00484ebf..2d32f3ce1 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp @@ -111,15 +111,6 @@ AppearanceSettingsPage::AppearanceSettingsPage() homeTabGroupBox = new QGroupBox; homeTabGroupBox->setLayout(homeTabGrid); - styleUserListCheckBox.setChecked(settings.getStyleUserList()); - connect(&styleUserListCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setStyleUserList); - - auto stylingTabGrid = new QGridLayout; - stylingTabGrid->addWidget(&styleUserListCheckBox, 0, 0, 1, 2); - - stylingGroupBox = new QGroupBox; - stylingGroupBox->setLayout(stylingTabGrid); - // Menu settings showShortcutsCheckBox.setChecked(settings.getShowShortcuts()); connect(&showShortcutsCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &AppearanceSettingsPage::showShortcutsChanged); @@ -293,7 +284,6 @@ AppearanceSettingsPage::AppearanceSettingsPage() auto *mainLayout = new QVBoxLayout; mainLayout->addWidget(themeGroupBox); mainLayout->addWidget(homeTabGroupBox); - mainLayout->addWidget(stylingGroupBox); mainLayout->addWidget(menuGroupBox); mainLayout->addWidget(printingsGroupBox); mainLayout->addWidget(cardsGroupBox); @@ -408,9 +398,6 @@ void AppearanceSettingsPage::retranslateUi() homeTabBackgroundShuffleFrequencySpinBox.setSpecialValueText(tr("Disabled")); homeTabDisplayCardNameCheckBox.setText(tr("Display card name of background in bottom right")); - stylingGroupBox->setTitle(tr("Styling settings")); - styleUserListCheckBox.setText(tr("Style user list")); - menuGroupBox->setTitle(tr("Menu settings")); showShortcutsCheckBox.setText(tr("Show keyboard shortcuts in right-click menus")); showGameSelectorFilterToolbarCheckBox.setText(tr("Show game filter toolbar above list in room tab")); diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h index e223d70f8..9ed27be4d 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h @@ -37,7 +37,6 @@ private: QLabel homeTabBackgroundShuffleFrequencyLabel; QSpinBox homeTabBackgroundShuffleFrequencySpinBox; QCheckBox homeTabDisplayCardNameCheckBox; - QCheckBox styleUserListCheckBox; QLabel minPlayersForMultiColumnLayoutLabel; QLabel maxFontSizeForCardsLabel; QCheckBox showShortcutsCheckBox; @@ -59,7 +58,6 @@ private: QCheckBox invertVerticalCoordinateCheckBox; QGroupBox *themeGroupBox; QGroupBox *homeTabGroupBox; - QGroupBox *stylingGroupBox; QGroupBox *menuGroupBox; QGroupBox *printingsGroupBox; QGroupBox *cardsGroupBox; diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp index 44b30d29c..6039e3758 100644 --- a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.cpp @@ -68,10 +68,6 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() connect(&showTotalSelectionCountCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setShowTotalSelectionCount); - showSubtypeSelectionTallyCheckBox.setChecked(SettingsCache::instance().getShowSubtypeSelectionTally()); - connect(&showSubtypeSelectionTallyCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setShowSubtypeSelectionTally); - useTearOffMenusCheckBox.setChecked(SettingsCache::instance().getUseTearOffMenus()); connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); @@ -90,9 +86,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() generalGrid->addWidget(&annotateTokensCheckBox, 6, 0); generalGrid->addWidget(&showDragSelectionCountCheckBox, 7, 0); generalGrid->addWidget(&showTotalSelectionCountCheckBox, 8, 0); - generalGrid->addWidget(&showSubtypeSelectionTallyCheckBox, 9, 0); - generalGrid->addWidget(&useTearOffMenusCheckBox, 10, 0); - generalGrid->addWidget(&keepGameChatFocusCheckBox, 11, 0); + generalGrid->addWidget(&useTearOffMenusCheckBox, 9, 0); + generalGrid->addWidget(&keepGameChatFocusCheckBox, 10, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -214,9 +209,8 @@ void UserInterfaceSettingsPage::retranslateUi() 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")); - showDragSelectionCountCheckBox.setText(tr("Show selection count during drag selection")); - showTotalSelectionCountCheckBox.setText(tr("Show total selection count")); - showSubtypeSelectionTallyCheckBox.setText(tr("Show subtype breakdown in selection tally")); + showDragSelectionCountCheckBox.setText(tr("Show selection counter during drag selection")); + showTotalSelectionCountCheckBox.setText(tr("Show total selection counter")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); keepGameChatFocusCheckBox.setText( tr("Keep game chat focused when clicking in game (Note: disables card view search bar)")); diff --git a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h index 06f0e6b83..e10ed2a06 100644 --- a/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/user_interface_settings_page.h @@ -29,7 +29,6 @@ private: QCheckBox annotateTokensCheckBox; QCheckBox showDragSelectionCountCheckBox; QCheckBox showTotalSelectionCountCheckBox; - QCheckBox showSubtypeSelectionTallyCheckBox; QCheckBox useTearOffMenusCheckBox; QCheckBox keepGameChatFocusCheckBox; QCheckBox tapAnimationCheckBox; diff --git a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp b/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp deleted file mode 100644 index 3dc4de15f..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "tab_card_art_rules.h" - -#include "libcockatrice/card/database/card_database_manager.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -CardArtRulesModel::CardArtRulesModel(AbstractClient *client, QObject *parent) - : QAbstractTableModel(parent), client(client) -{ -} - -int CardArtRulesModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } - return static_cast(entries.size()); -} - -int CardArtRulesModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return 4; -} - -QVariant CardArtRulesModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - return {}; - } - - const auto &e = entries.at(index.row()); - - if (role == Qt::DisplayRole) { - switch (index.column()) { - case 0: - return e.cardName; - case 1: - return e.cardProviderId; - case 2: - return e.mode; - case 3: - return e.reason; - } - } - - return {}; -} - -QVariant CardArtRulesModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { - return {}; - } - - switch (section) { - case 0: - return tr("Card"); - case 1: - return tr("ProviderId"); - case 2: - return tr("Mode"); - case 3: - return tr("Reason"); - default: - return {}; - } -} - -void CardArtRulesModel::refresh() -{ - Command_ListCardArtRules cmd; - - PendingCommand *pend = client->prepareModeratorCommand(cmd); - - connect(pend, &PendingCommand::finished, this, &CardArtRulesModel::onRefreshFinished); - - client->sendCommand(pend); -} - -void CardArtRulesModel::clear() -{ - beginResetModel(); - entries.clear(); - endResetModel(); -} - -QString CardArtRulesModel::cardAt(int row) const -{ - if (row < 0 || row >= static_cast(entries.size())) { - return {}; - } - - return entries[row].cardName; -} - -const CardArtRulesModel::Entry *CardArtRulesModel::entryAt(int row) const -{ - if (row < 0 || row >= static_cast(entries.size())) { - return nullptr; - } - - return &entries[row]; -} - -void CardArtRulesModel::onRefreshFinished(const Response &r) -{ - if (r.response_code() != Response::RespOk) { - return; - } - - const auto &resp = r.GetExtension(Response_ListCardArtRules::ext); - - beginResetModel(); - entries.clear(); - - for (const auto &e : resp.entries()) { - entries.push_back({QString::fromStdString(e.card_name()), QString::fromStdString(e.card_provider_id()), - QString::fromStdString(e.mode()), QString::fromStdString(e.reason())}); - } - - endResetModel(); -} - -TabCardArtRules::TabCardArtRules(TabSupervisor *parent, AbstractClient *_client) : Tab(parent), client(_client) -{ - setupUi(); - refresh(); -} - -void TabCardArtRules::setupUi() -{ - auto *central = new QWidget(this); - - initSearchBar(); - - providerComboBox = new QComboBox; - modeBox = new QComboBox; - reasonEdit = new QLineEdit; - - addBtn = new QPushButton; - removeBtn = new QPushButton; - refreshBtn = new QPushButton; - - modeBox->addItems({"ALLOW", "DENY"}); - - tableModel = new CardArtRulesModel(client, this); - - table = new QTableView; - table->setModel(tableModel); - table->setSelectionBehavior(QAbstractItemView::SelectRows); - table->setSelectionMode(QAbstractItemView::SingleSelection); - - auto *form = new QFormLayout; - form->addRow(tr("Card:"), searchEdit); - form->addRow(tr("ProviderId:"), providerComboBox); - form->addRow(tr("Mode:"), modeBox); - form->addRow(tr("Reason:"), reasonEdit); - - auto *buttons = new QHBoxLayout; - buttons->addWidget(addBtn); - buttons->addWidget(removeBtn); - buttons->addWidget(refreshBtn); - - auto *layout = new QVBoxLayout; - layout->addLayout(form); - layout->addLayout(buttons); - layout->addWidget(table); - - central->setLayout(layout); - setCentralWidget(central); - - connect(addBtn, &QPushButton::clicked, this, &TabCardArtRules::addRule); - - connect(removeBtn, &QPushButton::clicked, this, &TabCardArtRules::removeSelected); - - connect(refreshBtn, &QPushButton::clicked, this, &TabCardArtRules::refresh); - - retranslateUi(); -} - -void TabCardArtRules::initSearchBar() -{ - searchEdit = new QLineEdit; - searchEdit->setPlaceholderText(tr("Type a card name...")); - - cardDbModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this); - cardDbDisplayModel = new CardDatabaseDisplayModel(this); - cardDbDisplayModel->setSourceModel(cardDbModel); - cardSearchModel = new CardSearchModel(cardDbDisplayModel, this); - - cardProxyModel = new CardCompleterProxyModel(this); - cardProxyModel->setSourceModel(cardSearchModel); - cardProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - - searchCompleter = new QCompleter(cardProxyModel, this); - searchCompleter->setCompletionRole(Qt::DisplayRole); - searchCompleter->setCompletionMode(QCompleter::PopupCompletion); - searchCompleter->setCaseSensitivity(Qt::CaseInsensitive); - searchCompleter->setFilterMode(Qt::MatchContains); - searchCompleter->setMaxVisibleItems(15); - searchEdit->setCompleter(searchCompleter); - - connect(searchEdit, &QLineEdit::textEdited, cardSearchModel, &CardSearchModel::updateSearchResults); - connect(searchEdit, &QLineEdit::textEdited, this, [this](const QString &text) { - const QString pattern = ".*" + QRegularExpression::escape(text) + ".*"; - cardProxyModel->setFilterRegularExpression( - QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption)); - if (!text.isEmpty()) { - searchCompleter->complete(); - } - }); - connect(searchCompleter, static_cast(&QCompleter::activated), this, - [this](const QString &name) { searchEdit->setText(name); }); - connect(searchEdit, &QLineEdit::editingFinished, this, - [this]() { populateProviderCombo(searchEdit->text().trimmed()); }); -} - -void TabCardArtRules::populateProviderCombo(const QString &cardName) -{ - providerComboBox->clear(); - - auto card = CardDatabaseManager::query()->getCard({cardName}); - - const auto &sets = card.getInfo().getSets(); - - for (const auto &printings : sets) { - for (const auto &p : printings) { - - QString setName = p.getSet()->getLongName(); - QString collector = p.getProperty("num"); - QString uuid = p.getUuid(); - - QString label = setName; - - if (!collector.isEmpty()) { - label += " #" + collector; - } - - providerComboBox->addItem(label, uuid); - } - } -} - -void TabCardArtRules::retranslateUi() -{ - addBtn->setText(tr("Add rule")); - removeBtn->setText(tr("Remove rule")); - refreshBtn->setText(tr("Refresh")); -} - -void TabCardArtRules::refresh() -{ - tableModel->refresh(); -} - -void TabCardArtRules::addRule() -{ - Command_AddCardArtRule cmd; - cmd.set_card_name(searchEdit->text().toStdString()); - cmd.set_card_provider_id(providerComboBox->currentData().toString().toStdString()); - cmd.set_mode(modeBox->currentText().toStdString()); - cmd.set_reason(reasonEdit->text().toStdString()); - - client->sendCommand(client->prepareModeratorCommand(cmd)); - - refresh(); -} - -void TabCardArtRules::removeSelected() -{ - QModelIndex idx = table->currentIndex(); - if (!idx.isValid()) { - return; - } - - Command_RemoveCardArtRule cmd; - const auto e = tableModel->entryAt(idx.row()); - - cmd.set_card_name(e->cardName.toStdString()); - cmd.set_card_provider_id(e->cardProviderId.toStdString()); - - client->sendCommand(client->prepareModeratorCommand(cmd)); - - refresh(); -} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h b/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h deleted file mode 100644 index b9ea2ca83..000000000 --- a/cockatrice/src/interface/widgets/tabs/tab_card_art_rules.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef COCKATRICE_DLG_CARD_ART_RULES_H -#define COCKATRICE_DLG_CARD_ART_RULES_H - -#include "card/card_search_model.h" -#include "tab_supervisor.h" - -#include -#include -#include -#include -#include - -class AbstractClient; - -class CardArtRulesModel : public QAbstractTableModel -{ - Q_OBJECT - -public: - struct Entry - { - QString cardName; - QString cardProviderId; - QString mode; - QString reason; - }; - - explicit CardArtRulesModel(AbstractClient *client, QObject *parent = nullptr); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - void refresh(); - void clear(); - - QString cardAt(int row) const; - const Entry *entryAt(int row) const; - -private slots: - void onRefreshFinished(const Response &r); - -private: - AbstractClient *client; - std::vector entries; -}; - -class TabCardArtRules : public Tab -{ - Q_OBJECT - -public: - TabCardArtRules(TabSupervisor *parent, AbstractClient *client); - - QString getTabText() const override - { - return tr("Card Art Rules"); - } - void retranslateUi() override; - -private: - void setupUi(); - -private slots: - void addRule(); - void removeSelected(); - void refresh(); - -private: - AbstractClient *client; - - QLineEdit *searchEdit; - void initSearchBar(); - void populateProviderCombo(const QString &cardName); - QCompleter *searchCompleter; - CardDatabaseModel *cardDbModel; - CardDatabaseDisplayModel *cardDbDisplayModel; - CardSearchModel *cardSearchModel; - CardCompleterProxyModel *cardProxyModel; - QComboBox *providerComboBox; - QComboBox *modeBox; - QLineEdit *reasonEdit; - - QPushButton *addBtn; - QPushButton *removeBtn; - QPushButton *refreshBtn; - - QTableView *table; - CardArtRulesModel *tableModel; -}; - -#endif // COCKATRICE_DLG_CARD_ART_RULES_H diff --git a/cockatrice/src/interface/widgets/tabs/tab_room.cpp b/cockatrice/src/interface/widgets/tabs/tab_room.cpp index c7495da5a..424742e9b 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_room.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_room.cpp @@ -49,25 +49,10 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, QMap tempMap; tempMap.insert(info.room_id(), gameTypes); gameSelector = new GameSelector(client, tabSupervisor, this, QMap(), tempMap, true, true); - - auto *tabs = new QTabWidget(this); - - friendsList = new UserListWidget(tabSupervisor, client, UserListWidget::BuddyList); - friendsList->bind(tabSupervisor->getUserListManager()); userList = new UserListWidget(tabSupervisor, client, UserListWidget::RoomList); - userList->bind(tabSupervisor->getUserListManager()); - ignoreList = new UserListWidget(tabSupervisor, client, UserListWidget::IgnoreList); - ignoreList->bind(tabSupervisor->getUserListManager()); - - connect(friendsList, SIGNAL(openMessageDialog(const QString &, bool)), this, - SIGNAL(openMessageDialog(const QString &, bool))); connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); - tabs->addTab(friendsList, tr("Friends")); - tabs->addTab(userList, tr("Online")); - tabs->addTab(ignoreList, tr("Ignored")); - chatView = new ChatView(tabSupervisor, nullptr, true, this); connect(chatView, &ChatView::showMentionPopup, this, &TabRoom::actShowMentionPopup); connect(chatView, &ChatView::messageClickedSignal, this, &TabRoom::focusTab); @@ -116,7 +101,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, auto *hbox = new QHBoxLayout; hbox->addWidget(splitter, 3); - hbox->addWidget(tabs, 1); + hbox->addWidget(userList, 1); aLeaveRoom = new QAction(this); connect(aLeaveRoom, &QAction::triggered, this, &TabRoom::closeRequest); @@ -127,8 +112,10 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, const int userListSize = info.user_list_size(); for (int i = 0; i < userListSize; ++i) { + userList->processUserInfo(info.user_list(i), true); autocompleteUserList.append("@" + QString::fromStdString(info.user_list(i).name())); } + userList->sortItems(); const int gameListSize = info.game_list_size(); for (int i = 0; i < gameListSize; ++i) { @@ -282,6 +269,8 @@ void TabRoom::processListGamesEvent(const Event_ListGames &event) void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event) { + userList->processUserInfo(event.user_info(), true); + userList->sortItems(); if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))) { autocompleteUserList << "@" + QString::fromStdString(event.user_info().name()); sayEdit->setCompletionList(autocompleteUserList); @@ -290,6 +279,7 @@ void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event) void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event) { + userList->deleteUser(QString::fromStdString(event.name())); autocompleteUserList.removeOne("@" + QString::fromStdString(event.name())); sayEdit->setCompletionList(autocompleteUserList); } diff --git a/cockatrice/src/interface/widgets/tabs/tab_room.h b/cockatrice/src/interface/widgets/tabs/tab_room.h index d669b6107..eeb5a9e14 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_room.h +++ b/cockatrice/src/interface/widgets/tabs/tab_room.h @@ -56,9 +56,7 @@ private: QMap gameTypes; GameSelector *gameSelector; - UserListWidget *friendsList; UserListWidget *userList; - UserListWidget *ignoreList; const UserListProxy *userListProxy; ChatView *chatView; QLabel *sayLabel; diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp index 52309d94b..e7075f78f 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp @@ -9,7 +9,6 @@ #include "api/edhrec/tab_edhrec_main.h" #include "tab_account.h" #include "tab_admin.h" -#include "tab_card_art_rules.h" #include "tab_deck_editor.h" #include "tab_deck_storage.h" #include "tab_game.h" @@ -180,10 +179,6 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget * aTabAdmin->setCheckable(true); connect(aTabAdmin, &QAction::triggered, this, &TabSupervisor::actTabAdmin); - aTabCardArtRules = new QAction(this); - aTabCardArtRules->setCheckable(true); - connect(aTabCardArtRules, &QAction::triggered, this, &TabSupervisor::actTabCardArtRules); - aTabLog = new QAction(this); aTabLog->setCheckable(true); connect(aTabLog, &QAction::triggered, this, &TabSupervisor::actTabLog); @@ -440,7 +435,6 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo) tabsMenu->addSeparator(); tabsMenu->addAction(aTabAdmin); tabsMenu->addAction(aTabLog); - tabsMenu->addAction(aTabCardArtRules); if (SettingsCache::instance().getTabAdminOpen()) { openTabAdmin(); @@ -448,7 +442,6 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo) if (SettingsCache::instance().getTabLogOpen()) { openTabLog(); } - openTabCardArtRules(); } retranslateUi(); @@ -688,30 +681,6 @@ void TabSupervisor::openTabAdmin() aTabAdmin->setChecked(true); } -void TabSupervisor::actTabCardArtRules(bool checked) -{ - if (checked && !tabCardArtRules) { - openTabCardArtRules(); - setCurrentWidget(tabCardArtRules); - } else if (!checked && tabCardArtRules) { - tabCardArtRules->closeRequest(); - } -} - -void TabSupervisor::openTabCardArtRules() -{ - tabCardArtRules = new TabCardArtRules(this, client); - - myAddTab(tabCardArtRules, aTabCardArtRules); - - connect(tabCardArtRules, &QObject::destroyed, this, [this] { - tabCardArtRules = nullptr; - aTabCardArtRules->setChecked(false); - }); - - aTabCardArtRules->setChecked(true); -} - void TabSupervisor::actTabLog(bool checked) { SettingsCache::instance().setTabLogOpen(checked); diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h index 3eac144b7..e77fb4f7b 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h @@ -24,7 +24,6 @@ #include #include -class TabCardArtRules; inline Q_LOGGING_CATEGORY(TabSupervisorLog, "tab_supervisor"); class UserListManager; @@ -104,7 +103,6 @@ private: TabDeckStorage *tabDeckStorage; TabReplays *tabReplays; TabAdmin *tabAdmin; - TabCardArtRules *tabCardArtRules; TabLog *tabLog; QMap roomTabs; QMap gameTabs; @@ -114,8 +112,7 @@ private: bool isLocalGame; QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage, - *aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin, - *aTabCardArtRules, *aTabLog; + *aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin, *aTabLog; int myAddTab(Tab *tab, QAction *manager = nullptr); void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager); @@ -148,7 +145,7 @@ public: return userInfo; } [[nodiscard]] AbstractClient *getClient() const; - [[nodiscard]] UserListManager *getUserListManager() const + [[nodiscard]] const UserListManager *getUserListManager() const { return userListManager; } @@ -200,8 +197,6 @@ private slots: void openTabDeckStorage(); void openTabReplays(); void openTabAdmin(); - void actTabCardArtRules(bool checked); - void openTabCardArtRules(); void openTabLog(); void updateCurrent(int index); diff --git a/doc/carddatabase_v4/cards.xsd b/doc/carddatabase_v4/cards.xsd index 59ca3e560..92d30b94f 100644 --- a/doc/carddatabase_v4/cards.xsd +++ b/doc/carddatabase_v4/cards.xsd @@ -6,7 +6,6 @@ - diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.cpp b/libcockatrice_card/libcockatrice/card/database/card_database.cpp index 261b36690..edad46174 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database.cpp @@ -85,8 +85,7 @@ void CardDatabase::refreshCachedReverseRelatedCards() for (auto *rel : card->getReverseRelatedCards()) { if (auto target = cards.value(rel->getName())) { auto *newRel = new CardRelation(card->getName(), rel->getAttachType(), rel->getIsCreateAllExclusion(), - rel->getIsVariable(), rel->getDefaultCount(), rel->getIsPersistent(), - rel->getIsFaceDown()); + rel->getIsVariable(), rel->getDefaultCount(), rel->getIsPersistent()); target->addReverseRelatedCards2Me(newRel); } } diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp index c242425ab..96a5ac104 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp +++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp @@ -329,7 +329,6 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) bool exclude = false; bool variable = false; bool persistent = false; - bool facedown = false; int count = 1; QXmlStreamAttributes attrs = xml.attributes(); QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements); @@ -361,12 +360,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) persistent = true; } - if (attrs.hasAttribute("facedown")) { - facedown = true; - } - - auto *relation = - new CardRelation(cardName, attachType, exclude, variable, count, persistent, facedown); + auto *relation = new CardRelation(cardName, attachType, exclude, variable, count, persistent); if (xmlName == "reverse-related") { reverseRelatedCards << relation; } else { @@ -516,9 +510,6 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in if (i->getIsPersistent()) { xml.writeAttribute("persistent", "persistent"); } - if (i->getIsFaceDown()) { - xml.writeAttribute("facedown", "facedown"); - } if (i->getIsVariable()) { if (1 == i->getDefaultCount()) { xml.writeAttribute("count", "x"); diff --git a/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp b/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp index 8903c892d..90e59e439 100644 --- a/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp +++ b/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp @@ -7,10 +7,8 @@ CardRelation::CardRelation(const QString &_name, bool _isCreateAllExclusion, bool _isVariableCount, int _defaultCount, - bool _isPersistent, - bool _isFaceDown) + bool _isPersistent) : name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion), - isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent), - isFaceDown(_isFaceDown) + isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent) { -} +} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/relation/card_relation.h b/libcockatrice_card/libcockatrice/card/relation/card_relation.h index a1864f5b2..9ff704097 100644 --- a/libcockatrice_card/libcockatrice/card/relation/card_relation.h +++ b/libcockatrice_card/libcockatrice/card/relation/card_relation.h @@ -31,7 +31,6 @@ private: bool isVariableCount; ///< True if the number of creations is variable. int defaultCount; ///< Default number of cards created or involved. bool isPersistent; ///< True if this relation persists (i.e. is not destroyed) on zone change. - bool isFaceDown; ///< True if this relation creates the tokens facedown public: /** @@ -43,15 +42,13 @@ public: * @param _isVariableCount Whether the count is variable. * @param _defaultCount Default number for creations or transformations. * @param _isPersistent Whether the relation persists across zone changes. - * @param _isFaceDown Whether the relation creates the token face down */ explicit CardRelation(const QString &_name = QString(), CardRelationType _attachType = CardRelationType::DoesNotAttach, bool _isCreateAllExclusion = false, bool _isVariableCount = false, int _defaultCount = 1, - bool _isPersistent = false, - bool _isFaceDown = false); + bool _isPersistent = false); /** * @brief Returns the name of the related card. @@ -154,16 +151,6 @@ public: { return isPersistent; } - - /** - * @brief Returns whether the relation creates the token facedown. - * - * @return True if facedown, false otherwise. - */ - [[nodiscard]] bool getIsFaceDown() const - { - return isFaceDown; - } }; #endif // COCKATRICE_CARD_RELATION_H diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server.cpp b/libcockatrice_network/libcockatrice/network/server/remote/server.cpp index 7abccfca8..3da9ddc73 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/server.cpp @@ -190,25 +190,6 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, return authState; } -void Server::broadcastUserInfoUpdate(Server_ProtocolHandler *source) -{ - Event_UserJoined event; - event.mutable_user_info()->CopyFrom(source->copyUserInfo(false)); - - SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - - clientsLock.lockForRead(); - for (auto &client : clients) { - if (client->getAcceptsUserListChanges()) { - client->sendProtocolItem(*se); - } - } - clientsLock.unlock(); - - sendIsl_SessionEvent(*se); - delete se; -} - void Server::addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId) { QWriteLocker locker(&persistentPlayersLock); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server.h b/libcockatrice_network/libcockatrice/network/server/remote/server.h index 2fca46593..ab57fac4e 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server.h @@ -64,7 +64,6 @@ public: QString &clientid, QString &clientVersion, QString &connectionType); - void broadcastUserInfoUpdate(Server_ProtocolHandler *source); const QMap &getRooms() { diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt index 20a4cb08d..b4c7b6ac8 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt @@ -122,7 +122,6 @@ set(PROTO_FILES response_activate.proto response_adjust_mod.proto response_ban_history.proto - response_card_art_rule_entry.proto response_deck_download.proto response_deck_list.proto response_deck_upload.proto diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto index ca46e4dd7..9d01b51d2 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto @@ -11,9 +11,6 @@ message ModeratorCommand { FORCE_ACTIVATE_USER = 1007; GET_ADMIN_NOTES = 1008; UPDATE_ADMIN_NOTES = 1009; - ADD_CARD_ART_RULE = 1010; - REMOVE_CARD_ART_RULE = 1011; - LIST_CARD_ART_RULES = 1012; } extensions 100 to max; } @@ -109,29 +106,3 @@ message Command_UpdateAdminNotes { optional string user_name = 1; optional string notes = 2; } - -message Command_AddCardArtRule { - extend ModeratorCommand { - optional Command_AddCardArtRule ext = 1010; - } - - optional string card_name = 1; - optional string card_provider_id = 2; - optional string mode = 3; // "ALLOW" or "DENY" - optional string reason = 4; -} - -message Command_RemoveCardArtRule { - extend ModeratorCommand { - optional Command_RemoveCardArtRule ext = 1011; - } - - optional string card_name = 1; - optional string card_provider_id = 2; -} - -message Command_ListCardArtRules { - extend ModeratorCommand { - optional Command_ListCardArtRules ext = 1012; - } -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto index e719f3e92..dece8ae17 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/response.proto @@ -68,7 +68,6 @@ message Response { REPLAY_LIST = 1100; REPLAY_DOWNLOAD = 1101; REPLAY_GET_CODE = 1102; - CARD_ART_RULE_LIST = 1200; } required uint64 cmd_id = 1; optional ResponseCode response_code = 2; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto deleted file mode 100644 index 25b76e09f..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_card_art_rule_entry.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_CardArtRuleEntry { - optional string card_name = 1; - optional string card_provider_id = 2; - optional string mode = 3; - optional string reason = 4; -} - -message Response_ListCardArtRules { - extend Response { - optional Response_ListCardArtRules ext = 1200; - } - repeated Response_CardArtRuleEntry entries = 1; -} \ No newline at end of file diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto index 98cc3ce6a..10add611f 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_user.proto @@ -1,5 +1,4 @@ syntax = "proto2"; - message ServerInfo_User { enum UserLevelFlag { IsNothing = 0; @@ -13,14 +12,6 @@ message ServerInfo_User { optional string left_side = 1; optional string right_side = 2; }; - message CardArtParams { - optional string card_name = 1; - optional string card_provider_id = 2; - optional double margin_pct_l = 3 [default = 0.33]; - optional double margin_pct_r = 4 [default = 0.02]; - optional double vertical_offset = 5 [default = 0.35]; - optional double zoom = 6 [default = 1.0]; - }; optional string name = 1; optional uint32 user_level = 2; @@ -37,5 +28,4 @@ message ServerInfo_User { optional string clientid = 13; optional string privlevel = 14; optional PawnColorsOverride pawn_colors = 15; - optional CardArtParams card_art_params = 16; -} \ No newline at end of file +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto index 9d207c711..cecf87370 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/session_commands.proto @@ -27,7 +27,6 @@ message SessionCommand { FORGOT_PASSWORD_RESET = 1022; FORGOT_PASSWORD_CHALLENGE = 1023; REQUEST_PASSWORD_SALT = 1024; - SET_CARD_ART_PARAMS = 1025; REPLAY_LIST = 1100; REPLAY_DOWNLOAD = 1101; REPLAY_MODIFY_MATCH = 1102; @@ -206,15 +205,3 @@ message Command_RequestPasswordSalt { } required string user_name = 1; } - -message Command_SetCardArtParams { - extend SessionCommand { - optional Command_SetCardArtParams ext = 1025; - } - optional string card_name = 1; - optional string card_provider_id = 2; - optional double margin_pct_l = 3; - optional double margin_pct_r = 4; - optional double vertical_offset = 5; - optional double zoom = 6; -} diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index df29d6c9f..c0c7d8cc9 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -17,7 +17,6 @@ set(UTILITY_HEADERS libcockatrice/utility/passwordhasher.h libcockatrice/utility/trice_limits.h libcockatrice/utility/zone_names.h - libcockatrice/utility/days_years_between.h ) add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS}) diff --git a/libcockatrice_utility/libcockatrice/utility/days_years_between.h b/libcockatrice_utility/libcockatrice/utility/days_years_between.h index 4b3b5bc0c..c0f5da23a 100644 --- a/libcockatrice_utility/libcockatrice/utility/days_years_between.h +++ b/libcockatrice_utility/libcockatrice/utility/days_years_between.h @@ -1,6 +1,3 @@ -#ifndef COCKATRICE_DAYS_YEARS_BETWEEN_H -#define COCKATRICE_DAYS_YEARS_BETWEEN_H - #include inline static QPair getDaysAndYearsBetween(const QDate &then, const QDate &now) @@ -9,5 +6,3 @@ inline static QPair getDaysAndYearsBetween(const QDate &then, const QD int days = then.addYears(years).daysTo(now); return {days, years}; } - -#endif // COCKATRICE_DAYS_YEARS_BETWEEN_H diff --git a/libcockatrice_utility/libcockatrice/utility/qt_utils.h b/libcockatrice_utility/libcockatrice/utility/qt_utils.h index 8e5212031..334e56027 100644 --- a/libcockatrice_utility/libcockatrice/utility/qt_utils.h +++ b/libcockatrice_utility/libcockatrice/utility/qt_utils.h @@ -1,6 +1,5 @@ #ifndef COCKATRICE_QT_UTILS_H #define COCKATRICE_QT_UTILS_H -#include #include namespace QtUtils diff --git a/servatrice/migrations/servatrice_0034_to_0035.sql b/servatrice/migrations/servatrice_0034_to_0035.sql deleted file mode 100644 index acaad9c8b..000000000 --- a/servatrice/migrations/servatrice_0034_to_0035.sql +++ /dev/null @@ -1,19 +0,0 @@ -ALTER TABLE `cockatrice_users` ADD COLUMN `card_art_params` TEXT DEFAULT NULL, ALGORITHM=INSTANT; - -CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `card_name` varchar(255) NOT NULL, - `card_provider_id` varchar(255) NOT NULL, - `mode` enum('ALLOW','DENY') NOT NULL, - `reason` varchar(255) DEFAULT NULL, - `created_by` int(7) unsigned DEFAULT NULL, - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uniq_provider_card_name` (`card_provider_id`, `card_name`), - KEY `idx_mode` (`mode`), - FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`) - ON DELETE SET NULL - ON UPDATE CASCADE - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci; - -UPDATE cockatrice_schema_version SET version=35 WHERE version=34; diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 7f530063c..fa644dbc0 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` ( PRIMARY KEY (`version`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; -INSERT INTO cockatrice_schema_version VALUES(35); +INSERT INTO cockatrice_schema_version VALUES(34); -- users and user data tables CREATE TABLE IF NOT EXISTS `cockatrice_users` ( @@ -43,7 +43,6 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` ( `passwordLastChangedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `leftPawnColorOverride` varchar(255), `rightPawnColorOverride` varchar(255), - `card_art_params` TEXT DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `token` (`token`), @@ -301,19 +300,3 @@ CREATE TABLE IF NOT EXISTS `cockatrice_audit` ( PRIMARY KEY (`id`), KEY `user_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_card_art_name_rules` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `card_name` varchar(255) NOT NULL, - `card_provider_id` varchar(255) NOT NULL, - `mode` enum('ALLOW','DENY') NOT NULL, - `reason` varchar(255) DEFAULT NULL, - `created_by` int(7) unsigned DEFAULT NULL, - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uniq_provider_card_name` (`card_provider_id`, `card_name`), - KEY `idx_mode` (`mode`), - FOREIGN KEY (`created_by`) REFERENCES `cockatrice_users`(`id`) - ON DELETE SET NULL - ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index d5e1f13ef..73643825e 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -7,8 +7,6 @@ #include #include #include -#include -#include #include #include #include @@ -683,33 +681,6 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer if (!clientid.isEmpty()) { result.set_clientid(clientid.toStdString()); } - - const QString cardArtParamsJson = query->value(12).toString(); - if (!cardArtParamsJson.isEmpty()) { - const QJsonDocument doc = QJsonDocument::fromJson(cardArtParamsJson.toUtf8()); - if (doc.isObject()) { - const QJsonObject obj = doc.object(); - auto *cap = result.mutable_card_art_params(); - if (obj.contains("card_name")) { - cap->set_card_name(obj["card_name"].toString().toStdString()); - } - if (obj.contains("card_provider_id")) { - cap->set_card_provider_id(obj["card_provider_id"].toString().toStdString()); - } - if (obj.contains("marginPctL")) { - cap->set_margin_pct_l(obj["marginPctL"].toDouble(0.33)); - } - if (obj.contains("marginPctR")) { - cap->set_margin_pct_r(obj["marginPctR"].toDouble(0.02)); - } - if (obj.contains("verticalOffset")) { - cap->set_vertical_offset(obj["verticalOffset"].toDouble(0.35)); - } - if (obj.contains("zoom")) { - cap->set_zoom(obj["zoom"].toDouble(1.0)); - } - } - } } return result; } @@ -727,7 +698,7 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b QSqlQuery *query = prepareQuery("select id, name, admin, country, privlevel, leftPawnColorOverride, " "rightPawnColorOverride, realname, avatar_bmp, registrationDate, " - "email, clientid, card_art_params from {prefix}_users where " + "email, clientid from {prefix}_users where " "name = :name and active = 1"); query->bindValue(":name", name); if (!execSqlQuery(query)) { diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 1e3501ec7..68080404c 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -10,7 +10,7 @@ #include #include -#define DATABASE_SCHEMA_VERSION 35 +#define DATABASE_SCHEMA_VERSION 34 class Servatrice; diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 55f779468..bc90a3ef1 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #include #include @@ -61,10 +59,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -216,8 +212,6 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionComm return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc); case SessionCommand::ACCOUNT_IMAGE: return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc); - case SessionCommand::SET_CARD_ART_PARAMS: - return cmdSetCardArtParams(cmd.GetExtension(Command_SetCardArtParams::ext), rc); case SessionCommand::ACCOUNT_PASSWORD: return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc); case SessionCommand::REQUEST_PASSWORD_SALT: @@ -253,12 +247,6 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo return cmdGetAdminNotes(cmd.GetExtension(Command_GetAdminNotes::ext), rc); case ModeratorCommand::UPDATE_ADMIN_NOTES: return cmdUpdateAdminNotes(cmd.GetExtension(Command_UpdateAdminNotes::ext), rc); - case ModeratorCommand::ADD_CARD_ART_RULE: - return cmdAddCardArtRule(cmd.GetExtension((Command_AddCardArtRule::ext)), rc); - case ModeratorCommand::REMOVE_CARD_ART_RULE: - return cmdRemoveCardArtRule(cmd.GetExtension((Command_RemoveCardArtRule::ext)), rc); - case ModeratorCommand::LIST_CARD_ART_RULES: - return cmdListCardArtRules(cmd.GetExtension((Command_ListCardArtRules::ext)), rc); default: return Response::RespFunctionNotAllowed; } @@ -1577,173 +1565,6 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Comm return Response::RespOk; } -bool AbstractServerSocketInterface::isCardNameAllowed(const QString &cardName, const QString &cardProviderId) -{ - QSqlQuery *q = sqlInterface->prepareQuery( - "SELECT mode FROM {prefix}_card_art_name_rules WHERE card_name = :name AND card_provider_id = :provider"); - - q->bindValue(":name", cardName); - q->bindValue(":provider", cardProviderId); - - if (!sqlInterface->execSqlQuery(q)) { - qWarning() << "Card art rule lookup failed; failing open for" << cardName; - return true; - } - - if (!q->next()) { - return true; // default allow - } - - return q->value(0).toString() != "DENY"; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const Command_SetCardArtParams &cmd, - ResponseContainer & /* rc */) -{ - if (authState != PasswordRight) { - return Response::RespFunctionNotAllowed; - } - - const QString cardName = QString::fromStdString(cmd.card_name()); - const QString cardProviderId = QString::fromStdString(cmd.card_provider_id()); - - if (cardName.length() > MAX_NAME_LENGTH || cardProviderId.length() > MAX_NAME_LENGTH) { - return Response::RespInvalidData; - } - - if (cardName.isEmpty()) { - // Removal path - QSqlQuery *q = sqlInterface->prepareQuery("UPDATE {prefix}_users SET card_art_params = NULL WHERE id = :id"); - q->bindValue(":id", userInfo->id()); - if (!sqlInterface->execSqlQuery(q)) { - return Response::RespInternalError; - } - userInfo->clear_card_art_params(); - server->broadcastUserInfoUpdate(this); - return Response::RespOk; - } - - if (!isCardNameAllowed(cardName, cardProviderId)) { - return Response::RespFunctionNotAllowed; - } - - // Clamp everything to sane ranges server-side so a malicious client - // can't store garbage that breaks other clients' rendering. - const double marginPctL = qBound(0.0, cmd.margin_pct_l(), 0.95); - const double marginPctR = qBound(0.0, cmd.margin_pct_r(), 0.95); - const double verticalOffset = qBound(0.0, cmd.vertical_offset(), 1.0); - const double zoom = qBound(0.1, cmd.zoom(), 4.0); - - QJsonObject obj; - obj["card_name"] = cardName; - obj["card_provider_id"] = cardProviderId; - obj["marginPctL"] = marginPctL; - obj["marginPctR"] = marginPctR; - obj["verticalOffset"] = verticalOffset; - obj["zoom"] = zoom; - const QString json = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); - - QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set card_art_params=:params where id=:id"); - query->bindValue(":params", json); - query->bindValue(":id", userInfo->id()); - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - // Keep the in-memory userInfo in sync - auto *cap = userInfo->mutable_card_art_params(); - cap->set_card_name(cmd.card_name()); - cap->set_card_provider_id(cmd.card_provider_id()); - cap->set_margin_pct_l(marginPctL); - cap->set_margin_pct_r(marginPctR); - cap->set_vertical_offset(verticalOffset); - cap->set_zoom(zoom); - - const QString name = QString::fromStdString(userInfo->name()); - server->broadcastUserInfoUpdate(this); - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdAddCardArtRule(const Command_AddCardArtRule &cmd, - ResponseContainer &) -{ - const QString cardName = QString::fromStdString(cmd.card_name()); - const QString cardProviderId = QString::fromStdString(cmd.card_provider_id()); - const QString mode = QString::fromStdString(cmd.mode()); - - if (mode != "ALLOW" && mode != "DENY") { - return Response::RespInvalidData; - } - if (cardName.isEmpty() || cardName.length() > MAX_NAME_LENGTH || cardProviderId.length() > MAX_NAME_LENGTH) { - return Response::RespInvalidData; - } - - QSqlQuery *q = sqlInterface->prepareQuery("INSERT INTO {prefix}_card_art_name_rules " - "(card_name, card_provider_id, mode, reason, created_by) " - "VALUES (:name, :provider, :mode, :reason, :uid) " - "ON DUPLICATE KEY UPDATE mode=:mode2, reason=:reason2"); - - q->bindValue(":name", cardName); - q->bindValue(":provider", cardProviderId); - q->bindValue(":mode", mode); - q->bindValue(":mode2", mode); - q->bindValue(":reason", QString::fromStdString(cmd.reason())); - q->bindValue(":reason2", QString::fromStdString(cmd.reason())); - q->bindValue(":uid", userInfo->id()); - - if (!sqlInterface->execSqlQuery(q)) { - return Response::RespInternalError; - } - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, - ResponseContainer &) -{ - auto cardName = QString::fromStdString(cmd.card_name()); - auto cardProviderId = QString::fromStdString(cmd.card_provider_id()); - if (cardName.length() > MAX_NAME_LENGTH || cardProviderId.length() > MAX_NAME_LENGTH) { - return Response::RespInvalidData; - } - QSqlQuery *q = sqlInterface->prepareQuery( - "DELETE FROM {prefix}_card_art_name_rules WHERE card_name=:name AND card_provider_id=:provider"); - - q->bindValue(":name", cardName); - q->bindValue(":provider", cardProviderId); - - if (!sqlInterface->execSqlQuery(q)) { - return Response::RespInternalError; - } - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const Command_ListCardArtRules &, - ResponseContainer &rc) -{ - QSqlQuery *q = sqlInterface->prepareQuery( - "SELECT card_name, card_provider_id, mode, reason FROM {prefix}_card_art_name_rules"); - - if (!sqlInterface->execSqlQuery(q)) { - return Response::RespInternalError; - } - - auto *re = new Response_ListCardArtRules; - - while (q->next()) { - auto *entry = re->add_entries(); - entry->set_card_name(q->value(0).toString().toStdString()); - entry->set_card_provider_id(q->value(1).toString().toStdString()); - entry->set_mode(q->value(2).toString().toStdString()); - entry->set_reason(q->value(3).toString().toStdString()); - } - - rc.setResponseExtension(re); - return Response::RespOk; -} - Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer & /* rc */) { diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index 0d66ae78f..e10aa0dde 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -129,11 +129,6 @@ private: Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); - bool isCardNameAllowed(const QString &cardName, const QString &cardProviderId); - Response::ResponseCode cmdSetCardArtParams(const Command_SetCardArtParams &cmd, ResponseContainer &); - Response::ResponseCode cmdAddCardArtRule(const Command_AddCardArtRule &cmd, ResponseContainer &); - Response::ResponseCode cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, ResponseContainer &); - Response::ResponseCode cmdListCardArtRules(const Command_ListCardArtRules &, ResponseContainer &rc); Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); Response::ResponseCode cmdGrantReplayAccess(const Command_GrantReplayAccess &cmd, ResponseContainer &rc); Response::ResponseCode cmdForceActivateUser(const Command_ForceActivateUser &cmd, ResponseContainer &rc);