From 797681883ba6dc484b13f348c9cc9249a25414a8 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 11 May 2025 23:38:13 +0200 Subject: [PATCH 01/20] CI: Remove Ubuntu 20.04 (EOL May 31st) (#5849) * ubuntu 20.04 eol * Update release_template.md * [skip ci] Delete .ci/Ubuntu20.04 directory --- .ci/Ubuntu20.04/Dockerfile | 26 -------------------------- .ci/release_template.md | 1 - .github/workflows/desktop-build.yml | 5 ----- 3 files changed, 32 deletions(-) delete mode 100644 .ci/Ubuntu20.04/Dockerfile diff --git a/.ci/Ubuntu20.04/Dockerfile b/.ci/Ubuntu20.04/Dockerfile deleted file mode 100644 index 45481d44b..000000000 --- a/.ci/Ubuntu20.04/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM ubuntu:20.04 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt5multimedia5-plugins \ - libqt5sql5-mysql \ - libqt5svg5-dev \ - libqt5websockets5-dev \ - protobuf-compiler \ - qt5-default \ - qt5-image-formats-plugins \ - qtmultimedia5-dev \ - qttools5-dev \ - qttools5-dev-tools \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/release_template.md b/.ci/release_template.md index a7a2d31cb..d2e6716f8 100644 --- a/.ci/release_template.md +++ b/.ci/release_template.md @@ -20,7 +20,6 @@ Available pre-compiled binaries for installation: LinuxUbuntu 24.04 LTS Noble NumbatUbuntu 22.04 LTS Jammy Jellyfish - • Ubuntu 20.04 LTS Focal FossaDebian 12 BookwormDebian 11 BullseyeFedora 42 diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 7fb403549..587aeffb2 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -110,11 +110,6 @@ jobs: version: 42 package: RPM - - distro: Ubuntu - version: 20.04 - package: DEB - test: skip # Ubuntu 20.04 has a broken Qt for debug builds - - distro: Ubuntu version: 22.04 package: DEB From 3b0c7a3a30852f24d3c4e0b06f5b68061e83aed9 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 11 May 2025 23:39:45 +0200 Subject: [PATCH 02/20] Re-add vcpkg binary caching (#5933) --- .github/workflows/desktop-build.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 587aeffb2..7d7652141 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -382,16 +382,11 @@ jobs: tools: ${{matrix.qt_tools}} modules: ${{matrix.qt_modules}} - # TODO: re-enable when https://github.com/lukka/run-vcpkg/issues/243 is fixed - - if: false - name: Run vcpkg (disabled) - uses: lukka/run-vcpkg@v11 + - name: Setup vcpkg cache + id: vcpkg-cache + uses: TAServers/vcpkg-cache@v3 with: - runVcpkgInstall: true - doNotCache: false - env: - VCPKG_DEFAULT_TRIPLET: 'x64-windows' - VCPKG_DISABLE_METRICS: 1 + token: ${{ secrets.GITHUB_TOKEN }} - name: Build Cockatrice id: build @@ -401,6 +396,8 @@ jobs: CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' CMAKE_GENERATOR_PLATFORM: 'x64' QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}' + VCPKG_DISABLE_METRICS: 1 + VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite' # No need for --parallel flag, MTT is added in the compile script to let cmake/msbuild manage core count, # project and process parallelism: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/ run: .ci/compile.sh --server --release --test --package From b2749a0c4ef638e074996019c486d54ce576dbe2 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 11 May 2025 23:40:04 +0200 Subject: [PATCH 03/20] Add debug log (#5937) --- .github/ISSUE_TEMPLATE/bug_report.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4744d611a..7d7490eee 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,14 +9,24 @@ assignees: '' --- +sure to post your OS and the file name of the setup binary instead. +--> **System Information:** + +
Debug Log: + + + +
+ _______________________________________________________________________________________ @@ -26,7 +36,7 @@ ________________________________________________________________________________ _______________________________________________________________________________________ - + **Steps to reproduce:** - Do A From 17c767fa425f73e9ef7f77a851a0e94d801a679a Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Mon, 12 May 2025 23:36:55 +0200 Subject: [PATCH 04/20] [GDE] Add a group criteria to the deck list model (#5931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a group criteria to the deck list model and a combo box to the deck dock widget to change it. --------- Co-authored-by: Lukas Brübach --- .../deck_editor_deck_dock_widget.cpp | 18 +++++++++++++ .../deck_editor_deck_dock_widget.h | 2 ++ cockatrice/src/deck/deck_list_model.cpp | 26 ++++++++++++++++++- cockatrice/src/deck/deck_list_model.h | 10 +++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 3683666a2..efe1c0bc9 100644 --- a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -104,6 +104,20 @@ void DeckEditorDeckDockWidget::createDeckDock() deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()); deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible()); + activeGroupCriteriaLabel = new QLabel(this); + + activeGroupCriteriaComboBox = new QComboBox(this); + activeGroupCriteriaComboBox->addItem(tr("Main Type"), DeckListModelGroupCriteria::MAIN_TYPE); + activeGroupCriteriaComboBox->addItem(tr("Mana Cost"), DeckListModelGroupCriteria::MANA_COST); + activeGroupCriteriaComboBox->addItem(tr("Colors"), DeckListModelGroupCriteria::COLOR); + connect(activeGroupCriteriaComboBox, QOverload::of(&QComboBox::currentIndexChanged), [this]() { + deckModel->setActiveGroupCriteria( + static_cast(activeGroupCriteriaComboBox->currentData(Qt::UserRole).toInt())); + deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); + deckView->expandAll(); + deckView->expandAll(); + }); + aIncrement = new QAction(QString(), this); aIncrement->setIcon(QPixmap("theme:icons/increment")); connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrement); @@ -144,6 +158,9 @@ void DeckEditorDeckDockWidget::createDeckDock() upperLayout->addWidget(deckTagsDisplayWidget, 3, 1); + upperLayout->addWidget(activeGroupCriteriaLabel, 4, 0); + upperLayout->addWidget(activeGroupCriteriaComboBox, 4, 1); + hashLabel1 = new QLabel(); hashLabel1->setObjectName("hashLabel1"); auto *hashSizePolicy = new QSizePolicy(); @@ -578,6 +595,7 @@ void DeckEditorDeckDockWidget::retranslateUi() showBannerCardCheckBox->setText(tr("Show banner card selection menu")); showTagsWidgetCheckBox->setText(tr("Show tags selection menu")); commentsLabel->setText(tr("&Comments:")); + activeGroupCriteriaLabel->setText(tr("Group by:")); hashLabel1->setText(tr("Hash:")); aIncrement->setText(tr("&Increment number")); diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h index c81b4a6fa..27364a662 100644 --- a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -70,6 +70,8 @@ private: DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget; QLabel *hashLabel1; LineEditUnfocusable *hashLabel; + QLabel *activeGroupCriteriaLabel; + QComboBox *activeGroupCriteriaComboBox; QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard; diff --git a/cockatrice/src/deck/deck_list_model.cpp b/cockatrice/src/deck/deck_list_model.cpp index 5d8cb60f3..746a1d9e9 100644 --- a/cockatrice/src/deck/deck_list_model.cpp +++ b/cockatrice/src/deck/deck_list_model.cpp @@ -29,6 +29,24 @@ DeckListModel::~DeckListModel() delete root; } +QString DeckListModel::getSortCriteriaForCard(CardInfoPtr info) +{ + if (!info) { + return "unknown"; + } + + switch (activeGroupCriteria) { + case DeckListModelGroupCriteria::MAIN_TYPE: + return info->getMainCardType(); + case DeckListModelGroupCriteria::MANA_COST: + return info->getCmc(); + case DeckListModelGroupCriteria::COLOR: + return info->getColors() == "" ? "Colorless" : info->getColors(); + default: + return "unknown"; + } +} + void DeckListModel::rebuildTree() { beginResetModel(); @@ -49,7 +67,7 @@ void DeckListModel::rebuildTree() } CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName()); - QString cardType = info ? info->getMainCardType() : "unknown"; + QString cardType = getSortCriteriaForCard(info); auto *cardTypeNode = dynamic_cast(node->findChild(cardType)); @@ -467,6 +485,12 @@ void DeckListModel::sort(int column, Qt::SortOrder order) emit layoutChanged(); } +void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria) +{ + activeGroupCriteria = newCriteria; + rebuildTree(); +} + void DeckListModel::cleanList() { setDeckList(new DeckLoader); diff --git a/cockatrice/src/deck/deck_list_model.h b/cockatrice/src/deck/deck_list_model.h index 94b323e18..81ae06508 100644 --- a/cockatrice/src/deck/deck_list_model.h +++ b/cockatrice/src/deck/deck_list_model.h @@ -12,6 +12,13 @@ class CardDatabase; class QPrinter; class QTextCursor; +enum DeckListModelGroupCriteria +{ + MAIN_TYPE, + MANA_COST, + COLOR +}; + class DecklistModelCardNode : public AbstractDecklistCardNode { private: @@ -85,6 +92,7 @@ signals: public: explicit DeckListModel(QObject *parent = nullptr); ~DeckListModel() override; + QString getSortCriteriaForCard(CardInfoPtr info); int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; @@ -113,10 +121,12 @@ public: QList getCardsAsCardInfoPtrs() const; QList getCardsAsCardInfoPtrsForZone(QString zoneName) const; QList *getZones() const; + void setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria); private: DeckLoader *deckList; InnerDecklistNode *root; + DeckListModelGroupCriteria activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE; int lastKnownColumn; Qt::SortOrder lastKnownOrder; InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); From d51620640b3d6ba671d07ee470ab9083feace38a Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 12 May 2025 14:39:58 -0700 Subject: [PATCH 05/20] [VDS] Add setting to show deck filepath in tooltip (#5899) * [VDS] Show deck filepath in tooltip * Make tooltip type into a setting * Fix build failure --- .../deck_preview/deck_preview_widget.cpp | 24 +++++++++++++- .../deck_preview/deck_preview_widget.h | 1 + ...ual_deck_storage_quick_settings_widget.cpp | 31 +++++++++++++++++++ ...isual_deck_storage_quick_settings_widget.h | 15 +++++++++ cockatrice/src/settings/cache_settings.cpp | 7 +++++ cockatrice/src/settings/cache_settings.h | 6 ++++ dbconverter/src/mocks.cpp | 3 ++ tests/carddatabase/mocks.cpp | 3 ++ 8 files changed, 89 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp index 556968d2b..165160718 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp @@ -46,6 +46,8 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent, &DeckPreviewWidget::updateTagsVisibility); connect(&SettingsCache::instance(), &SettingsCache::visualDeckStorageShowBannerCardComboBoxChanged, this, &DeckPreviewWidget::updateBannerCardComboBoxVisibility); + connect(visualDeckStorageWidget->settings(), &VisualDeckStorageQuickSettingsWidget::deckPreviewTooltipChanged, this, + &DeckPreviewWidget::refreshBannerCardToolTip); layout->addWidget(bannerCardDisplayWidget); } @@ -79,7 +81,6 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess) bannerCardDisplayWidget->setCard(bannerCard); bannerCardDisplayWidget->setFontSize(24); - refreshBannerCardText(); setFilePath(deckLoader->getLastFileName()); colorIdentityWidget = new ColorIdentityWidget(this, getColorIdentity()); @@ -104,6 +105,8 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess) layout->addWidget(bannerCardLabel); layout->addWidget(bannerCardComboBox); + refreshBannerCardText(); + retranslateUi(); } @@ -184,10 +187,29 @@ void DeckPreviewWidget::setFilePath(const QString &_filePath) filePath = _filePath; } +/** + * Refreshes the banner card text. + * This also calls `refreshBannerCardToolTip`, since those two often need to be updated together. + */ void DeckPreviewWidget::refreshBannerCardText() { bannerCardDisplayWidget->setOverlayText( deckLoader->getName().isEmpty() ? QFileInfo(deckLoader->getLastFileName()).fileName() : deckLoader->getName()); + + refreshBannerCardToolTip(); +} + +void DeckPreviewWidget::refreshBannerCardToolTip() +{ + auto type = visualDeckStorageWidget->settings()->getDeckPreviewTooltip(); + switch (type) { + case VisualDeckStorageQuickSettingsWidget::TooltipType::None: + bannerCardDisplayWidget->setToolTip(""); + break; + case VisualDeckStorageQuickSettingsWidget::TooltipType::Filepath: + bannerCardDisplayWidget->setToolTip(filePath); + break; + } } void DeckPreviewWidget::updateBannerCardComboBox() diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h index 8b594a9db..7c0ff1ad4 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h @@ -49,6 +49,7 @@ signals: public slots: void setFilePath(const QString &filePath); void refreshBannerCardText(); + void refreshBannerCardToolTip(); void updateBannerCardComboBox(); void setBannerCard(int); void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp index 3b9063a9b..cd085a511 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp @@ -4,6 +4,7 @@ #include "visual_deck_storage_widget.h" #include +#include #include VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidget *parent) @@ -80,6 +81,26 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacityLabel); unusedColorIdentityOpacityLayout->addWidget(unusedColorIdentitiesOpacitySpinBox); + // tooltip selector + auto deckPreviewTooltipWidget = new QWidget(this); + + deckPreviewTooltipLabel = new QLabel(deckPreviewTooltipWidget); + deckPreviewTooltipComboBox = new QComboBox(deckPreviewTooltipWidget); + deckPreviewTooltipComboBox->setFocusPolicy(Qt::StrongFocus); + deckPreviewTooltipComboBox->addItem("", TooltipType::None); + deckPreviewTooltipComboBox->addItem("", TooltipType::Filepath); + + deckPreviewTooltipComboBox->setCurrentIndex(SettingsCache::instance().getVisualDeckStorageTooltipType()); + connect(deckPreviewTooltipComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + [this] { emit deckPreviewTooltipChanged(getDeckPreviewTooltip()); }); + connect(deckPreviewTooltipComboBox, QOverload::of(&QComboBox::currentIndexChanged), &SettingsCache::instance(), + &SettingsCache::setVisualDeckStorageTooltipType); + + auto deckPreviewTooltipLayout = new QHBoxLayout(deckPreviewTooltipWidget); + deckPreviewTooltipLayout->setContentsMargins(11, 0, 11, 0); + deckPreviewTooltipLayout->addWidget(deckPreviewTooltipLabel); + deckPreviewTooltipLayout->addWidget(deckPreviewTooltipComboBox); + // card size slider cardSizeWidget = new CardSizeWidget(this, nullptr, SettingsCache::instance().getVisualDeckStorageCardSize()); connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, this, @@ -95,6 +116,7 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg this->addSettingsWidget(searchFolderNamesCheckBox); this->addSettingsWidget(drawUnusedColorIdentitiesCheckBox); this->addSettingsWidget(unusedColorIdentityOpacityWidget); + this->addSettingsWidget(deckPreviewTooltipWidget); this->addSettingsWidget(cardSizeWidget); connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, @@ -112,6 +134,10 @@ void VisualDeckStorageQuickSettingsWidget::retranslateUi() drawUnusedColorIdentitiesCheckBox->setText(tr("Draw unused Color Identities")); unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity")); unusedColorIdentitiesOpacitySpinBox->setSuffix("%"); + + deckPreviewTooltipLabel->setText(tr("Deck tooltip:")); + deckPreviewTooltipComboBox->setItemText(0, tr("None")); + deckPreviewTooltipComboBox->setItemText(1, tr("Filepath")); } bool VisualDeckStorageQuickSettingsWidget::getShowFolders() const @@ -149,6 +175,11 @@ int VisualDeckStorageQuickSettingsWidget::getUnusedColorIdentitiesOpacity() cons return unusedColorIdentitiesOpacitySpinBox->value(); } +VisualDeckStorageQuickSettingsWidget::TooltipType VisualDeckStorageQuickSettingsWidget::getDeckPreviewTooltip() const +{ + return deckPreviewTooltipComboBox->currentData().value(); +} + int VisualDeckStorageQuickSettingsWidget::getCardSize() const { return cardSizeWidget->getSlider()->value(); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h index da82b177b..d27a9053b 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h @@ -7,6 +7,7 @@ class CardSizeWidget; class QLabel; class QSpinBox; class QCheckBox; +class QComboBox; /** * The VDS's quick settings menu. @@ -25,9 +26,21 @@ class VisualDeckStorageQuickSettingsWidget : public SettingsButtonWidget QCheckBox *searchFolderNamesCheckBox; QLabel *unusedColorIdentitiesOpacityLabel; QSpinBox *unusedColorIdentitiesOpacitySpinBox; + QLabel *deckPreviewTooltipLabel; + QComboBox *deckPreviewTooltipComboBox; CardSizeWidget *cardSizeWidget; public: + /** + * The info to display in the deck preview's banner card tooltip. + */ + enum TooltipType + { + None, + Filepath + }; + Q_ENUM(TooltipType) + explicit VisualDeckStorageQuickSettingsWidget(QWidget *parent = nullptr); void retranslateUi(); @@ -39,6 +52,7 @@ public: bool getShowTagsOnDeckPreviews() const; bool getSearchFolderNames() const; int getUnusedColorIdentitiesOpacity() const; + TooltipType getDeckPreviewTooltip() const; int getCardSize() const; signals: @@ -49,6 +63,7 @@ signals: void showTagsOnDeckPreviewsChanged(bool enabled); void searchFolderNamesChanged(bool enabled); void unusedColorIdentitiesOpacityChanged(int opacity); + void deckPreviewTooltipChanged(TooltipType tooltip); void cardSizeChanged(int scale); }; diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index ef369641b..acd8cd581 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -280,6 +280,7 @@ SettingsCache::SettingsCache() settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool(); visualDeckStorageUnusedColorIdentitiesOpacity = settings->value("interface/visualdeckstorageunusedcoloridentitiesopacity", 15).toInt(); + visualDeckStorageTooltipType = settings->value("interface/visualdeckstoragetooltiptype", 0).toInt(); visualDeckStoragePromptForConversion = settings->value("interface/visualdeckstoragepromptforconversion", true).toBool(); visualDeckStorageAlwaysConvert = settings->value("interface/visualdeckstoragealwaysconvert", false).toBool(); @@ -774,6 +775,12 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visual emit visualDeckStorageUnusedColorIdentitiesOpacityChanged(visualDeckStorageUnusedColorIdentitiesOpacity); } +void SettingsCache::setVisualDeckStorageTooltipType(int value) +{ + visualDeckStorageTooltipType = value; + settings->setValue("interface/visualdeckstoragetooltiptype", visualDeckStorageTooltipType); +} + void SettingsCache::setVisualDeckStoragePromptForConversion(bool _visualDeckStoragePromptForConversion) { visualDeckStoragePromptForConversion = _visualDeckStoragePromptForConversion; diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index 94cb2ed6a..f0861af58 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -150,6 +150,7 @@ private: int visualDeckStorageCardSize; bool visualDeckStorageDrawUnusedColorIdentities; int visualDeckStorageUnusedColorIdentitiesOpacity; + int visualDeckStorageTooltipType; bool visualDeckStoragePromptForConversion; bool visualDeckStorageAlwaysConvert; bool visualDeckStorageInGame; @@ -476,6 +477,10 @@ public: { return visualDeckStorageUnusedColorIdentitiesOpacity; } + int getVisualDeckStorageTooltipType() const + { + return visualDeckStorageTooltipType; + } bool getVisualDeckStoragePromptForConversion() const { return visualDeckStoragePromptForConversion; @@ -852,6 +857,7 @@ public slots: void setVisualDeckStorageCardSize(int _visualDeckStorageCardSize); void setVisualDeckStorageDrawUnusedColorIdentities(QT_STATE_CHANGED_T _visualDeckStorageDrawUnusedColorIdentities); void setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visualDeckStorageUnusedColorIdentitiesOpacity); + void setVisualDeckStorageTooltipType(int value); void setVisualDeckStoragePromptForConversion(bool _visualDeckStoragePromptForConversion); void setVisualDeckStorageAlwaysConvert(bool _visualDeckStorageAlwaysConvert); void setVisualDeckStorageInGame(QT_STATE_CHANGED_T value); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index 032cdc95e..acd199759 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -247,6 +247,9 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity( int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) { } +void SettingsCache::setVisualDeckStorageTooltipType(int /* value */) +{ +} void SettingsCache::setVisualDeckStoragePromptForConversion(bool /* _visualDeckStoragePromptForConversion */) { } diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index db7dd9353..8ac925c0b 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -251,6 +251,9 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity( int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) { } +void SettingsCache::setVisualDeckStorageTooltipType(int /* value */) +{ +} void SettingsCache::setVisualDeckStoragePromptForConversion(bool /* _visualDeckStoragePromptForConversion */) { } From 5dd027ad639d3a2f160fe3a67bcee85f939b9c2d Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 16 May 2025 11:39:33 -0700 Subject: [PATCH 06/20] [VDS] Deselect tag if clicked while already selected (#5944) --- .../deck_preview/deck_preview_tag_display_widget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp index 4cfd9447d..db466b77a 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp @@ -50,10 +50,10 @@ void DeckPreviewTagDisplayWidget::mousePressEvent(QMouseEvent *event) { switch (event->button()) { case Qt::LeftButton: - setState(TagState::Selected); + setState(state != TagState::Selected ? TagState::Selected : TagState::NotSelected); break; case Qt::RightButton: - setState(TagState::Excluded); + setState(state != TagState::Excluded ? TagState::Excluded : TagState::NotSelected); break; case Qt::MiddleButton: setState(TagState::NotSelected); From 8cc64bf44ea9bb0465645dfcabb1974a78da69bf Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 17 May 2025 19:23:30 -0700 Subject: [PATCH 07/20] Refactor TabGame: inline some QLayout and QWidget class fields (#5942) * inline layouts * inline widgets --- cockatrice/src/client/tabs/tab_game.cpp | 33 +++++++++++++------------ cockatrice/src/client/tabs/tab_game.h | 6 ++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index 999b5b164..1c8724278 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -1538,7 +1538,7 @@ void TabGame::createPlayAreaWidget(bool bReplay) scene = new GameScene(phasesToolbar, this); gameView = new GameView(scene); - gamePlayAreaVBox = new QVBoxLayout; + auto gamePlayAreaVBox = new QVBoxLayout; gamePlayAreaVBox->setContentsMargins(0, 0, 0, 0); gamePlayAreaVBox->addWidget(gameView); @@ -1594,12 +1594,12 @@ void TabGame::createReplayDock() connect(replayFastForwardButton, &QToolButton::toggled, this, &TabGame::replayFastForwardButtonToggled); // putting everything together - replayControlLayout = new QHBoxLayout; + auto replayControlLayout = new QHBoxLayout; replayControlLayout->addWidget(timelineWidget, 10); replayControlLayout->addWidget(replayPlayButton); replayControlLayout->addWidget(replayFastForwardButton); - replayControlWidget = new QWidget(); + auto replayControlWidget = new QWidget(); replayControlWidget->setObjectName("replayControlWidget"); replayControlWidget->setLayout(replayControlLayout); @@ -1635,13 +1635,13 @@ void TabGame::createCardInfoDock(bool bReplay) Q_UNUSED(bReplay); cardInfoFrameWidget = new CardInfoFrameWidget(); - cardHInfoLayout = new QHBoxLayout; - cardVInfoLayout = new QVBoxLayout; + auto cardHInfoLayout = new QHBoxLayout; + auto cardVInfoLayout = new QVBoxLayout; cardVInfoLayout->setContentsMargins(0, 0, 0, 0); cardVInfoLayout->addWidget(cardInfoFrameWidget); cardVInfoLayout->addLayout(cardHInfoLayout); - cardBoxLayoutWidget = new QWidget; + auto cardBoxLayoutWidget = new QWidget; cardBoxLayoutWidget->setLayout(cardVInfoLayout); cardInfoDock = new QDockWidget(this); @@ -1679,12 +1679,17 @@ void TabGame::createPlayerListDock(bool bReplay) void TabGame::createMessageDock(bool bReplay) { + auto messageLogLayout = new QVBoxLayout; + messageLogLayout->setContentsMargins(0, 0, 0, 0); + messageLog = new MessageLogWidget(tabSupervisor, this); connect(messageLog, &MessageLogWidget::cardNameHovered, cardInfoFrameWidget, qOverload(&CardInfoFrameWidget::setCard)); connect(messageLog, &MessageLogWidget::showCardInfoPopup, this, &TabGame::showCardInfoPopup); connect(messageLog, &MessageLogWidget::deleteCardInfoPopup, this, &TabGame::deleteCardInfoPopup); + messageLogLayout->addWidget(messageLog); + if (!bReplay) { connect(messageLog, &MessageLogWidget::openMessageDialog, this, &TabGame::openMessageDialog); connect(messageLog, &MessageLogWidget::addMentionTag, this, &TabGame::addMentionTag); @@ -1698,6 +1703,8 @@ void TabGame::createMessageDock(bool bReplay) connect(gameTimer, &QTimer::timeout, this, &TabGame::incrementGameTime); gameTimer->start(); + messageLogLayout->addWidget(timeElapsedLabel); + sayLabel = new QLabel; sayEdit = new LineEditCompleter; sayEdit->setMaxLength(MAX_TEXT_LENGTH); @@ -1726,20 +1733,14 @@ void TabGame::createMessageDock(bool bReplay) connect(tabSupervisor, &TabSupervisor::adminLockChanged, this, &TabGame::adminLockChanged); connect(sayEdit, &LineEditCompleter::returnPressed, this, &TabGame::actSay); - sayHLayout = new QHBoxLayout; + auto sayHLayout = new QHBoxLayout; sayHLayout->addWidget(sayLabel); sayHLayout->addWidget(sayEdit); + + messageLogLayout->addLayout(sayHLayout); } - messageLogLayout = new QVBoxLayout; - messageLogLayout->setContentsMargins(0, 0, 0, 0); - if (!bReplay) - messageLogLayout->addWidget(timeElapsedLabel); - messageLogLayout->addWidget(messageLog); - if (!bReplay) - messageLogLayout->addLayout(sayHLayout); - - messageLogLayoutWidget = new QWidget; + auto messageLogLayoutWidget = new QWidget; messageLogLayoutWidget->setLayout(messageLogLayout); messageLayoutDock = new QDockWidget(this); diff --git a/cockatrice/src/client/tabs/tab_game.h b/cockatrice/src/client/tabs/tab_game.h index 096998728..370634742 100644 --- a/cockatrice/src/client/tabs/tab_game.h +++ b/cockatrice/src/client/tabs/tab_game.h @@ -111,10 +111,8 @@ private: GameScene *scene; GameView *gameView; QMap deckViewContainers; - QVBoxLayout *cardVInfoLayout, *messageLogLayout, *gamePlayAreaVBox, *deckViewContainerLayout; - QHBoxLayout *cardHInfoLayout, *sayHLayout, *mainHLayout, *replayControlLayout; - QWidget *cardBoxLayoutWidget, *messageLogLayoutWidget, *gamePlayAreaWidget, *deckViewContainerWidget, - *replayControlWidget; + QVBoxLayout *deckViewContainerLayout; + QWidget *gamePlayAreaWidget, *deckViewContainerWidget; QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock; QAction *playersSeparator; QMenu *gameMenu, *viewMenu, *cardInfoDockMenu, *messageLayoutDockMenu, *playerListDockMenu, *replayDockMenu; From 1eee314d17e05e1a7420925d817b0927489d2ec1 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 17 May 2025 19:23:54 -0700 Subject: [PATCH 08/20] [VDS] Add ability to search by deck contents (#5943) * [VDS] Add ability to search by deck contents * add deck search syntax help * fix build failure --- cockatrice/CMakeLists.txt | 1 + cockatrice/cockatrice.qrc | 1 + cockatrice/resources/config/qtlogging.ini | 2 + cockatrice/resources/help/deck_search.md | 26 +++ .../visual_deck_storage_search_widget.cpp | 29 +-- .../src/game/filters/deck_filter_string.cpp | 167 ++++++++++++++++++ .../src/game/filters/deck_filter_string.h | 51 ++++++ cockatrice/src/game/filters/filter_string.cpp | 4 +- cockatrice/src/game/filters/syntax_help.cpp | 27 ++- cockatrice/src/game/filters/syntax_help.h | 5 + 10 files changed, 297 insertions(+), 16 deletions(-) create mode 100644 cockatrice/resources/help/deck_search.md create mode 100644 cockatrice/src/game/filters/deck_filter_string.cpp create mode 100644 cockatrice/src/game/filters/deck_filter_string.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 50c721a6f..c25b319db 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -190,6 +190,7 @@ set(cockatrice_SOURCES src/game/cards/card_search_model.cpp src/game/deckview/deck_view.cpp src/game/deckview/deck_view_container.cpp + src/game/filters/deck_filter_string.cpp src/game/filters/filter_builder.cpp src/game/filters/filter_card.cpp src/game/filters/filter_string.cpp diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index 38c40561f..72ca1d83f 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -379,5 +379,6 @@ resources/tips/tips_of_the_day.xml resources/help/search.md + resources/help/deck_search.md diff --git a/cockatrice/resources/config/qtlogging.ini b/cockatrice/resources/config/qtlogging.ini index eeae47b7f..2cd3d871a 100644 --- a/cockatrice/resources/config/qtlogging.ini +++ b/cockatrice/resources/config/qtlogging.ini @@ -60,4 +60,6 @@ # pixel_map_generator = false +# deck_filter_string = false # filter_string = false +# syntax_help = false diff --git a/cockatrice/resources/help/deck_search.md b/cockatrice/resources/help/deck_search.md new file mode 100644 index 000000000..cb06a40a6 --- /dev/null +++ b/cockatrice/resources/help/deck_search.md @@ -0,0 +1,26 @@ +## Deck Search Syntax Help +----- +The search bar recognizes a set of special commands.
+In this list of examples below, each entry has an explanation and can be clicked to test the query. Note that all +searches are case insensitive. +
+
Filename:
+
[red deck wins](#red deck wins) (Any deck filename containing the words red, deck, and wins)
+
["red deck wins"](#%22red deck wins%22) (Any deck filename containing the exact phrase "red deck wins")
+ +
Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):
+
[[plains]] (Any deck that contains at least one card with "plains" in its name)
+
[[t:legendary]] (Any deck that contains at least one legendary)
+
[[t:legendary]]>5 (Any card that contains at least 5 legendaries)
+
[[]]:100 (Any deck that contains exactly 100 cards)
+ +
Negate:
+
[soldier -aggro](#soldier -aggro) (Any deck filename that contains "soldier", but not "aggro")
+ +
Branching:
+
[t:aggro OR o:control](#t:aggro OR o:control) (Any deck filename that contains either aggro or control)
+ +
Grouping:
+
red -([[]]:100 or aggro) (Any deck that has red in its filename but is not 100 cards or has aggro in its filename)
+ +
diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp index 9b7f65224..2998845f1 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp @@ -1,6 +1,11 @@ #include "visual_deck_storage_search_widget.h" +#include "../../../../game/filters/deck_filter_string.h" +#include "../../../../game/filters/syntax_help.h" #include "../../../../settings/cache_settings.h" +#include "../../pixel_map_generator.h" + +#include /** * @brief Constructs a PrintingSelectorCardSearchWidget for searching cards by set name or set code. @@ -17,7 +22,13 @@ VisualDeckStorageSearchWidget::VisualDeckStorageSearchWidget(VisualDeckStorageWi setLayout(layout); searchBar = new QLineEdit(this); - searchBar->setPlaceholderText(tr("Search by filename")); + searchBar->setPlaceholderText(tr("Search by filename (or search expression)")); + searchBar->setClearButtonEnabled(true); + searchBar->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); + + auto help = searchBar->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); + connect(help, &QAction::triggered, this, [this] { createDeckSearchSyntaxHelpWindow(searchBar); }); + layout->addWidget(searchBar); // Add a debounce timer for the search bar to limit frequent updates @@ -52,11 +63,11 @@ static QString getFileSearchName(const QString &filePath, bool includeFolderName { QString deckPath = SettingsCache::instance().getDeckPath(); if (includeFolderName && filePath.startsWith(deckPath)) { - return filePath.mid(deckPath.length()).toLower(); + return filePath.mid(deckPath.length()); } QFileInfo fileInfo(filePath); - QString fileName = fileInfo.fileName().toLower(); + QString fileName = fileInfo.fileName(); return fileName; } @@ -64,14 +75,10 @@ void VisualDeckStorageSearchWidget::filterWidgets(QList wid const QString &searchText, bool includeFolderName) { - if (searchText.isEmpty() || searchText.isNull()) { - for (auto widget : widgets) { - widget->filteredBySearch = false; - } - } + auto filterString = DeckFilterString(searchText); - for (auto file : widgets) { - QString fileSearchName = getFileSearchName(file->filePath, includeFolderName); - file->filteredBySearch = !fileSearchName.contains(searchText.toLower()); + for (auto widget : widgets) { + QString fileSearchName = getFileSearchName(widget->filePath, includeFolderName); + widget->filteredBySearch = !filterString.check(widget, {fileSearchName}); } } diff --git a/cockatrice/src/game/filters/deck_filter_string.cpp b/cockatrice/src/game/filters/deck_filter_string.cpp new file mode 100644 index 000000000..52aa15c71 --- /dev/null +++ b/cockatrice/src/game/filters/deck_filter_string.cpp @@ -0,0 +1,167 @@ +#include "deck_filter_string.h" + +#include "../cards/card_database_manager.h" +#include "filter_string.h" +#include "lib/peglib.h" + +static peg::parser search(R"( +Start <- QueryPartList +~ws <- [ ]+ +QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws* + +ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart +SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart + +QueryPart <- NotQuery / DeckContentQuery / GenericQuery + +NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart + +DeckContentQuery <- CardSearch NumericExpression? +CardSearch <- '[[' CardFilterString ']]' +CardFilterString <- (!']]'.)* + +GenericQuery <- String + +NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["]. +NonSingleQuoteUnlessEscaped <- "\\\'". / ![']. +UnescapedStringListPart <- !['":<>=! ]. +SingleApostropheString <- (UnescapedStringListPart+ ws*)* ['] (UnescapedStringListPart+ ws*)* + +String <- SingleApostropheString / UnescapedStringListPart+ / ["] ["] / ['] ['] + +NumericExpression <- NumericOperator ws? NumericValue +NumericOperator <- [=:] / <[> +NumericValue <- [0-9]+ +)"); + +static std::once_flag init; + +static void setupParserRules() +{ + // plumbing + auto passthru = [](const peg::SemanticValues &sv) -> DeckFilter { + return !sv.empty() ? std::any_cast(sv[0]) : nullptr; + }; + + search["Start"] = passthru; + search["QueryPartList"] = [](const peg::SemanticValues &sv) -> DeckFilter { + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) { + auto matchesFilter = [&deck, &info](const std::any &query) { + return std::any_cast(query)(deck, info); + }; + return std::all_of(sv.begin(), sv.end(), matchesFilter); + }; + }; + search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> DeckFilter { + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) { + auto matchesFilter = [&deck, &info](const std::any &query) { + return std::any_cast(query)(deck, info); + }; + return std::any_of(sv.begin(), sv.end(), matchesFilter); + }; + }; + search["SomewhatComplexQueryPart"] = passthru; + search["QueryPart"] = passthru; + search["NotQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + const auto dependent = std::any_cast(sv[0]); + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) -> bool { + return !dependent(deck, info); + }; + }; + + search["String"] = [](const peg::SemanticValues &sv) -> QString { + if (sv.choice() == 0) { + return QString::fromStdString(std::string(sv.sv())); + } + + return QString::fromStdString(std::string(sv.token(0))); + }; + + search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher { + const auto arg = std::any_cast(sv[1]); + const auto op = std::any_cast(sv[0]); + + if (op == ">") + return [=](const int s) { return s > arg; }; + if (op == ">=") + return [=](const int s) { return s >= arg; }; + if (op == "<") + return [=](const int s) { return s < arg; }; + if (op == "<=") + return [=](const int s) { return s <= arg; }; + if (op == "=") + return [=](const int s) { return s == arg; }; + if (op == ":") + return [=](const int s) { return s == arg; }; + if (op == "!=") + return [=](const int s) { return s != arg; }; + return [](int) { return false; }; + }; + + search["NumericValue"] = [](const peg::SemanticValues &sv) -> int { + return QString::fromStdString(std::string(sv.sv())).toInt(); + }; + + search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString { + return QString::fromStdString(std::string(sv.sv())); + }; + + // actual functionality + search["DeckContentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + auto cardFilter = FilterString(std::any_cast(sv[0])); + auto numberMatcher = sv.size() > 1 ? std::any_cast(sv[1]) : [](int count) { return count > 0; }; + + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool { + int count = 0; + deck->deckLoader->forEachCard([&](InnerDecklistNode *, const DecklistCardNode *node) { + auto cardInfoPtr = CardDatabaseManager::getInstance()->getCard(node->getName()); + if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) { + count += node->getNumber(); + } + }); + return numberMatcher(count); + }; + }; + + search["CardSearch"] = [](const peg::SemanticValues &sv) -> QString { return std::any_cast(sv[0]); }; + + search["CardFilterString"] = [](const peg::SemanticValues &sv) -> QString { + return QString::fromStdString(std::string(sv.sv())); + }; + + search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + auto name = std::any_cast(sv[0]); + return [=](const DeckPreviewWidget *, const ExtraDeckSearchInfo &info) { + return info.fileSearchName.contains(name, Qt::CaseInsensitive); + }; + }; +} + +DeckFilterString::DeckFilterString() +{ + filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return false; }; + _error = "Not initialized"; +} + +DeckFilterString::DeckFilterString(const QString &expr) +{ + QByteArray ba = expr.simplified().toUtf8(); + + std::call_once(init, setupParserRules); + + _error = QString(); + + if (ba.isEmpty()) { + filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return true; }; + return; + } + + search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) { + _error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg)); + }); + + if (!search.parse(ba.data(), filter)) { + qCInfo(DeckFilterStringLog).nospace() << "DeckFilterString error for " << expr << "; " << qPrintable(_error); + filter = [](const DeckPreviewWidget *, const ExtraDeckSearchInfo &) { return false; }; + } +} \ No newline at end of file diff --git a/cockatrice/src/game/filters/deck_filter_string.h b/cockatrice/src/game/filters/deck_filter_string.h new file mode 100644 index 000000000..dfdb1f1a8 --- /dev/null +++ b/cockatrice/src/game/filters/deck_filter_string.h @@ -0,0 +1,51 @@ +#ifndef DECK_FILTER_STRING_H +#define DECK_FILTER_STRING_H + +#include "../../client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h" + +#include +#include +#include +#include +#include + +inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string"); + +/** + * Extra info relevant to filtering that isn't present in the DeckPreviewWidget + */ +struct ExtraDeckSearchInfo +{ + /** + * The filename used for filtering. Varies based on settings. + */ + QString fileSearchName; +}; + +typedef std::function DeckFilter; + +class DeckFilterString +{ +public: + DeckFilterString(); + explicit DeckFilterString(const QString &expr); + bool check(const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &info) const + { + return filter(deck, info); + } + + bool valid() const + { + return _error.isEmpty(); + } + + QString error() + { + return _error; + } + +private: + QString _error; + DeckFilter filter; +}; +#endif // DECK_FILTER_STRING_H diff --git a/cockatrice/src/game/filters/filter_string.cpp b/cockatrice/src/game/filters/filter_string.cpp index 01a7b6be9..e99805541 100644 --- a/cockatrice/src/game/filters/filter_string.cpp +++ b/cockatrice/src/game/filters/filter_string.cpp @@ -7,7 +7,7 @@ #include #include -peg::parser search(R"( +static peg::parser search(R"( Start <- QueryPartList ~ws <- [ ]+ QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws* @@ -63,7 +63,7 @@ NumericOperator <- [=:] / <[> NumericValue <- [0-9]+ )"); -std::once_flag init; +static std::once_flag init; static void setupParserRules() { diff --git a/cockatrice/src/game/filters/syntax_help.cpp b/cockatrice/src/game/filters/syntax_help.cpp index a4eaf9112..9fb918629 100644 --- a/cockatrice/src/game/filters/syntax_help.cpp +++ b/cockatrice/src/game/filters/syntax_help.cpp @@ -9,11 +9,12 @@ * * @return the QTextBrowser */ -static QTextBrowser *createBrowser() +static QTextBrowser *createBrowser(const QString &helpFile) { - QFile file("theme:help/search.md"); + QFile file(helpFile); if (!file.open(QFile::ReadOnly | QFile::Text)) { + qCWarning(SyntaxHelpLog) << "Could not open syntax help file: " << helpFile; return nullptr; } @@ -54,9 +55,29 @@ static QTextBrowser *createBrowser() */ QTextBrowser *createSearchSyntaxHelpWindow(QLineEdit *lineEdit) { - auto browser = createBrowser(); + auto browser = createBrowser("theme:help/search.md"); QObject::connect(browser, &QTextBrowser::anchorClicked, [lineEdit](const QUrl &link) { lineEdit->setText(link.fragment()); }); QObject::connect(lineEdit, &QObject::destroyed, browser, &QTextBrowser::close); return browser; +} + +/** + * Creates the deck search syntax help window and connects its anchorClicked signal to the given QLineEdit. + * The window will automatically close when the QLineEdit is destroyed. + * + * @return the QTextBrowser + */ +QTextBrowser *createDeckSearchSyntaxHelpWindow(QLineEdit *lineEdit) +{ + auto browser = createBrowser("theme:help/deck_search.md"); + QObject::connect(browser, &QTextBrowser::anchorClicked, [lineEdit](const QUrl &link) { + if (link.fragment() == "cardSearchSyntaxHelp") { + createSearchSyntaxHelpWindow(lineEdit); + } else { + lineEdit->setText(link.fragment()); + } + }); + QObject::connect(lineEdit, &QObject::destroyed, browser, &QTextBrowser::close); + return browser; } \ No newline at end of file diff --git a/cockatrice/src/game/filters/syntax_help.h b/cockatrice/src/game/filters/syntax_help.h index 4016e02bb..911c2fdf0 100644 --- a/cockatrice/src/game/filters/syntax_help.h +++ b/cockatrice/src/game/filters/syntax_help.h @@ -2,8 +2,13 @@ #define SEARCH_SYNTAX_HELP_H #include +#include #include +inline Q_LOGGING_CATEGORY(SyntaxHelpLog, "syntax_help"); + QTextBrowser *createSearchSyntaxHelpWindow(QLineEdit *lineEdit); +QTextBrowser *createDeckSearchSyntaxHelpWindow(QLineEdit *lineEdit); + #endif // SEARCH_SYNTAX_HELP_H From 269523a0340765c07288602c3db8ef9c934220b0 Mon Sep 17 00:00:00 2001 From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com> Date: Sat, 17 May 2025 23:24:14 -0300 Subject: [PATCH 09/20] ci: use Ninja on Linux and macOS builds (#5939) * ci: use Ninja on linux and macos * ci: simplify parallelism --- .ci/Arch/Dockerfile | 1 + .ci/Debian11/Dockerfile | 1 + .ci/Debian12/Dockerfile | 5 +++-- .ci/Fedora41/Dockerfile | 1 + .ci/Fedora42/Dockerfile | 1 + .ci/Ubuntu22.04/Dockerfile | 1 + .ci/Ubuntu24.04/Dockerfile | 5 +++-- .ci/compile.sh | 24 ++---------------------- .ci/docker.sh | 1 + .github/workflows/desktop-build.yml | 20 +++++++++----------- 10 files changed, 23 insertions(+), 37 deletions(-) diff --git a/.ci/Arch/Dockerfile b/.ci/Arch/Dockerfile index ed46e08a9..36cf5c4ae 100644 --- a/.ci/Arch/Dockerfile +++ b/.ci/Arch/Dockerfile @@ -7,6 +7,7 @@ RUN pacman --sync --refresh --sysupgrade --needed --noconfirm \ git \ gtest \ mariadb-libs \ + ninja \ protobuf \ qt6-base \ qt6-imageformats \ diff --git a/.ci/Debian11/Dockerfile b/.ci/Debian11/Dockerfile index 8c4fbe724..b994863bf 100644 --- a/.ci/Debian11/Dockerfile +++ b/.ci/Debian11/Dockerfile @@ -16,6 +16,7 @@ RUN apt-get update && \ libqt5sql5-mysql \ libqt5svg5-dev \ libqt5websockets5-dev \ + ninja-build \ protobuf-compiler \ qt5-image-formats-plugins \ qtmultimedia5-dev \ diff --git a/.ci/Debian12/Dockerfile b/.ci/Debian12/Dockerfile index 21c4ffa29..202405b84 100644 --- a/.ci/Debian12/Dockerfile +++ b/.ci/Debian12/Dockerfile @@ -15,13 +15,14 @@ RUN apt-get update && \ libprotobuf-dev \ libqt6multimedia6 \ libqt6sql6-mysql \ - qt6-svg-dev \ - qt6-websockets-dev \ + ninja-build \ protobuf-compiler \ qt6-image-formats-plugins \ qt6-l10n-tools \ qt6-multimedia-dev \ + qt6-svg-dev \ qt6-tools-dev \ qt6-tools-dev-tools \ + qt6-websockets-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Fedora41/Dockerfile b/.ci/Fedora41/Dockerfile index ab23a6052..fc9e86c2e 100644 --- a/.ci/Fedora41/Dockerfile +++ b/.ci/Fedora41/Dockerfile @@ -6,6 +6,7 @@ RUN dnf install -y \ gcc-c++ \ git \ mariadb-devel \ + ninja-build \ protobuf-devel \ qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \ qt6-qtimageformats \ diff --git a/.ci/Fedora42/Dockerfile b/.ci/Fedora42/Dockerfile index cf56ff604..ee4a856f2 100644 --- a/.ci/Fedora42/Dockerfile +++ b/.ci/Fedora42/Dockerfile @@ -6,6 +6,7 @@ RUN dnf install -y \ gcc-c++ \ git \ mariadb-devel \ + ninja-build \ protobuf-devel \ qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \ qt6-qtimageformats \ diff --git a/.ci/Ubuntu22.04/Dockerfile b/.ci/Ubuntu22.04/Dockerfile index 260f449cb..ff2e6e43b 100644 --- a/.ci/Ubuntu22.04/Dockerfile +++ b/.ci/Ubuntu22.04/Dockerfile @@ -17,6 +17,7 @@ RUN apt-get update && \ libqt6sql6-mysql \ libqt6svg6-dev \ libqt6websockets6-dev \ + ninja-build \ protobuf-compiler \ qt6-image-formats-plugins \ qt6-l10n-tools \ diff --git a/.ci/Ubuntu24.04/Dockerfile b/.ci/Ubuntu24.04/Dockerfile index d203690e1..809b2e43a 100644 --- a/.ci/Ubuntu24.04/Dockerfile +++ b/.ci/Ubuntu24.04/Dockerfile @@ -15,13 +15,14 @@ RUN apt-get update && \ libprotobuf-dev \ libqt6multimedia6 \ libqt6sql6-mysql \ - qt6-svg-dev \ - qt6-websockets-dev \ + ninja-build \ protobuf-compiler \ qt6-image-formats-plugins \ qt6-l10n-tools \ qt6-multimedia-dev \ + qt6-svg-dev \ qt6-tools-dev \ qt6-tools-dev-tools \ + qt6-websockets-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/.ci/compile.sh b/.ci/compile.sh index 1588c0d5f..6f51bc476 100755 --- a/.ci/compile.sh +++ b/.ci/compile.sh @@ -11,9 +11,8 @@ # --debug or --release sets the build type ie CMAKE_BUILD_TYPE # --ccache [] uses ccache and shows stats, optionally provide size # --dir sets the name of the build dir, default is "build" -# --parallel sets how many cores cmake should build with in parallel -# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR PARALLEL_COUNT -# (correspond to args: --debug/--release --install --package --suffix --server --test --ccache --dir --parallel ) +# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR +# (correspond to args: --debug/--release --install --package --suffix --server --test --ccache --dir ) # exitcode: 1 for failure, 3 for invalid arguments # Read arguments @@ -76,15 +75,6 @@ while [[ $# != 0 ]]; do BUILD_DIR="$1" shift ;; - '--parallel') - shift - if [[ $# == 0 ]]; then - echo "::error file=$0::--parallel expects an argument" - exit 3 - fi - PARALLEL_COUNT="$1" - shift - ;; *) echo "::error file=$0::unrecognized option: $1" exit 3 @@ -126,16 +116,6 @@ fi # Add cmake --build flags buildflags=(--config "$BUILDTYPE") -if [[ $PARALLEL_COUNT ]]; then - if [[ $(cmake --build /not_a_dir --parallel 2>&1 | head -1) =~ parallel ]]; then - # workaround for bionic having an old cmake - echo "this version of cmake does not support --parallel, using native build tool -j instead" - buildflags+=(-- -j "$PARALLEL_COUNT") - # note, no normal build flags should be added after this - else - buildflags+=(--parallel "$PARALLEL_COUNT") - fi -fi function ccachestatsverbose() { # note, verbose only works on newer ccache, discard the error diff --git a/.ci/docker.sh b/.ci/docker.sh index c6ec1b774..e6a900cb5 100644 --- a/.ci/docker.sh +++ b/.ci/docker.sh @@ -147,6 +147,7 @@ function RUN () if [[ $CCACHE_DIR ]]; then args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache") args+=(--env "CCACHE_DIR=/.ccache") + args+=(--env "CMAKE_GENERATOR="Ninja"") fi docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@" return $? diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 7d7652141..59e4d5005 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -129,6 +129,7 @@ jobs: # Cache size over the entire repo is 10Gi: # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy CCACHE_SIZE: 500M + CMAKE_GENERATOR: 'Ninja' steps: - name: Checkout @@ -156,9 +157,11 @@ jobs: - name: Build debug and test if: matrix.test != 'skip' shell: bash + env: + CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' run: | source .ci/docker.sh - RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 4 + RUN --server --debug --test --ccache "$CCACHE_SIZE" - name: Build release package id: build @@ -168,10 +171,11 @@ jobs: BUILD_DIR: build SUFFIX: '-${{matrix.distro}}${{matrix.version}}' type: '${{matrix.package}}' + CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' run: | source .ci/docker.sh RUN --server --release --package "$type" --dir "$BUILD_DIR" \ - --ccache "$CCACHE_SIZE" --parallel 4 + --ccache "$CCACHE_SIZE" .ci/name_build.sh - name: Upload artifact @@ -202,7 +206,6 @@ jobs: os: macos-13 xcode: "14.3.1" type: Release - core_count: 4 make_package: 1 - target: 14 @@ -210,7 +213,6 @@ jobs: os: macos-14 xcode: "15.4" type: Release - core_count: 3 make_package: 1 - target: 15 @@ -218,7 +220,6 @@ jobs: os: macos-15 xcode: "16.2" type: Release - core_count: 3 make_package: 1 - target: 15 @@ -226,7 +227,6 @@ jobs: os: macos-15 xcode: "16.2" type: Debug - core_count: 3 name: macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} needs: configure @@ -235,6 +235,7 @@ jobs: env: DEVELOPER_DIR: /Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer + CMAKE_GENERATOR: 'Ninja' steps: - name: Checkout @@ -262,9 +263,7 @@ jobs: MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} - # macOS runner have 3 cores usually - only the macos-13 image has 4: - # https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories - # https://github.com/actions/runner-images?tab=readme-ov-file#available-images + CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' run: | if [[ -n "$MACOS_CERTIFICATE_NAME" ]] then @@ -276,7 +275,7 @@ jobs: security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain fi - .ci/compile.sh --server --parallel ${{matrix.core_count}} + .ci/compile.sh --server - name: Sign app bundle if: matrix.make_package @@ -394,7 +393,6 @@ jobs: env: PACKAGE_SUFFIX: '-Win${{matrix.target}}' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' - CMAKE_GENERATOR_PLATFORM: 'x64' QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}' VCPKG_DISABLE_METRICS: 1 VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite' From 207211faccc81096956b8cc4d444b93b0d349c4e Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 18 May 2025 04:25:01 +0200 Subject: [PATCH 10/20] CI: Tweak ccache for Linux builds (#5938) * rework ccache * use branch name --- .github/workflows/desktop-build.yml | 32 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 59e4d5005..f5d3f86eb 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -135,25 +135,20 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Generate cache timestamp - id: cache_timestamp - shell: bash - run: echo "timestamp=$(date -u '+%Y%m%d%H%M%S')" >>"$GITHUB_OUTPUT" - - - name: Restore cache - uses: actions/cache@v4 - env: - timestamp: ${{steps.cache_timestamp.outputs.timestamp}} - with: - path: ${{env.CACHE}} - key: docker-${{matrix.distro}}${{matrix.version}}-cache-${{env.timestamp}} - restore-keys: | - docker-${{matrix.distro}}${{matrix.version}}-cache- - - name: Build ${{matrix.distro}} ${{matrix.version}} Docker image shell: bash run: source .ci/docker.sh --build + - name: Restore compiler cache (ccache) + id: ccache_restore + uses: actions/cache/restore@v4 + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + with: + path: ${{env.CACHE}} + key: ccache-${{matrix.distro}}${{matrix.version}}-${{env.BRANCH_NAME}} + restore-keys: ccache-${{matrix.distro}}${{matrix.version}}- + - name: Build debug and test if: matrix.test != 'skip' shell: bash @@ -178,6 +173,13 @@ jobs: --ccache "$CCACHE_SIZE" .ci/name_build.sh + - name: Save compiler cache (ccache) + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@v4 + with: + path: ${{env.CACHE}} + key: ${{ steps.ccache_restore.outputs.cache-primary-key }} + - name: Upload artifact if: matrix.package != 'skip' uses: actions/upload-artifact@v4 From cfbe59868bd0f12a59d9c1f4eb386d71597b84ba Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sun, 18 May 2025 04:25:13 +0200 Subject: [PATCH 11/20] [VDS] Add prompt before overwriting existing files when converting to .cod (#5926) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add prompt before overwriting existing files. * Lint. --------- Co-authored-by: Lukas Brübach --- .../deck_preview_deck_tags_display_widget.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp index 8c4723c37..d446589d0 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp @@ -12,6 +12,7 @@ #include #include #include +#include DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList) : QWidget(_parent), deckList(nullptr) @@ -77,6 +78,21 @@ static QStringList getAllFiles(const QString &filePath) return allFiles; } +bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath) +{ + QFileInfo fileInfo(filePath); + QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod"); + + if (QFile::exists(newFileName)) { + QMessageBox::StandardButton reply = + QMessageBox::question(parent, QObject::tr("Overwrite Existing File?"), + QObject::tr("A .cod version of this deck already exists. Overwrite it?"), + QMessageBox::Yes | QMessageBox::No); + return reply == QMessageBox::Yes; + } + return true; // Safe to proceed +} + void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg() { if (qobject_cast(parentWidget())) { @@ -91,6 +107,10 @@ void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg() // Retrieve saved preference if the prompt is disabled if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) { if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) { + + if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath)) + return; + deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath); deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName(); deckPreviewWidget->refreshBannerCardText(); @@ -100,6 +120,10 @@ void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg() // Show the dialog to the user DialogConvertDeckToCodFormat conversionDialog(parentWidget()); if (conversionDialog.exec() == QDialog::Accepted) { + + if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath)) + return; + deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath); deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName(); deckPreviewWidget->refreshBannerCardText(); From b270562a44e81951704214e2c947e3aed3fac84e Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 18 May 2025 19:05:27 -0700 Subject: [PATCH 12/20] Fix wrong Message Dock widget order from #5942 (#5946) * fix * refactor --- cockatrice/src/client/tabs/tab_game.cpp | 34 +++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index 1c8724278..40c25067c 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -1682,20 +1682,8 @@ void TabGame::createMessageDock(bool bReplay) auto messageLogLayout = new QVBoxLayout; messageLogLayout->setContentsMargins(0, 0, 0, 0); - messageLog = new MessageLogWidget(tabSupervisor, this); - connect(messageLog, &MessageLogWidget::cardNameHovered, cardInfoFrameWidget, - qOverload(&CardInfoFrameWidget::setCard)); - connect(messageLog, &MessageLogWidget::showCardInfoPopup, this, &TabGame::showCardInfoPopup); - connect(messageLog, &MessageLogWidget::deleteCardInfoPopup, this, &TabGame::deleteCardInfoPopup); - - messageLogLayout->addWidget(messageLog); - + // clock if (!bReplay) { - connect(messageLog, &MessageLogWidget::openMessageDialog, this, &TabGame::openMessageDialog); - connect(messageLog, &MessageLogWidget::addMentionTag, this, &TabGame::addMentionTag); - connect(&SettingsCache::instance(), &SettingsCache::chatMentionCompleterChanged, this, - &TabGame::actCompleterChanged); - timeElapsedLabel = new QLabel; timeElapsedLabel->setAlignment(Qt::AlignCenter); gameTimer = new QTimer(this); @@ -1704,7 +1692,26 @@ void TabGame::createMessageDock(bool bReplay) gameTimer->start(); messageLogLayout->addWidget(timeElapsedLabel); + } + // message log + messageLog = new MessageLogWidget(tabSupervisor, this); + connect(messageLog, &MessageLogWidget::cardNameHovered, cardInfoFrameWidget, + qOverload(&CardInfoFrameWidget::setCard)); + connect(messageLog, &MessageLogWidget::showCardInfoPopup, this, &TabGame::showCardInfoPopup); + connect(messageLog, &MessageLogWidget::deleteCardInfoPopup, this, &TabGame::deleteCardInfoPopup); + + if (!bReplay) { + connect(messageLog, &MessageLogWidget::openMessageDialog, this, &TabGame::openMessageDialog); + connect(messageLog, &MessageLogWidget::addMentionTag, this, &TabGame::addMentionTag); + connect(&SettingsCache::instance(), &SettingsCache::chatMentionCompleterChanged, this, + &TabGame::actCompleterChanged); + } + + messageLogLayout->addWidget(messageLog); + + // chat entry + if (!bReplay) { sayLabel = new QLabel; sayEdit = new LineEditCompleter; sayEdit->setMaxLength(MAX_TEXT_LENGTH); @@ -1740,6 +1747,7 @@ void TabGame::createMessageDock(bool bReplay) messageLogLayout->addLayout(sayHLayout); } + // dock auto messageLogLayoutWidget = new QWidget; messageLogLayoutWidget->setLayout(messageLogLayout); From 46643065eff1d3ce20995fef76cb485511c7369c Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 18 May 2025 19:06:18 -0700 Subject: [PATCH 13/20] Refactor banner card ComboBox (#5947) --- .../deck_editor/deck_editor_deck_dock_widget.cpp | 13 ++++--------- .../deck_preview/deck_preview_widget.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index efe1c0bc9..0cbeb2b50 100644 --- a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -303,11 +303,7 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() }); for (const auto &pair : pairList) { - QVariantMap dataMap; - dataMap["name"] = pair.first; - dataMap["uuid"] = pair.second; - - bannerCardComboBox->addItem(pair.first, dataMap); + bannerCardComboBox->addItem(pair.first, QVariant::fromValue(pair)); } // Try to restore the previous selection by finding the currentText @@ -315,7 +311,7 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() if (restoredIndex != -1) { bannerCardComboBox->setCurrentIndex(restoredIndex); if (deckModel->getDeckList()->getBannerCard().second != - bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap()["uuid"].toString()) { + bannerCardComboBox->currentData().value>().second) { setBannerCard(restoredIndex); } } else { @@ -335,9 +331,8 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */) { - QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap(); - deckModel->getDeckList()->setBannerCard( - QPair(itemData["name"].toString(), itemData["uuid"].toString())); + auto cardAndId = bannerCardComboBox->currentData().value>(); + deckModel->getDeckList()->setBannerCard(cardAndId); emit deckModified(); } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp index 165160718..7fc5c729f 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp @@ -282,11 +282,11 @@ void DeckPreviewWidget::updateBannerCardComboBox() void DeckPreviewWidget::setBannerCard(int /* changedIndex */) { - QVariant itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()); - deckLoader->setBannerCard(QPair(bannerCardComboBox->currentText(), itemData.toString())); + auto nameAndId = bannerCardComboBox->currentData().value>(); + deckLoader->setBannerCard(nameAndId); deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath)); - bannerCardDisplayWidget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId( - bannerCardComboBox->currentText(), itemData.toString())); + bannerCardDisplayWidget->setCard( + CardDatabaseManager::getInstance()->getCardByNameAndProviderId(nameAndId.first, nameAndId.second)); } void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance) From a5638ccc3b3acb49ffc8f3e9aa10c69a8888d0a4 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 18 May 2025 19:06:32 -0700 Subject: [PATCH 14/20] Parent the "new sets found" dialog (#5948) --- cockatrice/src/client/ui/window_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/client/ui/window_main.cpp b/cockatrice/src/client/ui/window_main.cpp index 7dabc96c6..721881c89 100644 --- a/cockatrice/src/client/ui/window_main.cpp +++ b/cockatrice/src/client/ui/window_main.cpp @@ -1099,7 +1099,7 @@ void MainWindow::cardDatabaseLoadingFailed() void MainWindow::cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames) { - QMessageBox msgBox; + QMessageBox msgBox(this); msgBox.setWindowTitle(tr("New sets found")); msgBox.setIcon(QMessageBox::Question); msgBox.setText(tr("%n new set(s) found in the card database\n" From d729df5cbadd21ecc74592545a924201d317ca9c Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 19 May 2025 05:09:03 -0700 Subject: [PATCH 15/20] Add missing shortcuts for Game Lobby buttons (#5951) --- cockatrice/src/game/deckview/deck_view_container.cpp | 2 ++ cockatrice/src/settings/shortcuts_settings.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/cockatrice/src/game/deckview/deck_view_container.cpp b/cockatrice/src/game/deckview/deck_view_container.cpp index 81ad8d322..64574ed87 100644 --- a/cockatrice/src/game/deckview/deck_view_container.cpp +++ b/cockatrice/src/game/deckview/deck_view_container.cpp @@ -198,8 +198,10 @@ void DeckViewContainer::refreshShortcuts() ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); loadLocalButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadLocalButton")); loadRemoteButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadRemoteButton")); + unloadDeckButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/unloadDeckButton")); readyStartButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/readyStartButton")); sideboardLockButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/sideboardLockButton")); + forceStartGameButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/forceStartGameButton")); } /** diff --git a/cockatrice/src/settings/shortcuts_settings.h b/cockatrice/src/settings/shortcuts_settings.h index 595932623..1407f5923 100644 --- a/cockatrice/src/settings/shortcuts_settings.h +++ b/cockatrice/src/settings/shortcuts_settings.h @@ -267,6 +267,9 @@ private: {"DeckViewContainer/loadRemoteButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Remote Deck..."), parseSequenceString("Ctrl+Alt+O"), ShortcutGroup::Game_Lobby)}, + {"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"), + parseSequenceString("Ctrl+Alt+U"), + ShortcutGroup::Game_Lobby)}, {"DeckViewContainer/readyStartButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Ready to Start"), parseSequenceString("Ctrl+Shift+S"), ShortcutGroup::Game_Lobby)}, @@ -274,6 +277,9 @@ private: ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Toggle Sideboard Lock"), parseSequenceString("Ctrl+Shift+B"), ShortcutGroup::Game_Lobby)}, + {"DeckViewContainer/forceStartGameButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Force Start"), + parseSequenceString(""), + ShortcutGroup::Game_Lobby)}, {"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Green Counter"), parseSequenceString("Ctrl+>"), ShortcutGroup::Card_Counters)}, From 50d3dfb98be3733734e1be9f00970b1f881f58f5 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 20 May 2025 07:36:17 -0700 Subject: [PATCH 16/20] Load deck from clipboard in Game Lobby (#5950) --- .../src/game/deckview/deck_view_container.cpp | 42 +++++++++++++++---- .../src/game/deckview/deck_view_container.h | 5 ++- cockatrice/src/settings/shortcuts_settings.h | 4 ++ common/decklist.cpp | 4 +- common/decklist.h | 4 +- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/game/deckview/deck_view_container.cpp b/cockatrice/src/game/deckview/deck_view_container.cpp index 64574ed87..4afd07943 100644 --- a/cockatrice/src/game/deckview/deck_view_container.cpp +++ b/cockatrice/src/game/deckview/deck_view_container.cpp @@ -4,6 +4,7 @@ #include "../../client/ui/picture_loader/picture_loader.h" #include "../../deck/deck_loader.h" #include "../../dialogs/dlg_load_deck.h" +#include "../../dialogs/dlg_load_deck_from_clipboard.h" #include "../../dialogs/dlg_load_remote_deck.h" #include "../../server/pending_command.h" #include "../../settings/cache_settings.h" @@ -52,6 +53,7 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) { loadLocalButton = new QPushButton; loadRemoteButton = new QPushButton; + loadFromClipboardButton = new QPushButton; unloadDeckButton = new QPushButton; readyStartButton = new ToggleButton; forceStartGameButton = new QPushButton; @@ -59,6 +61,7 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) connect(loadLocalButton, &QPushButton::clicked, this, &DeckViewContainer::loadLocalDeck); connect(loadRemoteButton, &QPushButton::clicked, this, &DeckViewContainer::loadRemoteDeck); + connect(loadFromClipboardButton, &QPushButton::clicked, this, &DeckViewContainer::loadFromClipboard); connect(readyStartButton, &QPushButton::clicked, this, &DeckViewContainer::readyStart); connect(unloadDeckButton, &QPushButton::clicked, this, &DeckViewContainer::unloadDeck); connect(forceStartGameButton, &QPushButton::clicked, this, &DeckViewContainer::forceStart); @@ -68,6 +71,7 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) auto *buttonHBox = new QHBoxLayout; buttonHBox->addWidget(loadLocalButton); buttonHBox->addWidget(loadRemoteButton); + buttonHBox->addWidget(loadFromClipboardButton); buttonHBox->addWidget(unloadDeckButton); buttonHBox->addWidget(readyStartButton); buttonHBox->addWidget(sideboardLockButton); @@ -118,6 +122,7 @@ void DeckViewContainer::retranslateUi() { loadLocalButton->setText(tr("Load deck...")); loadRemoteButton->setText(tr("Load remote deck...")); + loadFromClipboardButton->setText(tr("Load from clipboard...")); unloadDeckButton->setText(tr("Unload deck")); readyStartButton->setText(tr("Ready to start")); forceStartGameButton->setText(tr("Force start")); @@ -148,6 +153,7 @@ void DeckViewContainer::switchToDeckSelectView() setVisibility(loadLocalButton, true); setVisibility(loadRemoteButton, !parentGame->getIsLocalGame()); + setVisibility(loadFromClipboardButton, true); setVisibility(unloadDeckButton, false); setVisibility(readyStartButton, false); setVisibility(sideboardLockButton, false); @@ -172,6 +178,7 @@ void DeckViewContainer::switchToDeckLoadedView() setVisibility(loadLocalButton, false); setVisibility(loadRemoteButton, false); + setVisibility(loadFromClipboardButton, false); setVisibility(unloadDeckButton, true); setVisibility(readyStartButton, true); setVisibility(sideboardLockButton, true); @@ -198,6 +205,7 @@ void DeckViewContainer::refreshShortcuts() ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); loadLocalButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadLocalButton")); loadRemoteButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadRemoteButton")); + loadFromClipboardButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadFromClipboardButton")); unloadDeckButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/unloadDeckButton")); readyStartButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/readyStartButton")); sideboardLockButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/sideboardLockButton")); @@ -249,19 +257,27 @@ void DeckViewContainer::loadLocalDeck() void DeckViewContainer::loadDeckFromFile(const QString &filePath) { DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(filePath); - QString deckString; DeckLoader deck; - bool error = !deck.loadFromFile(filePath, fmt, true); - if (!error) { - deckString = deck.writeToString_Native(); - error = deckString.length() > MAX_FILE_LENGTH; - } - if (error) { + bool success = deck.loadFromFile(filePath, fmt, true); + + if (!success) { QMessageBox::critical(this, tr("Error"), tr("The selected file could not be loaded.")); return; } + loadDeckFromDeckLoader(&deck); +} + +void DeckViewContainer::loadDeckFromDeckLoader(const DeckLoader *deck) +{ + QString deckString = deck->writeToString_Native(); + + if (deckString.length() > MAX_FILE_LENGTH) { + QMessageBox::critical(this, tr("Error"), tr("Deck is greater than maximum file size.")); + return; + } + Command_DeckSelect cmd; cmd.set_deck(deckString.toStdString()); PendingCommand *pend = parentGame->prepareGameCommand(cmd); @@ -281,6 +297,18 @@ void DeckViewContainer::loadRemoteDeck() } } +void DeckViewContainer::loadFromClipboard() +{ + auto dlg = DlgLoadDeckFromClipboard(this); + + if (!dlg.exec()) { + return; + } + + DeckLoader *deck = dlg.getDeckList(); + loadDeckFromDeckLoader(deck); +} + void DeckViewContainer::deckSelectFinished(const Response &r) { const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); diff --git a/cockatrice/src/game/deckview/deck_view_container.h b/cockatrice/src/game/deckview/deck_view_container.h index c841c5934..d25a43d0d 100644 --- a/cockatrice/src/game/deckview/deck_view_container.h +++ b/cockatrice/src/game/deckview/deck_view_container.h @@ -44,7 +44,8 @@ class DeckViewContainer : public QWidget Q_OBJECT private: QVBoxLayout *deckViewLayout; - QPushButton *loadLocalButton, *loadRemoteButton, *unloadDeckButton, *forceStartGameButton; + QPushButton *loadLocalButton, *loadRemoteButton, *loadFromClipboardButton; + QPushButton *unloadDeckButton, *forceStartGameButton; ToggleButton *readyStartButton, *sideboardLockButton; DeckView *deckView; VisualDeckStorageWidget *visualDeckStorageWidget; @@ -58,6 +59,7 @@ private slots: void switchToDeckLoadedView(); void loadLocalDeck(); void loadRemoteDeck(); + void loadFromClipboard(); void unloadDeck(); void readyStart(); void forceStart(); @@ -81,6 +83,7 @@ public: public slots: void loadDeckFromFile(const QString &filePath); + void loadDeckFromDeckLoader(const DeckLoader *deck); }; #endif // DECK_VIEW_CONTAINER_H diff --git a/cockatrice/src/settings/shortcuts_settings.h b/cockatrice/src/settings/shortcuts_settings.h index 1407f5923..596781cff 100644 --- a/cockatrice/src/settings/shortcuts_settings.h +++ b/cockatrice/src/settings/shortcuts_settings.h @@ -267,6 +267,10 @@ private: {"DeckViewContainer/loadRemoteButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Remote Deck..."), parseSequenceString("Ctrl+Alt+O"), ShortcutGroup::Game_Lobby)}, + {"DeckViewContainer/loadFromClipboardButton", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."), + parseSequenceString("Ctrl+Shift+V"), + ShortcutGroup::Game_Lobby)}, {"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"), parseSequenceString("Ctrl+Alt+U"), ShortcutGroup::Game_Lobby)}, diff --git a/common/decklist.cpp b/common/decklist.cpp index 6f27d2895..a7da934f4 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -441,7 +441,7 @@ bool DeckList::readElement(QXmlStreamReader *xml) return true; } -void DeckList::write(QXmlStreamWriter *xml) +void DeckList::write(QXmlStreamWriter *xml) const { xml->writeStartElement("cockatrice_deck"); xml->writeAttribute("version", "1"); @@ -508,7 +508,7 @@ bool DeckList::loadFromString_Native(const QString &nativeString) return loadFromXml(&xml); } -QString DeckList::writeToString_Native() +QString DeckList::writeToString_Native() const { QString result; QXmlStreamWriter xml(&result); diff --git a/common/decklist.h b/common/decklist.h index 989c3e7fe..a2d58de47 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -345,10 +345,10 @@ public: } bool readElement(QXmlStreamReader *xml); - void write(QXmlStreamWriter *xml); + void write(QXmlStreamWriter *xml) const; bool loadFromXml(QXmlStreamReader *xml); bool loadFromString_Native(const QString &nativeString); - QString writeToString_Native(); + QString writeToString_Native() const; bool loadFromFile_Native(QIODevice *device); bool saveToFile_Native(QIODevice *device); bool loadFromStream_Plain(QTextStream &stream, bool preserveMetadata); From 452bf61ef91c5d66d37e3c99d4bde82d375d9b09 Mon Sep 17 00:00:00 2001 From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com> Date: Fri, 23 May 2025 01:01:53 +0200 Subject: [PATCH 17/20] FIX: re-add CMAKE_GENERATOR_PLATFORM: 'x64' (#5954) --- .github/workflows/desktop-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index f5d3f86eb..70acd3900 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -395,6 +395,7 @@ jobs: env: PACKAGE_SUFFIX: '-Win${{matrix.target}}' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' + CMAKE_GENERATOR_PLATFORM: 'x64' QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}' VCPKG_DISABLE_METRICS: 1 VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite' From cee67f430108baecadfab691fd65cf1a2392dd66 Mon Sep 17 00:00:00 2001 From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com> Date: Sat, 24 May 2025 15:24:15 +0200 Subject: [PATCH 18/20] ci: use ccache on macos (#5952) --- .github/workflows/desktop-build.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 70acd3900..1a9f18661 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -235,6 +235,7 @@ jobs: runs-on: ${{matrix.os}} continue-on-error: ${{matrix.allow-failure == 'yes'}} env: + CCACHE_DIR: /tmp/${{matrix.os}}-${{matrix.type}}-cache DEVELOPER_DIR: /Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer CMAKE_GENERATOR: 'Ninja' @@ -251,7 +252,17 @@ jobs: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 run: | brew update - brew install protobuf qt --force-bottle + brew install ccache protobuf qt --force-bottle + + - name: Restore compiler cache (ccache) + id: ccache_restore + uses: actions/cache/restore@v4 + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + with: + path: ${{env.CCACHE_DIR}} + key: ccache-${{matrix.os}}-${{matrix.type}}-${{env.BRANCH_NAME}} + restore-keys: ccache-${{matrix.os}}-${{matrix.type}}- - name: Build & Sign on Xcode ${{matrix.xcode}} shell: bash @@ -277,7 +288,14 @@ jobs: security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain fi - .ci/compile.sh --server + .ci/compile.sh --server --ccache "$CCACHE_SIZE" + + - name: Save compiler cache (ccache) + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@v4 + with: + path: ${{env.CCACHE_DIR}} + key: ${{ steps.ccache_restore.outputs.cache-primary-key }} - name: Sign app bundle if: matrix.make_package From 2cc75658415618b4209740b767cd6a21cf6dcd36 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sat, 7 Jun 2025 00:41:31 +0200 Subject: [PATCH 19/20] Only sign & notarize on master or tags (#5958) --- .github/workflows/desktop-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 1a9f18661..0d7ef4bf3 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -298,7 +298,7 @@ jobs: key: ${{ steps.ccache_restore.outputs.cache-primary-key }} - name: Sign app bundle - if: matrix.make_package + if: matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) env: MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} @@ -310,7 +310,7 @@ jobs: fi - name: Notarize app bundle - if: matrix.make_package + if: matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) env: MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} From 1b40c9e692b9904c53a571002dbea8e888147d3e Mon Sep 17 00:00:00 2001 From: tooomm Date: Sat, 7 Jun 2025 00:42:55 +0200 Subject: [PATCH 20/20] CI: Some cleanup (#5959) * Update desktop-build.yml * Update desktop-build.yml * use CMAKE_GENERATOR in docker.sh * revert to mtime --- .ci/docker.sh | 4 +++- .github/workflows/desktop-build.yml | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.ci/docker.sh b/.ci/docker.sh index e6a900cb5..a9fcfcc5b 100644 --- a/.ci/docker.sh +++ b/.ci/docker.sh @@ -147,7 +147,9 @@ function RUN () if [[ $CCACHE_DIR ]]; then args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache") args+=(--env "CCACHE_DIR=/.ccache") - args+=(--env "CMAKE_GENERATOR="Ninja"") + fi + if [[ -n "$CMAKE_GENERATOR" ]]; then + args+=(--env "CMAKE_GENERATOR=$CMAKE_GENERATOR") fi docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@" return $? diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 0d7ef4bf3..54a380683 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -125,7 +125,7 @@ jobs: continue-on-error: ${{matrix.allow-failure == 'yes'}} env: NAME: ${{matrix.distro}}${{matrix.version}} - CACHE: /tmp/${{matrix.distro}}${{matrix.version}}-cache # ${{runner.temp}} does not work? + CACHE: ${{github.workspace}}/.cache/${{matrix.distro}}${{matrix.version}} # directory for caching docker image and ccache # Cache size over the entire repo is 10Gi: # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy CCACHE_SIZE: 500M @@ -135,10 +135,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Build ${{matrix.distro}} ${{matrix.version}} Docker image - shell: bash - run: source .ci/docker.sh --build - - name: Restore compiler cache (ccache) id: ccache_restore uses: actions/cache/restore@v4 @@ -149,6 +145,10 @@ jobs: key: ccache-${{matrix.distro}}${{matrix.version}}-${{env.BRANCH_NAME}} restore-keys: ccache-${{matrix.distro}}${{matrix.version}}- + - name: Build ${{matrix.distro}} ${{matrix.version}} Docker image + shell: bash + run: source .ci/docker.sh --build + - name: Build debug and test if: matrix.test != 'skip' shell: bash @@ -165,11 +165,11 @@ jobs: env: BUILD_DIR: build SUFFIX: '-${{matrix.distro}}${{matrix.version}}' - type: '${{matrix.package}}' + package: '${{matrix.package}}' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' run: | source .ci/docker.sh - RUN --server --release --package "$type" --dir "$BUILD_DIR" \ + RUN --server --release --package "$package" --dir "$BUILD_DIR" \ --ccache "$CCACHE_SIZE" .ci/name_build.sh @@ -235,7 +235,8 @@ jobs: runs-on: ${{matrix.os}} continue-on-error: ${{matrix.allow-failure == 'yes'}} env: - CCACHE_DIR: /tmp/${{matrix.os}}-${{matrix.type}}-cache + CCACHE_DIR: ${{github.workspace}}/.ccache/${{matrix.os}}-${{matrix.type}} + CCACHE_SIZE: 500M DEVELOPER_DIR: /Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer CMAKE_GENERATOR: 'Ninja' @@ -269,7 +270,6 @@ jobs: id: build env: BUILDTYPE: '${{matrix.type}}' - MAKE_TEST: 1 MAKE_PACKAGE: '${{matrix.make_package}}' PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}' MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} @@ -288,7 +288,7 @@ jobs: security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain fi - .ci/compile.sh --server --ccache "$CCACHE_SIZE" + .ci/compile.sh --server --test --ccache "$CCACHE_SIZE" - name: Save compiler cache (ccache) if: github.ref == 'refs/heads/master' @@ -350,7 +350,7 @@ jobs: if-no-files-found: error - name: Upload to release - if: matrix.package != 'skip' && needs.configure.outputs.tag != null + if: matrix.make_package && needs.configure.outputs.tag != null shell: bash env: GH_TOKEN: ${{github.token}} @@ -438,7 +438,7 @@ jobs: if-no-files-found: error - name: Upload to release - if: matrix.package != 'skip' && needs.configure.outputs.tag != null + if: needs.configure.outputs.tag != null shell: bash env: GH_TOKEN: ${{github.token}}