From 1bc92623dc97059d856af093d00d3d47e27e45b8 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:24:50 -0800 Subject: [PATCH 01/27] add "open in new tab" button to decklist confirmation dialogue (#5183) * refactor to use confirmOpen * implement extra button in confirmation * use brackets in one-liner if statements * refactor save confirmation window into function --- .../src/client/tabs/tab_deck_editor.cpp | 91 ++++++++++++++++--- cockatrice/src/client/tabs/tab_deck_editor.h | 14 +++ 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index 0c9edbb0b..1e2284186 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -748,9 +748,7 @@ bool TabDeckEditor::confirmClose() { if (modified) { tabSupervisor->setCurrentWidget(this); - QMessageBox::StandardButton ret = QMessageBox::warning( - this, tr("Are you sure?"), tr("The decklist has been modified.\nDo you want to save the changes?"), - QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + int ret = createSaveConfirmationWindow()->exec(); if (ret == QMessageBox::Save) return actSaveDeck(); else if (ret == QMessageBox::Cancel) @@ -767,13 +765,16 @@ void TabDeckEditor::closeRequest() void TabDeckEditor::actNewDeck() { - if (SettingsCache::instance().getOpenDeckInNewTab()) { - emit openDeckEditor(nullptr); + auto deckOpenLocation = confirmOpen(false); + + if (deckOpenLocation == CANCELLED) { return; } - if (!confirmClose()) + if (deckOpenLocation == NEW_TAB) { + emit openDeckEditor(nullptr); return; + } deckModel->cleanList(); nameEdit->setText(QString()); @@ -785,10 +786,11 @@ void TabDeckEditor::actNewDeck() void TabDeckEditor::actLoadDeck() { - bool openInNewTab = SettingsCache::instance().getOpenDeckInNewTab() && !isBlankNewDeck(); + auto deckOpenLocation = confirmOpen(); - if (!openInNewTab && !confirmClose()) + if (deckOpenLocation == CANCELLED) { return; + } QFileDialog dialog(this, tr("Load deck")); dialog.setDirectory(SettingsCache::instance().getDeckPath()); @@ -801,7 +803,7 @@ void TabDeckEditor::actLoadDeck() auto *l = new DeckLoader; if (l->loadFromFile(fileName, fmt)) { - if (openInNewTab) { + if (deckOpenLocation == NEW_TAB) { emit openDeckEditor(l); } else { setSaveStatus(false); @@ -878,16 +880,17 @@ bool TabDeckEditor::actSaveDeckAs() void TabDeckEditor::actLoadDeckFromClipboard() { - bool openInNewTab = SettingsCache::instance().getOpenDeckInNewTab() && !isBlankNewDeck(); + auto deckOpenLocation = confirmOpen(); - if (!openInNewTab && !confirmClose()) + if (deckOpenLocation == CANCELLED) { return; + } DlgLoadDeckFromClipboard dlg(this); if (!dlg.exec()) return; - if (openInNewTab) { + if (deckOpenLocation == NEW_TAB) { emit openDeckEditor(dlg.getDeckList()); } else { setDeck(dlg.getDeckList()); @@ -987,6 +990,70 @@ void TabDeckEditor::recursiveExpand(const QModelIndex &index) deckView->expand(index); } +/** + * @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into + * account the `openDeckInNewTab` settting. + * + * @param openInSameTabIfBlank Open the deck in the same tab instead of a new tab if the current tab is completely + * blank. Only relevant when the `openDeckInNewTab` setting is enabled. + * + * @returns An enum that indicates if and where to load the deck + */ +TabDeckEditor::DeckOpenLocation TabDeckEditor::confirmOpen(const bool openInSameTabIfBlank) +{ + // handle `openDeckInNewTab` setting + if (SettingsCache::instance().getOpenDeckInNewTab()) { + if (openInSameTabIfBlank && isBlankNewDeck()) { + return SAME_TAB; + } else { + return NEW_TAB; + } + } + + // early return if deck is unmodified + if (!modified) { + return SAME_TAB; + } + + // do the save confirmation dialogue + tabSupervisor->setCurrentWidget(this); + + QMessageBox *msgBox = createSaveConfirmationWindow(); + QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole); + + int ret = msgBox->exec(); + + // `exec()` returns an opaque value if a non-standard button was clicked. + // Directly check if newTabButton was clicked before switching over the standard buttons. + if (msgBox->clickedButton() == newTabButton) { + return NEW_TAB; + } + + switch (ret) { + case QMessageBox::Save: + return actSaveDeck() ? SAME_TAB : CANCELLED; + case QMessageBox::Discard: + return SAME_TAB; + default: + return CANCELLED; + } +} + +/** + * @brief Creates the base save confirmation dialogue box. + * + * @returns A QMessageBox that can be further modified + */ +QMessageBox *TabDeckEditor::createSaveConfirmationWindow() +{ + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setIcon(QMessageBox::Warning); + msgBox->setWindowTitle(tr("Are you sure?")); + msgBox->setText(tr("The decklist has been modified.\nDo you want to save the changes?")); + msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + return msgBox; +} + /** * @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action. */ diff --git a/cockatrice/src/client/tabs/tab_deck_editor.h b/cockatrice/src/client/tabs/tab_deck_editor.h index b8c45c110..6aff1cc31 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.h +++ b/cockatrice/src/client/tabs/tab_deck_editor.h @@ -22,6 +22,7 @@ class Response; class FilterTreeModel; class FilterBuilder; class QGroupBox; +class QMessageBox; class QHBoxLayout; class QVBoxLayout; class QPushButton; @@ -100,6 +101,19 @@ private slots: void showSearchSyntaxHelp(); private: + /** + * @brief Which tab to open the new deck in + */ + enum DeckOpenLocation + { + CANCELLED, + SAME_TAB, + NEW_TAB + }; + + DeckOpenLocation confirmOpen(const bool openInSameTabIfBlank = true); + QMessageBox *createSaveConfirmationWindow(); + bool isBlankNewDeck() const; CardInfoPtr currentCardInfo() const; void addCardHelper(QString zoneName); From 83409c32c462d60260596731a5ebb452041a1d53 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sat, 23 Nov 2024 04:21:26 +0100 Subject: [PATCH 02/27] Cache redirects properly by implementing our own QSettings cache for urls. (#5186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cache redirects properly by implementing our own QSettings cache for urls. * Load and store redirects properly. * Set the maximum network cache size from settings value on PictureLoaderWorker instantiation. * Address comments. * Lint. * Adjust debug statements to be in line with existing ones. * Minor Tweaks * Make redirect cache ttl a user-adjustable setting. * Fix Build * Minor Cleanup * Minor Cleanup * Build Fix --------- Co-authored-by: Lukas Brübach Co-authored-by: ZeldaZach --- cockatrice/src/client/ui/picture_loader.cpp | 110 ++++++++++++++++++-- cockatrice/src/client/ui/picture_loader.h | 15 +++ cockatrice/src/dialogs/dlg_settings.cpp | 26 ++++- cockatrice/src/dialogs/dlg_settings.h | 2 + cockatrice/src/settings/cache_settings.cpp | 9 ++ cockatrice/src/settings/cache_settings.h | 26 ++++- dbconverter/src/mocks.cpp | 3 + tests/carddatabase/mocks.cpp | 3 + 8 files changed, 178 insertions(+), 16 deletions(-) diff --git a/cockatrice/src/client/ui/picture_loader.cpp b/cockatrice/src/client/ui/picture_loader.cpp index ad18a9071..a28a98eac 100644 --- a/cockatrice/src/client/ui/picture_loader.cpp +++ b/cockatrice/src/client/ui/picture_loader.cpp @@ -1,17 +1,14 @@ #include "picture_loader.h" -#include "../../game/cards/card_database.h" #include "../../game/cards/card_database_manager.h" #include "../../settings/cache_settings.h" -#include "theme_manager.h" #include #include #include #include -#include #include -#include +#include #include #include #include @@ -23,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -136,6 +132,8 @@ PictureLoaderWorker::PictureLoaderWorker() #endif auto cache = new QNetworkDiskCache(this); cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath()); + cache->setMaximumCacheSize(1024L * 1024L * + static_cast(SettingsCache::instance().getNetworkCacheSizeInMB())); // Note: the settings is in MB, but QNetworkDiskCache uses bytes connect(&SettingsCache::instance(), &SettingsCache::networkCacheSizeChanged, cache, [cache](int newSizeInMB) { cache->setMaximumCacheSize(1024L * 1024L * static_cast(newSizeInMB)); }); @@ -145,6 +143,13 @@ PictureLoaderWorker::PictureLoaderWorker() networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *))); + cacheFilePath = SettingsCache::instance().getRedirectCachePath() + REDIRECT_CACHE_FILENAME; + loadRedirectCache(); + cleanStaleEntries(); + + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, + &PictureLoaderWorker::saveRedirectCache); + pictureLoaderThread = new QThread; pictureLoaderThread->start(QThread::LowPriority); moveToThread(pictureLoaderThread); @@ -447,11 +452,104 @@ bool PictureLoaderWorker::imageIsBlackListed(const QByteArray &picData) QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url) { + // Check if the redirect is cached + QUrl cachedRedirect = getCachedRedirect(url); + if (!cachedRedirect.isEmpty()) { + qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName() + << " set: " << cardBeingDownloaded.getSetName() << "]: Using cached redirect for " + << url.toDisplayString() << " to " << cachedRedirect.toDisplayString(); + return makeRequest(cachedRedirect); // Use the cached redirect + } + QNetworkRequest req(url); + if (!picDownload) { req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); } - return networkManager->get(req); + + QNetworkReply *reply = networkManager->get(req); + + connect(reply, &QNetworkReply::finished, this, [this, reply, url]() { + QVariant redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + + if (redirectTarget.isValid()) { + QUrl redirectUrl = redirectTarget.toUrl(); + if (redirectUrl.isRelative()) { + redirectUrl = url.resolved(redirectUrl); + } + + cacheRedirect(url, redirectUrl); + qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName() + << " set: " << cardBeingDownloaded.getSetName() << "]: Caching redirect from " + << url.toDisplayString() << " to " << redirectUrl.toDisplayString(); + } + + reply->deleteLater(); + }); + + return reply; +} + +void PictureLoaderWorker::cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl) +{ + redirectCache[originalUrl] = qMakePair(redirectUrl, QDateTime::currentDateTimeUtc()); + saveRedirectCache(); +} + +QUrl PictureLoaderWorker::getCachedRedirect(const QUrl &originalUrl) const +{ + if (redirectCache.contains(originalUrl)) { + return redirectCache[originalUrl].first; + } + return {}; +} + +void PictureLoaderWorker::loadRedirectCache() +{ + QSettings settings(cacheFilePath, QSettings::IniFormat); + + redirectCache.clear(); + int size = settings.beginReadArray(REDIRECT_HEADER_NAME); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + QUrl originalUrl = settings.value(REDIRECT_ORIGINAL_URL).toUrl(); + QUrl redirectUrl = settings.value(REDIRECT_URL).toUrl(); + QDateTime timestamp = settings.value(REDIRECT_TIMESTAMP).toDateTime(); + + if (originalUrl.isValid() && redirectUrl.isValid()) { + redirectCache[originalUrl] = qMakePair(redirectUrl, timestamp); + } + } + settings.endArray(); +} + +void PictureLoaderWorker::saveRedirectCache() const +{ + QSettings settings(cacheFilePath, QSettings::IniFormat); + + settings.beginWriteArray(REDIRECT_HEADER_NAME, static_cast(redirectCache.size())); + int index = 0; + for (auto it = redirectCache.cbegin(); it != redirectCache.cend(); ++it) { + settings.setArrayIndex(index++); + settings.setValue(REDIRECT_ORIGINAL_URL, it.key()); + settings.setValue(REDIRECT_URL, it.value().first); + settings.setValue(REDIRECT_TIMESTAMP, it.value().second); + } + settings.endArray(); +} + +void PictureLoaderWorker::cleanStaleEntries() +{ + QDateTime now = QDateTime::currentDateTimeUtc(); + + auto it = redirectCache.begin(); + while (it != redirectCache.end()) { + if (it.value().second.addDays(SettingsCache::instance().getRedirectCacheTtl()) < now) { + it = redirectCache.erase(it); // Remove stale entry + } else { + ++it; + } + } } void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply) diff --git a/cockatrice/src/client/ui/picture_loader.h b/cockatrice/src/client/ui/picture_loader.h index 1c840eb40..245114358 100644 --- a/cockatrice/src/client/ui/picture_loader.h +++ b/cockatrice/src/client/ui/picture_loader.h @@ -11,6 +11,12 @@ class QNetworkAccessManager; class QNetworkReply; class QThread; +#define REDIRECT_HEADER_NAME "redirects" +#define REDIRECT_ORIGINAL_URL "original" +#define REDIRECT_URL "redirect" +#define REDIRECT_TIMESTAMP "timestamp" +#define REDIRECT_CACHE_FILENAME "cache.ini" + class PictureToLoad { private: @@ -83,6 +89,9 @@ private: QList loadQueue; QMutex mutex; QNetworkAccessManager *networkManager; + QHash> redirectCache; // Stores redirect and timestamp + QString cacheFilePath; // Path to persistent storage + static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable QList cardsToDownload; PictureToLoad cardBeingLoaded; PictureToLoad cardBeingDownloaded; @@ -91,6 +100,12 @@ private: bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName); bool imageIsBlackListed(const QByteArray &); QNetworkReply *makeRequest(const QUrl &url); + void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl); + QUrl getCachedRedirect(const QUrl &originalUrl) const; + void loadRedirectCache(); + void saveRedirectCache() const; + void cleanStaleEntries(); + private slots: void picDownloadFinished(QNetworkReply *reply); void picDownloadFailed(); diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index efdff930a..cef4ceaee 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -629,11 +629,22 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB()); networkCacheEdit.setSuffix(" MB"); + networkRedirectCacheTtlEdit.setMinimum(NETWORK_REDIRECT_CACHE_TTL_MIN); + networkRedirectCacheTtlEdit.setMaximum(NETWORK_REDIRECT_CACHE_TTL_MAX); + networkRedirectCacheTtlEdit.setSingleStep(1); + networkRedirectCacheTtlEdit.setValue(SettingsCache::instance().getRedirectCacheTtl()); + networkRedirectCacheTtlEdit.setSuffix(" " + tr("Day(s)")); + auto networkCacheLayout = new QHBoxLayout; networkCacheLayout->addStretch(); networkCacheLayout->addWidget(&networkCacheLabel); networkCacheLayout->addWidget(&networkCacheEdit); + auto networkRedirectCacheLayout = new QHBoxLayout; + networkRedirectCacheLayout->addStretch(); + networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlLabel); + networkRedirectCacheLayout->addWidget(&networkRedirectCacheTtlEdit); + auto pixmapCacheLayout = new QHBoxLayout; pixmapCacheLayout->addStretch(); pixmapCacheLayout->addWidget(&pixmapCacheLabel); @@ -645,8 +656,9 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() lpGeneralGrid->addLayout(messageListLayout, 1, 0, 1, 2); lpGeneralGrid->addLayout(networkCacheLayout, 2, 0, 1, 2); lpGeneralGrid->addLayout(pixmapCacheLayout, 3, 0, 1, 2); - lpGeneralGrid->addWidget(&urlLinkLabel, 4, 0); - lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 4, 1); + lpGeneralGrid->addLayout(networkRedirectCacheLayout, 4, 0, 1, 2); + lpGeneralGrid->addWidget(&urlLinkLabel, 5, 0); + lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 5, 1); // Spoiler Layout lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0); @@ -657,13 +669,15 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() lpSpoilerGrid->addWidget(updateNowButton, 2, 1); lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 3, 0, 1, 3, Qt::AlignTop); - // On a change to the check box, hide/unhide the other fields + // On a change to the checkbox, hide/un-hide the other fields connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), &SettingsCache::instance(), SLOT(setDownloadSpoilerStatus(bool))); connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), this, SLOT(setSpoilersEnabled(bool))); connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(), SLOT(setPixmapCacheSize(int))); connect(&networkCacheEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(), SLOT(setNetworkCacheSizeInMB(int))); + connect(&networkRedirectCacheTtlEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(), + SLOT(setNetworkRedirectCacheTtl(int))); mpGeneralGroupBox = new QGroupBox; mpGeneralGroupBox->setLayout(lpGeneralGrid); @@ -844,9 +858,11 @@ void DeckEditorSettingsPage::retranslateUi() urlLinkLabel.setText(QString("%2").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL"))); clearDownloadedPicsButton.setText(tr("Delete Downloaded Images")); resetDownloadURLs.setText(tr("Reset Download URLs")); - networkCacheLabel.setText(tr("Downloaded images directory size:")); + networkCacheLabel.setText(tr("Network Cache Size:")); networkCacheEdit.setToolTip(tr("On-disk cache for downloaded pictures")); - pixmapCacheLabel.setText(tr("Picture cache size:")); + networkRedirectCacheTtlLabel.setText(tr("Redirect Cache TTL:")); + networkRedirectCacheTtlEdit.setToolTip(tr("How long cached redirects for urls are valid for.")); + pixmapCacheLabel.setText(tr("Picture Cache Size:")); pixmapCacheEdit.setToolTip(tr("In-memory cache for pictures not currently on screen")); } diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index 70915cc0e..09c5ded13 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -174,6 +174,8 @@ private: QPushButton *updateNowButton; QLabel networkCacheLabel; QSpinBox networkCacheEdit; + QLabel networkRedirectCacheTtlLabel; + QSpinBox networkRedirectCacheTtlEdit; QSpinBox pixmapCacheEdit; QLabel pixmapCacheLabel; }; diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index 05de9fd48..da0e5c0b0 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -221,6 +221,7 @@ SettingsCache::SettingsCache() pixmapCacheSize = PIXMAPCACHE_SIZE_DEFAULT; networkCacheSize = settings->value("personal/networkCacheSize", NETWORK_CACHE_SIZE_DEFAULT).toInt(); + redirectCacheTtl = settings->value("personal/redirectCacheTtl", NETWORK_REDIRECT_CACHE_TTL_DEFAULT).toInt(); picDownload = settings->value("personal/picturedownload", true).toBool(); @@ -657,6 +658,13 @@ void SettingsCache::setNetworkCacheSizeInMB(const int _networkCacheSize) emit networkCacheSizeChanged(networkCacheSize); } +void SettingsCache::setNetworkRedirectCacheTtl(const int _redirectCacheTtl) +{ + redirectCacheTtl = _redirectCacheTtl; + settings->setValue("personal/redirectCacheSize", redirectCacheTtl); + emit redirectCacheTtlChanged(redirectCacheTtl); +} + void SettingsCache::setClientID(const QString &_clientID) { clientID = _clientID; @@ -1030,6 +1038,7 @@ void SettingsCache::loadPaths() replaysPath = getSafeConfigPath("paths/replays", dataPath + "/replays/"); themesPath = getSafeConfigPath("paths/themes", dataPath + "/themes/"); picsPath = getSafeConfigPath("paths/pics", dataPath + "/pics/"); + redirectCachePath = getSafeConfigPath("paths/redirects", getCachePath() + "/redirects/"); // this has never been exposed as an user-configurable setting if (picsPath.endsWith("/")) { customPicsPath = getSafeConfigPath("paths/custompics", picsPath + "CUSTOM/"); diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index e0bf8b9ed..fdfdb6f53 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -16,16 +16,21 @@ class ReleaseChannel; -// size should be a multiple of 64 -#define PIXMAPCACHE_SIZE_DEFAULT 2047 +// In MB (Increments of 64) +#define PIXMAPCACHE_SIZE_DEFAULT 2048 #define PIXMAPCACHE_SIZE_MIN 64 -#define PIXMAPCACHE_SIZE_MAX 2047 +#define PIXMAPCACHE_SIZE_MAX 4096 // In MB constexpr int NETWORK_CACHE_SIZE_DEFAULT = 1024 * 4; // 4 GB constexpr int NETWORK_CACHE_SIZE_MIN = 1; // 1 MB constexpr int NETWORK_CACHE_SIZE_MAX = 1024 * 1024; // 1 TB +// In Days +#define NETWORK_REDIRECT_CACHE_TTL_DEFAULT 30 +#define NETWORK_REDIRECT_CACHE_TTL_MIN 1 +#define NETWORK_REDIRECT_CACHE_TTL_MAX 90 + #define DEFAULT_LANG_NAME "English" #define CLIENT_INFO_NOT_SET "notset" @@ -53,6 +58,7 @@ signals: void ignoreUnregisteredUserMessagesChanged(); void pixmapCacheSizeChanged(int newSizeInMBs); void networkCacheSizeChanged(int newSizeInMBs); + void redirectCacheTtlChanged(int newTtl); void masterVolumeChanged(int value); void chatMentionCompleterChanged(); void downloadSpoilerTimeIndexChanged(); @@ -73,8 +79,8 @@ private: QByteArray tokenDialogGeometry; QByteArray setsDialogGeometry; QString lang; - QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath, themesPath, - spoilerDatabasePath, tokenDatabasePath, themeName; + QString deckPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath, + customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName; bool notifyAboutUpdates; bool notifyAboutNewVersion; bool showTipsOnStartup; @@ -116,6 +122,7 @@ private: bool useTearOffMenus; int pixmapCacheSize; int networkCacheSize; + int redirectCacheTtl; bool scaleCards; int verticalCardOverlapPercent; bool showMessagePopups; @@ -183,6 +190,10 @@ public: { return picsPath; } + QString getRedirectCachePath() const + { + return redirectCachePath; + } QString getCustomPicsPath() const { return customPicsPath; @@ -356,6 +367,10 @@ public: { return networkCacheSize; } + int getRedirectCacheTtl() const + { + return redirectCacheTtl; + } bool getScaleCards() const { return scaleCards; @@ -557,6 +572,7 @@ public slots: void setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T _ignoreUnregisteredUserMessages); void setPixmapCacheSize(const int _pixmapCacheSize); void setNetworkCacheSizeInMB(const int _networkCacheSize); + void setNetworkRedirectCacheTtl(const int _redirectCacheTtl); void setCardScaling(const QT_STATE_CHANGED_T _scaleCards); void setStackCardOverlapPercent(const int _verticalCardOverlapPercent); void setShowMessagePopups(const QT_STATE_CHANGED_T _showMessagePopups); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index 37c1424dc..1c90612fa 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -220,6 +220,9 @@ void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */) void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */) { } +void SettingsCache::setNetworkRedirectCacheTtl(const int /* _redirectCacheTtl */) +{ +} void SettingsCache::setClientID(const QString & /* _clientID */) { } diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index 112fc9416..f311241de 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -224,6 +224,9 @@ void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */) void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */) { } +void SettingsCache::setNetworkRedirectCacheTtl(const int /* _redirectCacheTtl */) +{ +} void SettingsCache::setClientID(const QString & /* _clientID */) { } From bd60a9fd2ebae290c434691bcd3bc71c82061fc5 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 22 Nov 2024 19:21:54 -0800 Subject: [PATCH 03/27] don't blink highlighted phase when backwards skipping in replays (#5185) --- cockatrice/src/client/tabs/tab_game.cpp | 3 +-- cockatrice/src/client/ui/phases_toolbar.cpp | 27 --------------------- cockatrice/src/client/ui/phases_toolbar.h | 2 -- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index 68c60ff15..a37e05567 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -648,8 +648,7 @@ void TabGame::replayRewind() messageLog->clearChat(); // reset phase markers - currentPhase = -1; - phasesToolbar->reset(); + setActivePhase(-1); } void TabGame::incrementGameTime() diff --git a/cockatrice/src/client/ui/phases_toolbar.cpp b/cockatrice/src/client/ui/phases_toolbar.cpp index f43d9f00d..036a7c19e 100644 --- a/cockatrice/src/client/ui/phases_toolbar.cpp +++ b/cockatrice/src/client/ui/phases_toolbar.cpp @@ -86,23 +86,6 @@ void PhaseButton::updateAnimation() update(); } -/** - * @brief Immediately resets the button to the inactive state, without going through the animation. - */ -void PhaseButton::reset() -{ - activeAnimationTimer->stop(); - active = false; - - if (highlightable) { - activeAnimationCounter = 0; - } else { - activeAnimationCounter = 9; - } - - update(); -} - void PhaseButton::mousePressEvent(QGraphicsSceneMouseEvent * /*event*/) { emit clicked(); @@ -268,16 +251,6 @@ void PhasesToolbar::phaseButtonClicked() emit sendGameCommand(cmd, -1); } -/** - * @brief Immediately resets the toolbar to its initial state, with all phases inactive. - */ -void PhasesToolbar::reset() -{ - for (auto &i : buttonList) { - i->reset(); - } -} - void PhasesToolbar::actNextTurn() { emit sendGameCommand(Command_NextTurn(), -1); diff --git a/cockatrice/src/client/ui/phases_toolbar.h b/cockatrice/src/client/ui/phases_toolbar.h index 1595c65d5..3567e741b 100644 --- a/cockatrice/src/client/ui/phases_toolbar.h +++ b/cockatrice/src/client/ui/phases_toolbar.h @@ -45,7 +45,6 @@ public: { return active; } - void reset(); void triggerDoubleClickAction(); signals: void clicked(); @@ -86,7 +85,6 @@ public: public slots: void setActivePhase(int phase); void triggerPhaseAction(int phase); - void reset(); private slots: void phaseButtonClicked(); void actNextTurn(); From 50274cb66d76e7c38ac9bdcd79fa01fb34cd1839 Mon Sep 17 00:00:00 2001 From: Zach H Date: Fri, 22 Nov 2024 22:22:22 -0500 Subject: [PATCH 04/27] Change 'custom(VER)' to 'custom-VER' because Fedora mad (#5180) --- cmake/getversion.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/getversion.cmake b/cmake/getversion.cmake index 5bec3548d..cb27ebd87 100644 --- a/cmake/getversion.cmake +++ b/cmake/getversion.cmake @@ -19,7 +19,7 @@ function(get_commit_id) PARENT_SCOPE ) set(PROJECT_VERSION_LABEL - "custom(${GIT_COM_ID})" + "custom-${GIT_COM_ID}" PARENT_SCOPE ) endfunction() From 39d8ca050f2058681f72ddc172676fef02391672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:22:36 -0500 Subject: [PATCH 05/27] Bump cross-spawn from 7.0.3 to 7.0.6 in /webclient (#5181) Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6. - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webclient/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/webclient/package-lock.json b/webclient/package-lock.json index c0305729d..2dc6ee7f1 100644 --- a/webclient/package-lock.json +++ b/webclient/package-lock.json @@ -7006,9 +7006,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -25082,9 +25082,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", From 7b1653034bec04f4e9bc504cb8e96ec4b47dff23 Mon Sep 17 00:00:00 2001 From: Zach H Date: Fri, 22 Nov 2024 22:52:42 -0500 Subject: [PATCH 06/27] Bump macos14 XCode to 15.4 (#5188) --- .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 1f4ee4e67..99c05deee 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -212,7 +212,7 @@ jobs: - target: 14 soc: Apple os: macos-14 - xcode: "14.3.1" + xcode: "15.4" type: Release core_count: 3 make_package: 1 @@ -220,7 +220,7 @@ jobs: - target: 14 soc: Apple os: macos-14 - xcode: "14.3.1" + xcode: "15.4" type: Debug core_count: 3 From 27055944dfe4463bb9405a4dc8919f65bb67f819 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 23 Nov 2024 07:40:37 -0800 Subject: [PATCH 07/27] skip tap animation when rewinding (#5168) --- .../client/network/replay_timeline_widget.cpp | 4 ++++ cockatrice/src/game/player/player.cpp | 18 +++++++++++------- cockatrice/src/game/player/player.h | 9 ++++++--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cockatrice/src/client/network/replay_timeline_widget.cpp b/cockatrice/src/client/network/replay_timeline_widget.cpp index 83b1abda0..deb77d623 100644 --- a/cockatrice/src/client/network/replay_timeline_widget.cpp +++ b/cockatrice/src/client/network/replay_timeline_widget.cpp @@ -162,6 +162,10 @@ void ReplayTimelineWidget::processNewEvents(PlaybackMode playbackMode) if (playbackMode == BACKWARD_SKIP || currentTime - replayTimeline[currentEvent] > BIG_SKIP_MS) options |= Player::EventProcessingOption::SKIP_REVEAL_WINDOW; + // backwards skip => always skip tap animation + if (playbackMode == BACKWARD_SKIP) + options |= Player::EventProcessingOption::SKIP_TAP_ANIMATION; + emit processNextEvent(options); ++currentEvent; } diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index dd98305ff..71402fc61 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -1893,7 +1893,8 @@ void Player::setCardAttrHelper(const GameEventContext &context, CardItem *card, CardAttribute attribute, const QString &avalue, - bool allCards) + bool allCards, + EventProcessingOptions options) { if (card == nullptr) { return; @@ -1907,7 +1908,8 @@ void Player::setCardAttrHelper(const GameEventContext &context, if (!allCards) { emit logSetTapped(this, card, tapped); } - card->setTapped(tapped, !moveCardContext); + bool canAnimate = !options.testFlag(SKIP_TAP_ANIMATION) && !moveCardContext; + card->setTapped(tapped, canAnimate); } break; } @@ -2049,7 +2051,9 @@ void Player::eventCreateToken(const Event_CreateToken &event) zone->addCard(card, true, event.x(), event.y()); } -void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context) +void Player::eventSetCardAttr(const Event_SetCardAttr &event, + const GameEventContext &context, + EventProcessingOptions options) { CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0); if (!zone) { @@ -2059,8 +2063,8 @@ void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventCon if (!event.has_card_id()) { const CardList &cards = zone->getCards(); for (int i = 0; i < cards.size(); ++i) { - setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()), - true); + setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()), true, + options); } if (event.attribute() == AttrTapped) { emit logSetTapped(this, nullptr, event.attr_value() == "1"); @@ -2071,7 +2075,7 @@ void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventCon qWarning() << "Player::eventSetCardAttr: card id=" << event.card_id() << "not found"; return; } - setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false); + setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false, options); } } @@ -2444,7 +2448,7 @@ void Player::processGameEvent(GameEvent::GameEventType type, eventCreateToken(event.GetExtension(Event_CreateToken::ext)); break; case GameEvent::SET_CARD_ATTR: - eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context); + eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context, options); break; case GameEvent::SET_CARD_COUNTER: eventSetCardCounter(event.GetExtension(Event_SetCardCounter::ext)); diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index 832e613b7..5aecaa932 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -229,7 +229,8 @@ private slots: public: enum EventProcessingOption { - SKIP_REVEAL_WINDOW = 0x0001 + SKIP_REVEAL_WINDOW = 0x0001, + SKIP_TAP_ANIMATION = 0x0002 }; Q_DECLARE_FLAGS(EventProcessingOptions, EventProcessingOption) @@ -301,7 +302,8 @@ private: CardItem *card, CardAttribute attribute, const QString &avalue, - bool allCards); + bool allCards, + EventProcessingOptions options); void addRelatedCardActions(const CardItem *card, QMenu *cardMenu); void addRelatedCardView(const CardItem *card, QMenu *cardMenu); void createCard(const CardItem *sourceCard, @@ -328,7 +330,8 @@ private: void eventCreateArrow(const Event_CreateArrow &event); void eventDeleteArrow(const Event_DeleteArrow &event); void eventCreateToken(const Event_CreateToken &event); - void eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context); + void + eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context, EventProcessingOptions options); void eventSetCardCounter(const Event_SetCardCounter &event); void eventCreateCounter(const Event_CreateCounter &event); void eventSetCounter(const Event_SetCounter &event); From a3f0807d474da18c0db84380a82161bf149d7394 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 24 Nov 2024 06:52:56 -0800 Subject: [PATCH 08/27] fix error message (#5192) `QObject::connect: No such slot UserInterfaceSettingsPage::setNotificationEnabled(Qt::CheckState) in /Users/Ricky/GitHub/Cockatrice/cockatrice/src/dialogs/dlg_settings.cpp:448` --- cockatrice/src/dialogs/dlg_settings.cpp | 2 +- cockatrice/src/dialogs/dlg_settings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index cef4ceaee..de354bae5 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -524,7 +524,7 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() setLayout(mainLayout); } -void UserInterfaceSettingsPage::setNotificationEnabled(int i) +void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i) { specNotificationsEnabledCheckBox.setEnabled(i != 0); buddyConnectNotificationsEnabledCheckBox.setEnabled(i != 0); diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index 09c5ded13..fdd114274 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -113,7 +113,7 @@ class UserInterfaceSettingsPage : public AbstractSettingsPage { Q_OBJECT private slots: - void setNotificationEnabled(int); + void setNotificationEnabled(QT_STATE_CHANGED_T); private: QCheckBox notificationsEnabledCheckBox; From c51b54c0c51d4db9ff72f0974fc4b9e42bd14f38 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:27:21 -0800 Subject: [PATCH 09/27] rename variables for url list layout (#5195) --- cockatrice/src/dialogs/dlg_settings.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index de354bae5..236a7eeb5 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -604,16 +604,16 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() aRemove->setToolTip(tr("Remove URL")); connect(aRemove, SIGNAL(triggered()), this, SLOT(actRemoveURL())); - auto *messageToolBar = new QToolBar; - messageToolBar->setOrientation(Qt::Vertical); - messageToolBar->addAction(aAdd); - messageToolBar->addAction(aRemove); - messageToolBar->addAction(aEdit); - messageToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + auto *urlToolBar = new QToolBar; + urlToolBar->setOrientation(Qt::Vertical); + urlToolBar->addAction(aAdd); + urlToolBar->addAction(aRemove); + urlToolBar->addAction(aEdit); + urlToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - auto *messageListLayout = new QHBoxLayout; - messageListLayout->addWidget(messageToolBar); - messageListLayout->addWidget(urlList); + auto *urlListLayout = new QHBoxLayout; + urlListLayout->addWidget(urlToolBar); + urlListLayout->addWidget(urlList); // pixmap cache pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN); @@ -653,7 +653,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() // Top Layout lpGeneralGrid->addWidget(&picDownloadCheckBox, 0, 0); lpGeneralGrid->addWidget(&resetDownloadURLs, 0, 1); - lpGeneralGrid->addLayout(messageListLayout, 1, 0, 1, 2); + lpGeneralGrid->addLayout(urlListLayout, 1, 0, 1, 2); lpGeneralGrid->addLayout(networkCacheLayout, 2, 0, 1, 2); lpGeneralGrid->addLayout(pixmapCacheLayout, 3, 0, 1, 2); lpGeneralGrid->addLayout(networkRedirectCacheLayout, 4, 0, 1, 2); From 3255ed3ffb06e4179128c37e01f58205943d7f6d Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:43:08 -0800 Subject: [PATCH 10/27] add menu option to reload card db (#5196) --- cockatrice/src/client/ui/window_main.cpp | 10 ++++++++++ cockatrice/src/client/ui/window_main.h | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/client/ui/window_main.cpp b/cockatrice/src/client/ui/window_main.cpp index 4de3b3cb3..121f61285 100644 --- a/cockatrice/src/client/ui/window_main.cpp +++ b/cockatrice/src/client/ui/window_main.cpp @@ -666,6 +666,7 @@ void MainWindow::retranslateUi() aOpenCustomFolder->setText(tr("Open custom image folder")); aOpenCustomsetsFolder->setText(tr("Open custom sets folder")); aAddCustomSet->setText(tr("Add custom sets/cards")); + aReloadCardDatabase->setText(tr("Reload card database")); helpMenu->setTitle(tr("&Help")); aAbout->setText(tr("&About Cockatrice")); @@ -714,6 +715,8 @@ void MainWindow::createActions() connect(aOpenCustomsetsFolder, SIGNAL(triggered()), this, SLOT(actOpenCustomsetsFolder())); aAddCustomSet = new QAction(QString(), this); connect(aAddCustomSet, SIGNAL(triggered()), this, SLOT(actAddCustomSet())); + aReloadCardDatabase = new QAction(QString(), this); + connect(aReloadCardDatabase, SIGNAL(triggered()), this, SLOT(actReloadCardDatabase())); aAbout = new QAction(this); connect(aAbout, SIGNAL(triggered()), this, SLOT(actAbout())); @@ -788,6 +791,8 @@ void MainWindow::createMenus() dbMenu->addAction(aOpenCustomFolder); dbMenu->addAction(aOpenCustomsetsFolder); dbMenu->addAction(aAddCustomSet); + dbMenu->addSeparator(); + dbMenu->addAction(aReloadCardDatabase); helpMenu = menuBar()->addMenu(QString()); helpMenu->addAction(aAbout); @@ -1324,6 +1329,11 @@ int MainWindow::getNextCustomSetPrefix(QDir dataDir) return maxIndex + 1; } +void MainWindow::actReloadCardDatabase() +{ + const auto reloadOk1 = QtConcurrent::run([] { CardDatabaseManager::getInstance()->loadCardDatabases(); }); +} + void MainWindow::actManageSets() { wndSets = new WndSets(this); diff --git a/cockatrice/src/client/ui/window_main.h b/cockatrice/src/client/ui/window_main.h index 1813b3b4a..2b240beb1 100644 --- a/cockatrice/src/client/ui/window_main.h +++ b/cockatrice/src/client/ui/window_main.h @@ -98,6 +98,7 @@ private slots: void actOpenCustomFolder(); void actOpenCustomsetsFolder(); void actAddCustomSet(); + void actReloadCardDatabase(); void actManageSets(); void actEditTokens(); @@ -125,7 +126,7 @@ private: QMenu *cockatriceMenu, *dbMenu, *helpMenu, *trayIconMenu; QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit, *aAbout, *aTips, *aCheckCardUpdates, *aRegister, *aForgotPassword, *aUpdate, *aViewLog, *aManageSets, - *aEditTokens, *aOpenCustomFolder, *aOpenCustomsetsFolder, *aAddCustomSet, *aShow; + *aEditTokens, *aOpenCustomFolder, *aOpenCustomsetsFolder, *aAddCustomSet, *aReloadCardDatabase, *aShow; TabSupervisor *tabSupervisor; WndSets *wndSets; RemoteClient *client; From 5f1c03682f7e6849eb1e6a87363da7a68e9f0bff Mon Sep 17 00:00:00 2001 From: tooomm Date: Tue, 26 Nov 2024 00:05:09 +0100 Subject: [PATCH 11/27] macOS version fix + wording (#5189) --- .ci/release_template.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/.ci/release_template.md b/.ci/release_template.md index 75671f90e..ecd758ebf 100644 --- a/.ci/release_template.md +++ b/.ci/release_template.md @@ -4,13 +4,13 @@ git push -d origin --REPLACE-WITH-BETA-LIST-- --> -
 Pre-compiled binaries we serve:
  - Windows 10+
  - Windows 7+
- - macOS 13+ ("Ventura") / Apple M
+ - macOS 14+ ("Sonoma") / Apple M
  - macOS 13+ ("Ventura") / Intel
  - Ubuntu 24.04 LTS ("Noble Numbat")
  - Ubuntu 22.04 LTS ("Jammy Jellyfish")
@@ -19,8 +19,9 @@ include different targets -->
  - Debian 11 ("Bullseye")
  - Fedora 41
  - Fedora 40
-We are also packaged in Arch Linux's official "extra" repository, courtesy of @FFY00
-General Linux support is available via a flatpak package (Flathub)
+ 
+We are also packaged in Arch Linux's official "extra" repository, courtesy of @FFY00
+General Linux support is available via a flatpak package (Flathub)
 
@@ -28,22 +29,24 @@ include different targets --> We're pleased to announce the newest official release: --REPLACE-WITH-RELEASE-TITLE-- -We hope you enjoy the changes made and we have listed all changes, with their corresponding tickets, since the last version of Cockatrice was released for your convenience. +We hope you enjoy the changes made! All improvements with their corresponding tickets since the last version of Cockatrice are listed in the changelog below. -If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a GitHub Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice)) +If you ever encounter a bug, have a suggestion or idea, or feel a need for a developer to look into something, please feel free to [open a ticket](https://github.com/Cockatrice/Cockatrice/issues). ([How to create a Ticket for Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice)) -For any information relating to Cockatrice, please take a look at our official site: **https://cockatrice.github.io** +For basic information related to the app and getting started, please take a look at our official site: **https://cockatrice.github.io** -If you'd like to help contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-). We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution. +If you'd like to help and contribute to Cockatrice in any way, check out our [README](https://github.com/Cockatrice/Cockatrice#get-involved-). +We're always available to answer questions you may have on how the program works and how you can provide a meaningful contribution. ## Upgrading Cockatrice -- Run the internal software updater: Help → Check for Client Updates +Run the internal software updater: Help → Check for Client Updates Don't forget to update your card database right after! (Help → Check for Card Updates...) @@ -60,14 +63,14 @@ Remove empty headers when done. --> -### ⚠️ Important: +### 🔖 Highlights: ### ✨ New Features: -### 🐛 Fixed Bugs / Resolved issues: +### 🐛 Fixed Bugs / Resolved Issues:
-📘 Show all changes (--REPLACE-WITH-COMMIT-COUNT-- commits) +Show all changes (--REPLACE-WITH-COMMIT-COUNT-- commits) ### User Interface @@ -88,5 +91,6 @@ Remove empty headers when done. ## Special Thanks -We continue to find it amazing that so many people contribute their time, knowledge, code, testing and more to the project. We'd like to thank the entire Cockatrice community for their efforts. +It's amazing that so many people contribute their time, knowledge, code, testing and more to the project. +We'd like to thank the entire Cockatrice community for their efforts! 🙏 From a8471f62bc86d23a7d7cd9b43c28eda7b1450a23 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:12:56 -0800 Subject: [PATCH 12/27] clean up DownloadSettings (#5194) * refactor DownloadSettings * only reset to default on first run * use c++ foreach * use addItems * move default urls to static const --- cockatrice/src/client/ui/window_main.cpp | 1 + cockatrice/src/dialogs/dlg_settings.cpp | 11 +++-- cockatrice/src/settings/download_settings.cpp | 48 ++++--------------- cockatrice/src/settings/download_settings.h | 14 ++---- 4 files changed, 21 insertions(+), 53 deletions(-) diff --git a/cockatrice/src/client/ui/window_main.cpp b/cockatrice/src/client/ui/window_main.cpp index 121f61285..b30f5556d 100644 --- a/cockatrice/src/client/ui/window_main.cpp +++ b/cockatrice/src/client/ui/window_main.cpp @@ -883,6 +883,7 @@ void MainWindow::startupConfigCheck() // no config found, 99% new clean install qDebug() << "Startup: old client version empty, assuming first start after clean install"; alertForcedOracleRun(VERSION_STRING, false); + SettingsCache::instance().downloads().resetToDefaultURLs(); // populate the download urls SettingsCache::instance().setClientVersion(VERSION_STRING); } else if (SettingsCache::instance().getClientVersion() != VERSION_STRING) { // config found, from another (presumably older) version diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 236a7eeb5..9451bb543 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -588,8 +588,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() connect(urlList->model(), SIGNAL(rowsMoved(const QModelIndex, int, int, const QModelIndex, int)), this, SLOT(urlListChanged(const QModelIndex, int, int, const QModelIndex, int))); - for (int i = 0; i < SettingsCache::instance().downloads().getCount(); i++) - urlList->addItem(SettingsCache::instance().downloads().getDownloadUrlAt(i)); + urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); auto aAdd = new QAction(this); aAdd->setIcon(QPixmap("theme:icons/increment")); @@ -694,7 +693,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked() { - SettingsCache::instance().downloads().clear(); + SettingsCache::instance().downloads().resetToDefaultURLs(); urlList->clear(); urlList->addItems(SettingsCache::instance().downloads().getAllURLs()); QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset.")); @@ -774,11 +773,13 @@ void DeckEditorSettingsPage::actEditURL() void DeckEditorSettingsPage::storeSettings() { qInfo() << "URL Priority Reset"; - SettingsCache::instance().downloads().clear(); + + QStringList downloadUrls; for (int i = 0; i < urlList->count(); i++) { qInfo() << "Priority" << i << ":" << urlList->item(i)->text(); - SettingsCache::instance().downloads().setDownloadUrlAt(i, urlList->item(i)->text()); + downloadUrls << urlList->item(i)->text(); } + SettingsCache::instance().downloads().setDownloadUrls(downloadUrls); } void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int) diff --git a/cockatrice/src/settings/download_settings.cpp b/cockatrice/src/settings/download_settings.cpp index 1af9d4f71..adbe26363 100644 --- a/cockatrice/src/settings/download_settings.cpp +++ b/cockatrice/src/settings/download_settings.cpp @@ -2,56 +2,28 @@ #include "settings_manager.h" +const QStringList DownloadSettings::DEFAULT_DOWNLOAD_URLS = { + "https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!", + "https://api.scryfall.com/cards/multiverse/!set:muid!?format=image", + "https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card", + "https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card"}; + DownloadSettings::DownloadSettings(const QString &settingPath, QObject *parent = nullptr) : SettingsManager(settingPath + "downloads.ini", parent) { - downloadURLs = getValue("urls", "downloads").value(); } -void DownloadSettings::setDownloadUrlAt(int index, const QString &url) +void DownloadSettings::setDownloadUrls(const QStringList &downloadURLs) { - downloadURLs.insert(index, url); setValue(QVariant::fromValue(downloadURLs), "urls", "downloads"); } -/** - * If reset or first run, this method contains the default URLs we will populate - */ QStringList DownloadSettings::getAllURLs() { - // First run, these will be empty - if (downloadURLs.count() == 0) { - populateDefaultURLs(); - } - - return downloadURLs; + return getValue("urls", "downloads").toStringList(); } -void DownloadSettings::populateDefaultURLs() +void DownloadSettings::resetToDefaultURLs() { - downloadURLs.clear(); - downloadURLs.append("https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!"); - downloadURLs.append("https://api.scryfall.com/cards/multiverse/!set:muid!?format=image"); - downloadURLs.append("https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card"); - downloadURLs.append("https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card"); - setValue(QVariant::fromValue(downloadURLs), "urls", "downloads"); + setValue(QVariant::fromValue(DEFAULT_DOWNLOAD_URLS), "urls", "downloads"); } - -QString DownloadSettings::getDownloadUrlAt(int index) -{ - if (0 <= index && index < downloadURLs.size()) { - return downloadURLs[index]; - } - - return ""; -} - -int DownloadSettings::getCount() -{ - return downloadURLs.size(); -} - -void DownloadSettings::clear() -{ - downloadURLs.clear(); -} \ No newline at end of file diff --git a/cockatrice/src/settings/download_settings.h b/cockatrice/src/settings/download_settings.h index 5916b0083..a961199b3 100644 --- a/cockatrice/src/settings/download_settings.h +++ b/cockatrice/src/settings/download_settings.h @@ -10,20 +10,14 @@ class DownloadSettings : public SettingsManager Q_OBJECT friend class SettingsCache; + static const QStringList DEFAULT_DOWNLOAD_URLS; + public: explicit DownloadSettings(const QString &, QObject *); QStringList getAllURLs(); - QString getDownloadUrlAt(int); - void setDownloadUrlAt(int, const QString &); - int getCount(); - void clear(); - -private: - QStringList downloadURLs; - -private: - void populateDefaultURLs(); + void setDownloadUrls(const QStringList &downloadURLs); + void resetToDefaultURLs(); }; #endif // COCKATRICE_DOWNLOADSETTINGS_H From 0ca8bdb3a80d3bfbaffbee003cada58fe425213c Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:15:35 -0800 Subject: [PATCH 13/27] refactor CardList (#5197) --- cockatrice/src/game/cards/card_list.cpp | 57 +++++++++---------------- cockatrice/src/game/cards/card_list.h | 5 +-- cockatrice/src/game/zones/card_zone.cpp | 2 +- 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/cockatrice/src/game/cards/card_list.cpp b/cockatrice/src/game/cards/card_list.cpp index e2866e5d8..f35a0d842 100644 --- a/cockatrice/src/game/cards/card_list.cpp +++ b/cockatrice/src/game/cards/card_list.cpp @@ -9,42 +9,31 @@ CardList::CardList(bool _contentsKnown) : QList(), contentsKnown(_co { } -CardItem *CardList::findCard(const int id, const bool remove, int *position) +/** + * @brief Finds the CardItem with the given id in the list. + * If contentsKnown is false, then this just returns the first element of the list. + * + * @param cardId The id of the card to find. + * + * @returns A pointer to the CardItem, or a nullptr if not found. + */ +CardItem *CardList::findCard(const int cardId) const { - if (!contentsKnown) { - if (empty()) - return 0; - CardItem *temp = at(0); - if (remove) - removeAt(0); - if (position) - *position = id; - return temp; - } else - for (int i = 0; i < size(); i++) { - CardItem *temp = at(i); - if (temp->getId() == id) { - if (remove) - removeAt(i); - if (position) - *position = i; - return temp; + if (!contentsKnown && !empty()) { + return at(0); + } else { + for (auto *cardItem : *this) { + if (cardItem->getId() == cardId) { + return cardItem; } } - return 0; + } + return nullptr; } -class CardList::compareFunctor +void CardList::sort(int flags) { -private: - int flags; - -public: - explicit compareFunctor(int _flags) : flags(_flags) - { - } - inline bool operator()(CardItem *a, CardItem *b) const - { + auto comparator = [flags](CardItem *a, CardItem *b) { if (flags & SortByType) { QString t1 = a->getInfo() ? a->getInfo()->getMainCardType() : QString(); QString t2 = b->getInfo() ? b->getInfo()->getMainCardType() : QString(); @@ -53,11 +42,7 @@ public: return t1 < t2; } else return a->getName() < b->getName(); - } -}; + }; -void CardList::sort(int flags) -{ - compareFunctor cf(flags); - std::sort(begin(), end(), cf); + std::sort(begin(), end(), comparator); } diff --git a/cockatrice/src/game/cards/card_list.h b/cockatrice/src/game/cards/card_list.h index 1835ca96e..d48e4c3d2 100644 --- a/cockatrice/src/game/cards/card_list.h +++ b/cockatrice/src/game/cards/card_list.h @@ -7,9 +7,6 @@ class CardItem; class CardList : public QList { -private: - class compareFunctor; - protected: bool contentsKnown; @@ -20,7 +17,7 @@ public: SortByType = 2 }; CardList(bool _contentsKnown); - CardItem *findCard(const int id, const bool remove, int *position = NULL); + CardItem *findCard(const int cardId) const; bool getContentsKnown() const { return contentsKnown; diff --git a/cockatrice/src/game/zones/card_zone.cpp b/cockatrice/src/game/zones/card_zone.cpp index 13732f960..7e860d6de 100644 --- a/cockatrice/src/game/zones/card_zone.cpp +++ b/cockatrice/src/game/zones/card_zone.cpp @@ -142,7 +142,7 @@ void CardZone::addCard(CardItem *card, bool reorganize, int x, int y) CardItem *CardZone::getCard(int cardId, const QString &cardName) { - CardItem *c = cards.findCard(cardId, false); + CardItem *c = cards.findCard(cardId); if (!c) { qDebug() << "CardZone::getCard: card id=" << cardId << "not found"; return 0; From f2b0fa164e4ba7140f54f0633a3e27c1be8f79cc Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:17:37 -0800 Subject: [PATCH 14/27] add padding to right side of card reveal window (#5198) --- cockatrice/src/game/zones/view_zone.cpp | 17 +++++++++-------- cockatrice/src/game/zones/view_zone.h | 3 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 6d4908179..aea6e0df2 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -145,28 +145,29 @@ void ZoneViewZone::reorganizeCards() } lastCardType = cardType; - qreal x = 7 + (typeColumn * CARD_WIDTH); + qreal x = typeColumn * CARD_WIDTH; qreal y = typeRow * CARD_HEIGHT / 3; - c->setPos(x + 5, y + 5); + c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); longestRow = qMax(typeRow, longestRow); } } else { for (int i = 0; i < cardCount; i++) { CardItem *c = cardsToDisplay.at(i); - qreal x = 7 + ((i / rows) * CARD_WIDTH); + qreal x = (i / rows) * CARD_WIDTH; qreal y = (i % rows) * CARD_HEIGHT / 3; - c->setPos(x + 5, y + 5); + c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); } } + int totalRows = (pileView && sortByType) ? longestRow : rows; + int totalColumns = (pileView && sortByType) ? qMax(typeColumn + 1, 3) : qMax(cols, 1); + qreal aleft = 0; qreal atop = 0; - qreal awidth = (pileView && sortByType) ? qMax(typeColumn + 1, 3) * CARD_WIDTH + (CARD_WIDTH / 2) - : qMax(cols, 1) * CARD_WIDTH + (CARD_WIDTH / 2); - qreal aheight = (pileView && sortByType) ? (longestRow * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3 - : (rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; + qreal awidth = totalColumns * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; + qreal aheight = (totalRows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; optimumRect = QRectF(aleft, atop, awidth, aheight); updateGeometry(); diff --git a/cockatrice/src/game/zones/view_zone.h b/cockatrice/src/game/zones/view_zone.h index a0e17b6c3..a0bc15291 100644 --- a/cockatrice/src/game/zones/view_zone.h +++ b/cockatrice/src/game/zones/view_zone.h @@ -15,6 +15,9 @@ class ZoneViewZone : public SelectZone, public QGraphicsLayoutItem Q_OBJECT Q_INTERFACES(QGraphicsLayoutItem) private: + static constexpr int HORIZONTAL_PADDING = 12; + static constexpr int VERTICAL_PADDING = 5; + QRectF bRect, optimumRect; int minRows, numberCards; void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint); From c6bfc8b8eacf359ba15fa2ea33d9df3200c2f9aa Mon Sep 17 00:00:00 2001 From: Zach H Date: Wed, 27 Nov 2024 14:11:55 -0800 Subject: [PATCH 15/27] Fix Qt5 Slot/Signals for QCheckBoxes (#5201) --- cockatrice/src/dialogs/dlg_connect.cpp | 3 +- cockatrice/src/dialogs/dlg_create_game.cpp | 3 +- cockatrice/src/dialogs/dlg_settings.cpp | 103 ++++++++---------- .../src/game/zones/view_zone_widget.cpp | 9 +- 4 files changed, 53 insertions(+), 65 deletions(-) diff --git a/cockatrice/src/dialogs/dlg_connect.cpp b/cockatrice/src/dialogs/dlg_connect.cpp index d618d3ae4..27fff0509 100644 --- a/cockatrice/src/dialogs/dlg_connect.cpp +++ b/cockatrice/src/dialogs/dlg_connect.cpp @@ -80,8 +80,7 @@ DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent) autoConnectCheckBox->setEnabled(false); } - connect(savePasswordCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(passwordSaved(QT_STATE_CHANGED_T))); + connect(savePasswordCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &DlgConnect::passwordSaved); serverIssuesLabel = new QLabel(tr("If you have any trouble connecting or registering then contact the server staff for help!")); diff --git a/cockatrice/src/dialogs/dlg_create_game.cpp b/cockatrice/src/dialogs/dlg_create_game.cpp index 536077de2..c8131a1cf 100644 --- a/cockatrice/src/dialogs/dlg_create_game.cpp +++ b/cockatrice/src/dialogs/dlg_create_game.cpp @@ -81,8 +81,7 @@ void DlgCreateGame::sharedCtor() spectatorsAllowedCheckBox = new QCheckBox(tr("&Spectators can watch")); spectatorsAllowedCheckBox->setChecked(true); - connect(spectatorsAllowedCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(spectatorsAllowedChanged(QT_STATE_CHANGED_T))); + connect(spectatorsAllowedCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &DlgCreateGame::spectatorsAllowedChanged); spectatorsNeedPasswordCheckBox = new QCheckBox(tr("Spectators &need a password to watch")); spectatorsCanTalkCheckBox = new QCheckBox(tr("Spectators can &chat")); spectatorsSeeEverythingCheckBox = new QCheckBox(tr("Spectators can see &hands")); diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 9451bb543..babe49213 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -74,10 +74,9 @@ GeneralSettingsPage::GeneralSettingsPage() connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); connect(&updateReleaseChannelBox, SIGNAL(currentIndexChanged(int)), &settings, SLOT(setUpdateReleaseChannel(int))); - connect(&updateNotificationCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setNotifyAboutUpdate(QT_STATE_CHANGED_T))); - connect(&newVersionOracleCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setNotifyAboutNewVersion(QT_STATE_CHANGED_T))); + connect(&updateNotificationCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setNotifyAboutUpdate); + connect(&newVersionOracleCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setNotifyAboutNewVersion); connect(&showTipsOnStartup, SIGNAL(clicked(bool)), &settings, SLOT(setShowTipsOnStartup(bool))); auto *personalGrid = new QGridLayout; @@ -324,12 +323,10 @@ AppearanceSettingsPage::AppearanceSettingsPage() themeGroupBox->setLayout(themeGrid); displayCardNamesCheckBox.setChecked(settings.getDisplayCardNames()); - connect(&displayCardNamesCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setDisplayCardNames(QT_STATE_CHANGED_T))); + connect(&displayCardNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setDisplayCardNames); cardScalingCheckBox.setChecked(settings.getScaleCards()); - connect(&cardScalingCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setCardScaling(QT_STATE_CHANGED_T))); + connect(&cardScalingCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setCardScaling); verticalCardOverlapPercentBox.setValue(settings.getStackCardOverlapPercent()); verticalCardOverlapPercentBox.setRange(0, 80); @@ -346,12 +343,10 @@ AppearanceSettingsPage::AppearanceSettingsPage() cardsGroupBox->setLayout(cardsGrid); horizontalHandCheckBox.setChecked(settings.getHorizontalHand()); - connect(&horizontalHandCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setHorizontalHand(QT_STATE_CHANGED_T))); + connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand); leftJustifiedHandCheckBox.setChecked(settings.getLeftJustified()); - connect(&leftJustifiedHandCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setLeftJustified(QT_STATE_CHANGED_T))); + connect(&leftJustifiedHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setLeftJustified); auto *handGrid = new QGridLayout; handGrid->addWidget(&horizontalHandCheckBox, 0, 0, 1, 2); @@ -361,8 +356,8 @@ AppearanceSettingsPage::AppearanceSettingsPage() handGroupBox->setLayout(handGrid); invertVerticalCoordinateCheckBox.setChecked(settings.getInvertVerticalCoordinate()); - connect(&invertVerticalCoordinateCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &settings, - SLOT(setInvertVerticalCoordinate(QT_STATE_CHANGED_T))); + connect(&invertVerticalCoordinateCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, + &SettingsCache::setInvertVerticalCoordinate); minPlayersForMultiColumnLayoutEdit.setMinimum(2); minPlayersForMultiColumnLayoutEdit.setValue(settings.getMinPlayersForMultiColumnLayout()); @@ -442,37 +437,37 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() { // general settings and notification settings notificationsEnabledCheckBox.setChecked(SettingsCache::instance().getNotificationsEnabled()); - connect(¬ificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setNotificationsEnabled(QT_STATE_CHANGED_T))); - connect(¬ificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(setNotificationEnabled(QT_STATE_CHANGED_T))); + connect(¬ificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setNotificationsEnabled); + connect(¬ificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, this, + &UserInterfaceSettingsPage::setNotificationEnabled); specNotificationsEnabledCheckBox.setChecked(SettingsCache::instance().getSpectatorNotificationsEnabled()); specNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled()); - connect(&specNotificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setSpectatorNotificationsEnabled(QT_STATE_CHANGED_T))); + connect(&specNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setSpectatorNotificationsEnabled); buddyConnectNotificationsEnabledCheckBox.setChecked( SettingsCache::instance().getBuddyConnectNotificationsEnabled()); buddyConnectNotificationsEnabledCheckBox.setEnabled(SettingsCache::instance().getNotificationsEnabled()); - connect(&buddyConnectNotificationsEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), - &SettingsCache::instance(), SLOT(setBuddyConnectNotificationsEnabled(QT_STATE_CHANGED_T))); + connect(&buddyConnectNotificationsEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setBuddyConnectNotificationsEnabled); doubleClickToPlayCheckBox.setChecked(SettingsCache::instance().getDoubleClickToPlay()); - connect(&doubleClickToPlayCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setDoubleClickToPlay(QT_STATE_CHANGED_T))); + connect(&doubleClickToPlayCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setDoubleClickToPlay); playToStackCheckBox.setChecked(SettingsCache::instance().getPlayToStack()); - connect(&playToStackCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setPlayToStack(QT_STATE_CHANGED_T))); + connect(&playToStackCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPlayToStack); annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens()); - connect(&annotateTokensCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setAnnotateTokens(QT_STATE_CHANGED_T))); + connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setAnnotateTokens); useTearOffMenusCheckBox.setChecked(SettingsCache::instance().getUseTearOffMenus()); connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - [](QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); + [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); auto *generalGrid = new QGridLayout; generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0); @@ -493,8 +488,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() // animation settings tapAnimationCheckBox.setChecked(SettingsCache::instance().getTapAnimation()); - connect(&tapAnimationCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setTapAnimation(QT_STATE_CHANGED_T))); + connect(&tapAnimationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setTapAnimation); auto *animationGrid = new QGridLayout; animationGrid->addWidget(&tapAnimationCheckBox, 0, 0); @@ -504,8 +499,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() // deck editor settings openDeckInNewTabCheckBox.setChecked(SettingsCache::instance().getOpenDeckInNewTab()); - connect(&openDeckInNewTabCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setOpenDeckInNewTab(QT_STATE_CHANGED_T))); + connect(&openDeckInNewTabCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setOpenDeckInNewTab); auto *deckEditorGrid = new QGridLayout; deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); @@ -554,8 +549,8 @@ void UserInterfaceSettingsPage::retranslateUi() DeckEditorSettingsPage::DeckEditorSettingsPage() { picDownloadCheckBox.setChecked(SettingsCache::instance().getPicDownload()); - connect(&picDownloadCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setPicDownload(QT_STATE_CHANGED_T))); + connect(&picDownloadCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setPicDownload); urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); urlLinkLabel.setOpenExternalLinks(true); @@ -870,30 +865,29 @@ void DeckEditorSettingsPage::retranslateUi() MessagesSettingsPage::MessagesSettingsPage() { chatMentionCheckBox.setChecked(SettingsCache::instance().getChatMention()); - connect(&chatMentionCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setChatMention(QT_STATE_CHANGED_T))); + connect(&chatMentionCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setChatMention); chatMentionCompleterCheckbox.setChecked(SettingsCache::instance().getChatMentionCompleter()); - connect(&chatMentionCompleterCheckbox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setChatMentionCompleter(QT_STATE_CHANGED_T))); + connect(&chatMentionCompleterCheckbox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setChatMentionCompleter); explainMessagesLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); explainMessagesLabel.setOpenExternalLinks(true); ignoreUnregUsersMainChat.setChecked(SettingsCache::instance().getIgnoreUnregisteredUsers()); ignoreUnregUserMessages.setChecked(SettingsCache::instance().getIgnoreUnregisteredUserMessages()); - connect(&ignoreUnregUsersMainChat, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setIgnoreUnregisteredUsers(QT_STATE_CHANGED_T))); - connect(&ignoreUnregUserMessages, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T))); + connect(&ignoreUnregUsersMainChat, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setIgnoreUnregisteredUsers); + connect(&ignoreUnregUserMessages, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setIgnoreUnregisteredUserMessages); invertMentionForeground.setChecked(SettingsCache::instance().getChatMentionForeground()); - connect(&invertMentionForeground, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(updateTextColor(QT_STATE_CHANGED_T))); + connect(&invertMentionForeground, &QCheckBox::QT_STATE_CHANGED, this, &MessagesSettingsPage::updateTextColor); invertHighlightForeground.setChecked(SettingsCache::instance().getChatHighlightForeground()); - connect(&invertHighlightForeground, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(updateTextHighlightColor(QT_STATE_CHANGED_T))); + connect(&invertHighlightForeground, &QCheckBox::QT_STATE_CHANGED, this, + &MessagesSettingsPage::updateTextHighlightColor); mentionColor = new QLineEdit(); mentionColor->setText(SettingsCache::instance().getChatMentionColor()); @@ -901,16 +895,15 @@ MessagesSettingsPage::MessagesSettingsPage() connect(mentionColor, SIGNAL(textChanged(QString)), this, SLOT(updateColor(QString))); messagePopups.setChecked(SettingsCache::instance().getShowMessagePopup()); - connect(&messagePopups, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setShowMessagePopups(QT_STATE_CHANGED_T))); + connect(&messagePopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowMessagePopups); mentionPopups.setChecked(SettingsCache::instance().getShowMentionPopup()); - connect(&mentionPopups, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setShowMentionPopups(QT_STATE_CHANGED_T))); + connect(&mentionPopups, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setShowMentionPopups); roomHistory.setChecked(SettingsCache::instance().getRoomHistory()); - connect(&roomHistory, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setRoomHistory(QT_STATE_CHANGED_T))); + connect(&roomHistory, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setRoomHistory); customAlertString = new QLineEdit(); customAlertString->setPlaceholderText(tr("Word1 Word2 Word3")); @@ -1112,8 +1105,8 @@ void MessagesSettingsPage::retranslateUi() SoundSettingsPage::SoundSettingsPage() { soundEnabledCheckBox.setChecked(SettingsCache::instance().getSoundEnabled()); - connect(&soundEnabledCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), &SettingsCache::instance(), - SLOT(setSoundEnabled(QT_STATE_CHANGED_T))); + connect(&soundEnabledCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setSoundEnabled); QString themeName = SettingsCache::instance().getSoundThemeName(); diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 974073a88..077cff0b2 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -96,12 +96,9 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // numberCard is the num of cards we want to reveal from an area. Ex: scry the top 3 cards. // If the number is < 0 then it means that we can make the area sorted and we dont care about the order. if (numberCards < 0) { - connect(&sortByNameCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(processSortByName(QT_STATE_CHANGED_T))); - connect(&sortByTypeCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(processSortByType(QT_STATE_CHANGED_T))); - connect(&pileViewCheckBox, SIGNAL(QT_STATE_CHANGED(QT_STATE_CHANGED_T)), this, - SLOT(processSetPileView(QT_STATE_CHANGED_T))); + connect(&sortByNameCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSortByName); + connect(&sortByTypeCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSortByType); + connect(&pileViewCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSetPileView); sortByNameCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByName()); sortByTypeCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByType()); pileViewCheckBox.setChecked(SettingsCache::instance().getZoneViewPileView()); From 24b5dab456b546b2b60b522fa83eca325f458211 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:40:49 -0800 Subject: [PATCH 16/27] leave some documentation on Zone classes (#5199) * leave some documentation on Zone classes * small refactor * undo functional change from refactor and clean up comments * move variables into if block --- .../src/game/board/abstract_graphics_item.h | 3 ++ cockatrice/src/game/player/player.h | 3 ++ cockatrice/src/game/zones/card_zone.cpp | 8 +++++ cockatrice/src/game/zones/card_zone.h | 6 ++++ cockatrice/src/game/zones/pile_zone.h | 4 +++ cockatrice/src/game/zones/select_zone.h | 3 ++ cockatrice/src/game/zones/view_zone.cpp | 7 ++++ cockatrice/src/game/zones/view_zone.h | 9 ++++++ .../src/game/zones/view_zone_widget.cpp | 32 ++++++++++++------- cockatrice/src/game/zones/view_zone_widget.h | 6 ++++ 10 files changed, 69 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/game/board/abstract_graphics_item.h b/cockatrice/src/game/board/abstract_graphics_item.h index 8fef7bb79..3461c35b3 100644 --- a/cockatrice/src/game/board/abstract_graphics_item.h +++ b/cockatrice/src/game/board/abstract_graphics_item.h @@ -13,6 +13,9 @@ enum GraphicsItemType typeOther = QGraphicsItem::UserType + 6 }; +/** + * Parent class of all objects that appear in a game. + */ class AbstractGraphicsItem : public QObject, public QGraphicsItem { Q_OBJECT diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index 5aecaa932..7f744a909 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -68,6 +68,9 @@ class ZoneViewZone; const int MAX_TOKENS_PER_DIALOG = 99; +/** + * The entire graphical area belonging to a single player. + */ class PlayerArea : public QObject, public QGraphicsItem { Q_OBJECT diff --git a/cockatrice/src/game/zones/card_zone.cpp b/cockatrice/src/game/zones/card_zone.cpp index 7e860d6de..ad0175506 100644 --- a/cockatrice/src/game/zones/card_zone.cpp +++ b/cockatrice/src/game/zones/card_zone.cpp @@ -11,6 +11,14 @@ #include #include +/** + * @param _p the player that the zone belongs to + * @param _name internal name of the zone + * @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it + * @param _contentsKnown whether the cards in the zone are known to the client + * @param parent the parent graphics object. + * @param _isView whether this zone is a view of another zone. Modifications to a view should modify the original + */ CardZone::CardZone(Player *_p, const QString &_name, bool _hasCardAttr, diff --git a/cockatrice/src/game/zones/card_zone.h b/cockatrice/src/game/zones/card_zone.h index c0ad27bc3..6d6cddd81 100644 --- a/cockatrice/src/game/zones/card_zone.h +++ b/cockatrice/src/game/zones/card_zone.h @@ -14,6 +14,12 @@ class QAction; class QPainter; class CardDragItem; +/** + * A zone in the game that can contain cards. + * This class contains methods to get and modify the cards that are contained inside this zone. + * + * The cards are stored as a list of `CardItem*`. + */ class CardZone : public AbstractGraphicsItem { Q_OBJECT diff --git a/cockatrice/src/game/zones/pile_zone.h b/cockatrice/src/game/zones/pile_zone.h index 5d28b3816..a66543ab3 100644 --- a/cockatrice/src/game/zones/pile_zone.h +++ b/cockatrice/src/game/zones/pile_zone.h @@ -3,6 +3,10 @@ #include "card_zone.h" +/** + * A CardZone where the cards are in a single pile instead of being laid out. + * Usually only top card is accessible by clicking. + */ class PileZone : public CardZone { Q_OBJECT diff --git a/cockatrice/src/game/zones/select_zone.h b/cockatrice/src/game/zones/select_zone.h index efec4bb02..ff0c12182 100644 --- a/cockatrice/src/game/zones/select_zone.h +++ b/cockatrice/src/game/zones/select_zone.h @@ -3,6 +3,9 @@ #include "card_zone.h" +/** + * A CardZone where the cards are laid out, with each card directly interactable by clicking. + */ class SelectZone : public CardZone { Q_OBJECT diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index aea6e0df2..6c7defe66 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -16,6 +16,13 @@ #include #include +/** + * @param _p the player that the cards are revealed to. + * @param _origZone the zone the cards were revealed from. + * @param _revealZone if false, the cards will be face down. + * @param _writeableRevealZone whether the player can interact with the revealed cards. + * @param parent the parent QGraphicsWidget containing the reveal zone + */ ZoneViewZone::ZoneViewZone(Player *_p, CardZone *_origZone, int _numberCards, diff --git a/cockatrice/src/game/zones/view_zone.h b/cockatrice/src/game/zones/view_zone.h index a0bc15291..092a1c51c 100644 --- a/cockatrice/src/game/zones/view_zone.h +++ b/cockatrice/src/game/zones/view_zone.h @@ -10,6 +10,15 @@ class Response; class ServerInfo_Card; class QGraphicsSceneWheelEvent; +/** + * A CardZone that is a view into another CardZone. + * If this zone is writable, then modifications to this zone will be reflected in the underlying zone. + * + * Most interactions with StackZones are handled through a zone view. + * For example, viewing the deck/graveyard/exile is handled through a view. + * + * Handles both limited reveals (eg. look at top X cards) and full zone reveals (eg. searching the deck). + */ class ZoneViewZone : public SelectZone, public QGraphicsLayoutItem { Q_OBJECT diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 077cff0b2..b6275c813 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -17,6 +17,15 @@ #include #include +/** + * @param _player the player the cards were revealed to. + * @param _origZone the zone the cards were revealed from. + * @param numberCards num of cards to reveal from the zone. Ex: scry the top 3 cards. + * Pass in a negative number to reveal the entire zone. + * -1 specifically will give the option to shuffle the zone upon closing the window. + * @param _revealZone if false, the cards will be face down. + * @param _writeableRevealZone whether the player can interact with the revealed cards. + */ ZoneViewWidget::ZoneViewWidget(Player *_player, CardZone *_origZone, int numberCards, @@ -31,10 +40,10 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, setFlag(ItemIgnoresTransformations); QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical); - QGraphicsLinearLayout *hPilebox = 0; + // If the number is < 0, then it means that we can give the option to make the area sorted if (numberCards < 0) { - hPilebox = new QGraphicsLinearLayout(Qt::Horizontal); + QGraphicsLinearLayout *hPilebox = new QGraphicsLinearLayout(Qt::Horizontal); QGraphicsLinearLayout *hFilterbox = new QGraphicsLinearLayout(Qt::Horizontal); QGraphicsProxyWidget *sortByNameProxy = new QGraphicsProxyWidget; @@ -57,16 +66,16 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, QGraphicsProxyWidget *pileViewProxy = new QGraphicsProxyWidget; pileViewProxy->setWidget(&pileViewCheckBox); hPilebox->addItem(pileViewProxy); - } - if (_origZone->getIsShufflable() && (numberCards == -1)) { - shuffleCheckBox.setChecked(true); - QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget; - shuffleProxy->setWidget(&shuffleCheckBox); - hPilebox->addItem(shuffleProxy); - } + if (_origZone->getIsShufflable() && numberCards == -1) { + shuffleCheckBox.setChecked(true); + QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget; + shuffleProxy->setWidget(&shuffleCheckBox); + hPilebox->addItem(shuffleProxy); + } - vbox->addItem(hPilebox); + vbox->addItem(hPilebox); + } extraHeight = vbox->sizeHint(Qt::PreferredSize).height(); resize(150, 150); @@ -93,8 +102,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, connect(zone, SIGNAL(wheelEventReceived(QGraphicsSceneWheelEvent *)), scrollBarProxy, SLOT(recieveWheelEvent(QGraphicsSceneWheelEvent *))); - // numberCard is the num of cards we want to reveal from an area. Ex: scry the top 3 cards. - // If the number is < 0 then it means that we can make the area sorted and we dont care about the order. + // only wire up sort options after creating ZoneViewZone, since it segfaults otherwise. if (numberCards < 0) { connect(&sortByNameCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSortByName); connect(&sortByTypeCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSortByType); diff --git a/cockatrice/src/game/zones/view_zone_widget.h b/cockatrice/src/game/zones/view_zone_widget.h index 43e92f425..78b667bfa 100644 --- a/cockatrice/src/game/zones/view_zone_widget.h +++ b/cockatrice/src/game/zones/view_zone_widget.h @@ -31,6 +31,12 @@ public slots: } }; +/** + * A QGraphicsWidget that holds a ZoneViewZone. + * + * Some zone views allow sorting. + * This widget will display the sort options when relevant, and forward the values of the options to the ZoneViewZone. + */ class ZoneViewWidget : public QGraphicsWidget { Q_OBJECT From 37bb1367db8c2a80e500a4c3663f362c2fb32c36 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:59:31 -0800 Subject: [PATCH 17/27] refactor method for positioning cards in ZoneViewZone (#5203) * refactor out method for positioning cards in zone view * rename some variables * use max/min * some small formatting stuff --- cockatrice/src/game/zones/view_zone.cpp | 97 ++++++++++++++----------- cockatrice/src/game/zones/view_zone.h | 8 ++ 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 6c7defe66..6f238527f 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -112,73 +112,86 @@ void ZoneViewZone::reorganizeCards() for (int i = 0; i < cardCount; ++i) cards[i]->setId(i); - int cols = qFloor(qSqrt((double)cardCount / 2)); - if (cols > 7) - cols = 7; - int rows = qCeil((double)cardCount / cols); - if (rows < 1) - rows = 1; - if (minRows == 0) - minRows = rows; - else if (rows < minRows) { - rows = minRows; - cols = qCeil((double)cardCount / minRows); - } - if (cols < 2) - cols = 2; - - qDebug() << "reorganizeCards: rows=" << rows << "cols=" << cols; - CardList cardsToDisplay(cards); if (sortByName || sortByType) cardsToDisplay.sort((sortByName ? CardList::SortByName : 0) | (sortByType ? CardList::SortByType : 0)); - int typeColumn = 0; - int longestRow = 0; - if (pileView && sortByType) { // we need sort by type enabled for the feature to work - int typeRow = 0; + auto gridSize = positionCardsForDisplay(cardsToDisplay, pileView && sortByType); + + qreal aleft = 0; + qreal atop = 0; + qreal awidth = gridSize.cols * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; + qreal aheight = (gridSize.rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; + optimumRect = QRectF(aleft, atop, awidth, aheight); + + updateGeometry(); + emit optimumRectChanged(); +} + +/** + * @brief Sets the position of each card to the proper position for the view + * + * @param cards The cards to reposition. Will modify the cards in the list. + * @param groupByType Whether to make piles by card type. If true, then expects `cards` to be sorted by type. + * + * @returns The number of rows and columns to display + */ +ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, bool groupByType) +{ + int cardCount = cards.size(); + if (groupByType) { + int row = 0; + int col = 0; + int longestRow = 0; QString lastCardType; for (int i = 0; i < cardCount; i++) { - CardItem *c = cardsToDisplay.at(i); + CardItem *c = cards.at(i); QString cardType = c->getInfo() ? c->getInfo()->getMainCardType() : ""; if (i) { // if not the first card if (cardType == lastCardType) - typeRow++; // add below current card - else { // if no match then move card to next column - typeColumn++; - typeRow = 0; + row++; // add below current card + else { // if no match then move card to next column + col++; + row = 0; } } lastCardType = cardType; - qreal x = typeColumn * CARD_WIDTH; - qreal y = typeRow * CARD_HEIGHT / 3; + qreal x = col * CARD_WIDTH; + qreal y = row * CARD_HEIGHT / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); - longestRow = qMax(typeRow, longestRow); + longestRow = qMax(row, longestRow); } + + return GridSize{longestRow, qMax(col + 1, 3)}; + } else { + int cols = qMin(qFloor(qSqrt((double)cardCount / 2)), 7); + int rows = qMax(qCeil((double)cardCount / cols), 1); + if (minRows == 0) { + minRows = rows; + } else if (rows < minRows) { + rows = minRows; + cols = qCeil((double)cardCount / minRows); + } + + if (cols < 2) + cols = 2; + + qDebug() << "reorganizeCards: rows=" << rows << "cols=" << cols; + for (int i = 0; i < cardCount; i++) { - CardItem *c = cardsToDisplay.at(i); + CardItem *c = cards.at(i); qreal x = (i / rows) * CARD_WIDTH; qreal y = (i % rows) * CARD_HEIGHT / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); } + + return GridSize{rows, qMax(cols, 1)}; } - - int totalRows = (pileView && sortByType) ? longestRow : rows; - int totalColumns = (pileView && sortByType) ? qMax(typeColumn + 1, 3) : qMax(cols, 1); - - qreal aleft = 0; - qreal atop = 0; - qreal awidth = totalColumns * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; - qreal aheight = (totalRows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; - optimumRect = QRectF(aleft, atop, awidth, aheight); - - updateGeometry(); - emit optimumRectChanged(); } void ZoneViewZone::setSortByName(int _sortByName) diff --git a/cockatrice/src/game/zones/view_zone.h b/cockatrice/src/game/zones/view_zone.h index 092a1c51c..9179c477b 100644 --- a/cockatrice/src/game/zones/view_zone.h +++ b/cockatrice/src/game/zones/view_zone.h @@ -35,6 +35,14 @@ private: bool sortByName, sortByType; bool pileView; + struct GridSize + { + int rows; + int cols; + }; + + GridSize positionCardsForDisplay(CardList &cards, bool groupByType = false); + public: ZoneViewZone(Player *_p, CardZone *_origZone, From 17eabf2004aabf3580aea944d71519ecaf2cce79 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 29 Nov 2024 09:53:06 -0800 Subject: [PATCH 18/27] add sort options to card view window (#5206) * refactor to allow for sorting by property of choice * implement thing * prevent overlapping sort properties * enable/disable pile view checkbox if groupBy is off * fix compiler warnings * check to disable pile view checkbox on init * Fix builds on Qt5 * Fix builds on Qt5 --------- Co-authored-by: ZeldaZach --- cockatrice/src/game/cards/card_list.cpp | 56 +++++++++++--- cockatrice/src/game/cards/card_list.h | 13 +++- cockatrice/src/game/zones/view_zone.cpp | 60 +++++++++++---- cockatrice/src/game/zones/view_zone.h | 8 +- .../src/game/zones/view_zone_widget.cpp | 77 ++++++++++++++----- cockatrice/src/game/zones/view_zone_widget.h | 10 +-- cockatrice/src/settings/cache_settings.cpp | 16 ++-- cockatrice/src/settings/cache_settings.h | 15 ++-- dbconverter/src/mocks.cpp | 4 +- tests/carddatabase/mocks.cpp | 4 +- 10 files changed, 183 insertions(+), 80 deletions(-) diff --git a/cockatrice/src/game/cards/card_list.cpp b/cockatrice/src/game/cards/card_list.cpp index f35a0d842..5d5e5dbef 100644 --- a/cockatrice/src/game/cards/card_list.cpp +++ b/cockatrice/src/game/cards/card_list.cpp @@ -3,6 +3,7 @@ #include "card_database.h" #include "card_item.h" +#include #include CardList::CardList(bool _contentsKnown) : QList(), contentsKnown(_contentsKnown) @@ -31,18 +32,53 @@ CardItem *CardList::findCard(const int cardId) const return nullptr; } -void CardList::sort(int flags) +/** + * @brief sorts the list by using string comparison on properties extracted from the CardItem + * The cards are compared using each property in order. + * If two cards have the same value for a property, then the next property in the list is used. + * + * @param option the option to compare the cards by, in order of usage. + */ +void CardList::sortBy(const QList &option) { - auto comparator = [flags](CardItem *a, CardItem *b) { - if (flags & SortByType) { - QString t1 = a->getInfo() ? a->getInfo()->getMainCardType() : QString(); - QString t2 = b->getInfo() ? b->getInfo()->getMainCardType() : QString(); - if ((t1 == t2) && (flags & SortByName)) - return a->getName() < b->getName(); - return t1 < t2; - } else - return a->getName() < b->getName(); + // early return if we know we won't be sorting + if (option.isEmpty()) { + return; + } + + auto comparator = [&option](CardItem *a, CardItem *b) { + for (auto prop : option) { + auto extractor = getExtractorFor(prop); + QString t1 = extractor(a); + QString t2 = extractor(b); + if (t1 != t2) { + return t1 < t2; + } + } + return false; }; std::sort(begin(), end(), comparator); } + +/** + * @brief returns the function that extracts the given property from the CardItem. + */ +std::function CardList::getExtractorFor(SortOption option) +{ + switch (option) { + case NoSort: + return [](CardItem *) { return ""; }; + case SortByName: + return [](CardItem *c) { return c->getName(); }; + case SortByType: + return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getMainCardType() : ""; }; + case SortByManaValue: + // getCmc returns the int as a string. We pad with 0's so that string comp also works on it + return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getCmc().rightJustified(4, '0') : ""; }; + } + + // this line should never be reached + qDebug() << "cardlist.cpp: Could not find extractor for SortOption" << option; + return [](CardItem *) { return ""; }; +} \ No newline at end of file diff --git a/cockatrice/src/game/cards/card_list.h b/cockatrice/src/game/cards/card_list.h index d48e4c3d2..cbfb3ecff 100644 --- a/cockatrice/src/game/cards/card_list.h +++ b/cockatrice/src/game/cards/card_list.h @@ -11,10 +11,12 @@ protected: bool contentsKnown; public: - enum SortFlags + enum SortOption { - SortByName = 1, - SortByType = 2 + NoSort, + SortByName, + SortByType, + SortByManaValue }; CardList(bool _contentsKnown); CardItem *findCard(const int cardId) const; @@ -22,7 +24,10 @@ public: { return contentsKnown; } - void sort(int flags = SortByName); + + void sortBy(const QList &options); + + static std::function getExtractorFor(SortOption option); }; #endif diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 6f238527f..566b4896e 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -31,7 +31,7 @@ ZoneViewZone::ZoneViewZone(Player *_p, QGraphicsItem *parent) : SelectZone(_p, _origZone->getName(), false, false, true, parent, true), bRect(QRectF()), minRows(0), numberCards(_numberCards), origZone(_origZone), revealZone(_revealZone), - writeableRevealZone(_writeableRevealZone), sortByName(false), sortByType(false) + writeableRevealZone(_writeableRevealZone), groupBy(CardList::NoSort), sortBy(CardList::NoSort) { if (!(revealZone && !writeableRevealZone)) { origZone->getViews().append(this); @@ -113,11 +113,33 @@ void ZoneViewZone::reorganizeCards() cards[i]->setId(i); CardList cardsToDisplay(cards); - if (sortByName || sortByType) - cardsToDisplay.sort((sortByName ? CardList::SortByName : 0) | (sortByType ? CardList::SortByType : 0)); - auto gridSize = positionCardsForDisplay(cardsToDisplay, pileView && sortByType); + // sort cards + QList sortOptions; + if (groupBy != CardList::NoSort) { + sortOptions << groupBy; + } + if (sortBy != CardList::NoSort) { + sortOptions << sortBy; + + // implicitly sort by name at the end so that cards with the same name appear together + if (sortBy != CardList::SortByName) { + sortOptions << CardList::SortByName; + } + } + + cardsToDisplay.sortBy(sortOptions); + + // position cards + GridSize gridSize; + if (pileView) { + gridSize = positionCardsForDisplay(cardsToDisplay, groupBy); + } else { + gridSize = positionCardsForDisplay(cardsToDisplay); + } + + // determine bounding rect qreal aleft = 0; qreal atop = 0; qreal awidth = gridSize.cols * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; @@ -132,24 +154,30 @@ void ZoneViewZone::reorganizeCards() * @brief Sets the position of each card to the proper position for the view * * @param cards The cards to reposition. Will modify the cards in the list. - * @param groupByType Whether to make piles by card type. If true, then expects `cards` to be sorted by type. + * @param pileOption Property used to group cards for the piles. Expects `cards` to be sorted by that property. Pass in + * NoSort to not make piles. * * @returns The number of rows and columns to display */ -ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, bool groupByType) +ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, CardList::SortOption pileOption) { int cardCount = cards.size(); - if (groupByType) { + + if (pileOption != CardList::NoSort) { int row = 0; int col = 0; int longestRow = 0; - QString lastCardType; + + QString lastColumnProp; + + const auto extractor = CardList::getExtractorFor(pileOption); + for (int i = 0; i < cardCount; i++) { CardItem *c = cards.at(i); - QString cardType = c->getInfo() ? c->getInfo()->getMainCardType() : ""; + QString columnProp = extractor(c); if (i) { // if not the first card - if (cardType == lastCardType) + if (columnProp == lastColumnProp) row++; // add below current card else { // if no match then move card to next column col++; @@ -157,7 +185,7 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, bo } } - lastCardType = cardType; + lastColumnProp = columnProp; qreal x = col * CARD_WIDTH; qreal y = row * CARD_HEIGHT / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); @@ -194,17 +222,15 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, bo } } -void ZoneViewZone::setSortByName(int _sortByName) +void ZoneViewZone::setGroupBy(CardList::SortOption _groupBy) { - sortByName = _sortByName; + groupBy = _groupBy; reorganizeCards(); } -void ZoneViewZone::setSortByType(int _sortByType) +void ZoneViewZone::setSortBy(CardList::SortOption _sortBy) { - sortByType = _sortByType; - if (!sortByType) - pileView = false; + sortBy = _sortBy; reorganizeCards(); } diff --git a/cockatrice/src/game/zones/view_zone.h b/cockatrice/src/game/zones/view_zone.h index 9179c477b..b1f5270b6 100644 --- a/cockatrice/src/game/zones/view_zone.h +++ b/cockatrice/src/game/zones/view_zone.h @@ -32,7 +32,7 @@ private: void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint); CardZone *origZone; bool revealZone, writeableRevealZone; - bool sortByName, sortByType; + CardList::SortOption groupBy, sortBy; bool pileView; struct GridSize @@ -41,7 +41,7 @@ private: int cols; }; - GridSize positionCardsForDisplay(CardList &cards, bool groupByType = false); + GridSize positionCardsForDisplay(CardList &cards, CardList::SortOption pileOption = CardList::NoSort); public: ZoneViewZone(Player *_p, @@ -75,8 +75,8 @@ public: } void setWriteableRevealZone(bool _writeableRevealZone); public slots: - void setSortByName(int _sortByName); - void setSortByType(int _sortByType); + void setGroupBy(CardList::SortOption _groupBy); + void setSortBy(CardList::SortOption _sortBy); void setPileView(int _pileView); private slots: void zoneDumpReceived(const Response &r); diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index b6275c813..20634f03a 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -46,16 +46,30 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, QGraphicsLinearLayout *hPilebox = new QGraphicsLinearLayout(Qt::Horizontal); QGraphicsLinearLayout *hFilterbox = new QGraphicsLinearLayout(Qt::Horizontal); - QGraphicsProxyWidget *sortByNameProxy = new QGraphicsProxyWidget; - sortByNameProxy->setWidget(&sortByNameCheckBox); - hFilterbox->addItem(sortByNameProxy); + // groupBy options + groupBySelector.addItem(tr("Group by ---"), CardList::NoSort); + groupBySelector.addItem(tr("Group by Type"), CardList::SortByType); + groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue); - QGraphicsProxyWidget *sortByTypeProxy = new QGraphicsProxyWidget; - sortByTypeProxy->setWidget(&sortByTypeCheckBox); - hFilterbox->addItem(sortByTypeProxy); + QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; + groupBySelectorProxy->setWidget(&groupBySelector); + groupBySelectorProxy->setZValue(2000000008); + hFilterbox->addItem(groupBySelectorProxy); + + // sortBy options + sortBySelector.addItem(tr("Sort by ---"), CardList::NoSort); + sortBySelector.addItem(tr("Sort by Name"), CardList::SortByName); + sortBySelector.addItem(tr("Sort by Type"), CardList::SortByType); + sortBySelector.addItem(tr("Sort by Mana Value"), CardList::SortByManaValue); + + QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; + sortBySelectorProxy->setWidget(&sortBySelector); + sortBySelectorProxy->setZValue(2000000007); + hFilterbox->addItem(sortBySelectorProxy); vbox->addItem(hFilterbox); + // line QGraphicsProxyWidget *lineProxy = new QGraphicsProxyWidget; QFrame *line = new QFrame; line->setFrameShape(QFrame::HLine); @@ -63,10 +77,12 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, lineProxy->setWidget(line); vbox->addItem(lineProxy); + // pile view options QGraphicsProxyWidget *pileViewProxy = new QGraphicsProxyWidget; pileViewProxy->setWidget(&pileViewCheckBox); hPilebox->addItem(pileViewProxy); + // shuffle options if (_origZone->getIsShufflable() && numberCards == -1) { shuffleCheckBox.setChecked(true); QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget; @@ -104,14 +120,18 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // only wire up sort options after creating ZoneViewZone, since it segfaults otherwise. if (numberCards < 0) { - connect(&sortByNameCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSortByName); - connect(&sortByTypeCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSortByType); + connect(&groupBySelector, static_cast(&QComboBox::currentIndexChanged), this, + &ZoneViewWidget::processGroupBy); + connect(&sortBySelector, static_cast(&QComboBox::currentIndexChanged), this, + &ZoneViewWidget::processSortBy); connect(&pileViewCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSetPileView); - sortByNameCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByName()); - sortByTypeCheckBox.setChecked(SettingsCache::instance().getZoneViewSortByType()); + groupBySelector.setCurrentIndex(groupBySelector.findData(SettingsCache::instance().getZoneViewGroupBy())); + sortBySelector.setCurrentIndex(sortBySelector.findData(SettingsCache::instance().getZoneViewSortBy())); pileViewCheckBox.setChecked(SettingsCache::instance().getZoneViewPileView()); - if (!SettingsCache::instance().getZoneViewSortByType()) + + if (CardList::NoSort == static_cast(groupBySelector.currentData().toInt())) { pileViewCheckBox.setEnabled(false); + } } retranslateUi(); @@ -122,18 +142,35 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, zone->initializeCards(cardList); } -void ZoneViewWidget::processSortByType(QT_STATE_CHANGED_T value) +void ZoneViewWidget::processGroupBy(int index) { - pileViewCheckBox.setEnabled(value); - SettingsCache::instance().setZoneViewSortByType(value); - zone->setPileView(pileViewCheckBox.isChecked()); - zone->setSortByType(value); + auto option = static_cast(groupBySelector.itemData(index).toInt()); + SettingsCache::instance().setZoneViewGroupBy(option); + zone->setGroupBy(option); + + // disable pile view checkbox if we're not grouping by anything + pileViewCheckBox.setEnabled(option != CardList::NoSort); + + // reset sortBy if it has the same value as groupBy + if (option != CardList::NoSort && + option == static_cast(sortBySelector.currentData().toInt())) { + sortBySelector.setCurrentIndex(1); // set to SortByName + } } -void ZoneViewWidget::processSortByName(QT_STATE_CHANGED_T value) +void ZoneViewWidget::processSortBy(int index) { - SettingsCache::instance().setZoneViewSortByName(value); - zone->setSortByName(value); + auto option = static_cast(sortBySelector.itemData(index).toInt()); + + // set to SortByName instead if it has the same value as groupBy + if (option != CardList::NoSort && + option == static_cast(groupBySelector.currentData().toInt())) { + sortBySelector.setCurrentIndex(1); // set to SortByName + return; + } + + SettingsCache::instance().setZoneViewSortBy(option); + zone->setSortBy(option); } void ZoneViewWidget::processSetPileView(QT_STATE_CHANGED_T value) @@ -145,8 +182,6 @@ void ZoneViewWidget::processSetPileView(QT_STATE_CHANGED_T value) void ZoneViewWidget::retranslateUi() { setWindowTitle(zone->getTranslatedName(false, CaseNominative)); - sortByNameCheckBox.setText(tr("sort by name")); - sortByTypeCheckBox.setText(tr("sort by type")); shuffleCheckBox.setText(tr("shuffle when closing")); pileViewCheckBox.setText(tr("pile view")); } diff --git a/cockatrice/src/game/zones/view_zone_widget.h b/cockatrice/src/game/zones/view_zone_widget.h index 78b667bfa..bb5ec7128 100644 --- a/cockatrice/src/game/zones/view_zone_widget.h +++ b/cockatrice/src/game/zones/view_zone_widget.h @@ -4,6 +4,7 @@ #include "../../utility/macros.h" #include +#include #include #include @@ -14,7 +15,6 @@ class ZoneViewZone; class Player; class CardDatabase; class QScrollBar; -class QCheckBox; class GameScene; class ServerInfo_Card; class QGraphicsSceneMouseEvent; @@ -47,8 +47,8 @@ private: QPushButton *closeButton; QScrollBar *scrollBar; ScrollableGraphicsProxyWidget *scrollBarProxy; - QCheckBox sortByNameCheckBox; - QCheckBox sortByTypeCheckBox; + QComboBox groupBySelector; + QComboBox sortBySelector; QCheckBox shuffleCheckBox; QCheckBox pileViewCheckBox; @@ -58,8 +58,8 @@ private: signals: void closePressed(ZoneViewWidget *zv); private slots: - void processSortByType(QT_STATE_CHANGED_T value); - void processSortByName(QT_STATE_CHANGED_T value); + void processGroupBy(int value); + void processSortBy(int value); void processSetPileView(QT_STATE_CHANGED_T value); void resizeToZoneContents(); void handleScrollBarChange(int value); diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index da0e5c0b0..c799ae85a 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -252,8 +252,8 @@ SettingsCache::SettingsCache() chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString(); chatHighlightColor = settings->value("chat/highlightcolor", "A6120D").toString(); - zoneViewSortByName = settings->value("zoneview/sortbyname", true).toBool(); - zoneViewSortByType = settings->value("zoneview/sortbytype", true).toBool(); + zoneViewGroupBy = settings->value("zoneview/groupby", 2).toInt(); + zoneViewSortBy = settings->value("zoneview/sortby", 1).toInt(); zoneViewPileView = settings->value("zoneview/pileview", true).toBool(); soundEnabled = settings->value("sound/enabled", false).toBool(); @@ -582,16 +582,16 @@ void SettingsCache::setChatHighlightColor(const QString &_chatHighlightColor) settings->setValue("chat/highlightcolor", chatHighlightColor); } -void SettingsCache::setZoneViewSortByName(QT_STATE_CHANGED_T _zoneViewSortByName) +void SettingsCache::setZoneViewGroupBy(int _zoneViewGroupBy) { - zoneViewSortByName = static_cast(_zoneViewSortByName); - settings->setValue("zoneview/sortbyname", zoneViewSortByName); + zoneViewGroupBy = _zoneViewGroupBy; + settings->setValue("zoneview/groupby", zoneViewGroupBy); } -void SettingsCache::setZoneViewSortByType(QT_STATE_CHANGED_T _zoneViewSortByType) +void SettingsCache::setZoneViewSortBy(int _zoneViewSortBy) { - zoneViewSortByType = static_cast(_zoneViewSortByType); - settings->setValue("zoneview/sortbytype", zoneViewSortByType); + zoneViewSortBy = _zoneViewSortBy; + settings->setValue("zoneview/sortby", zoneViewSortBy); } void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T _zoneViewPileView) diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index fdfdb6f53..d87c4ba3c 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -109,7 +109,8 @@ private: QString chatHighlightColor; bool chatMentionForeground; bool chatHighlightForeground; - bool zoneViewSortByName, zoneViewSortByType, zoneViewPileView; + int zoneViewSortBy, zoneViewGroupBy; + bool zoneViewPileView; bool soundEnabled; QString soundThemeName; bool ignoreUnregisteredUsers; @@ -327,13 +328,13 @@ public: { return chatHighlightForeground; } - bool getZoneViewSortByName() const + int getZoneViewGroupBy() const { - return zoneViewSortByName; + return zoneViewGroupBy; } - bool getZoneViewSortByType() const + int getZoneViewSortBy() const { - return zoneViewSortByType; + return zoneViewSortBy; } /** Returns if the view should be sorted into pile view. @@ -563,8 +564,8 @@ public slots: void setChatMentionCompleter(QT_STATE_CHANGED_T _chatMentionCompleter); void setChatMentionForeground(QT_STATE_CHANGED_T _chatMentionForeground); void setChatHighlightForeground(QT_STATE_CHANGED_T _chatHighlightForeground); - void setZoneViewSortByName(QT_STATE_CHANGED_T _zoneViewSortByName); - void setZoneViewSortByType(QT_STATE_CHANGED_T _zoneViewSortByType); + void setZoneViewGroupBy(const int _zoneViewGroupBy); + void setZoneViewSortBy(const int _zoneViewSortBy); void setZoneViewPileView(QT_STATE_CHANGED_T _zoneViewPileView); void setSoundEnabled(QT_STATE_CHANGED_T _soundEnabled); void setSoundThemeName(const QString &_soundThemeName); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index 1c90612fa..8f04ca68d 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -184,10 +184,10 @@ void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */) void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */) { } -void SettingsCache::setZoneViewSortByName(QT_STATE_CHANGED_T /* _zoneViewSortByName */) +void SettingsCache::setZoneViewGroupBy(int /* _zoneViewSortByName */) { } -void SettingsCache::setZoneViewSortByType(QT_STATE_CHANGED_T /* _zoneViewSortByType */) +void SettingsCache::setZoneViewSortBy(int /* _zoneViewSortByType */) { } void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */) diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index f311241de..b25a4ceca 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -188,10 +188,10 @@ void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */) void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */) { } -void SettingsCache::setZoneViewSortByName(QT_STATE_CHANGED_T /* _zoneViewSortByName */) +void SettingsCache::setZoneViewGroupBy(int /* _zoneViewGroupBy */) { } -void SettingsCache::setZoneViewSortByType(QT_STATE_CHANGED_T /* _zoneViewSortByType */) +void SettingsCache::setZoneViewSortBy(int /* _zoneViewSortBy */) { } void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */) From 1d8651bc0050ae9741be5316c7887e5b1fd435b9 Mon Sep 17 00:00:00 2001 From: Zach H Date: Fri, 29 Nov 2024 09:53:19 -0800 Subject: [PATCH 19/27] Fix Deck Popup Glitchy Rendering (#5204) - QLabel sizes weren't taken into account until the widget is rendered - Long QLabels can cause exacerbated issues - Force refresh after 1ms to take QLabels into account --- cockatrice/src/game/zones/view_zone_widget.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 20634f03a..7190bd043 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -140,6 +140,15 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, connect(zone, SIGNAL(optimumRectChanged()), this, SLOT(resizeToZoneContents())); connect(zone, SIGNAL(beingDeleted()), this, SLOT(zoneDeleted())); zone->initializeCards(cardList); + + auto *lastResizeBeforeVisibleTimer = new QTimer(this); + connect(lastResizeBeforeVisibleTimer, &QTimer::timeout, this, [=] { + resizeToZoneContents(); + disconnect(lastResizeBeforeVisibleTimer); + lastResizeBeforeVisibleTimer->deleteLater(); + }); + lastResizeBeforeVisibleTimer->setSingleShot(true); + lastResizeBeforeVisibleTimer->start(1); } void ZoneViewWidget::processGroupBy(int index) From 5ef1ca06f5319ecb1f9c0da5c69eb70c57339cd5 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 29 Nov 2024 19:46:16 -0800 Subject: [PATCH 20/27] store sort option in settings as QComboBox index instead of enum value (#5207) * rename config property * change default * functional changes --- .../src/game/zones/view_zone_widget.cpp | 8 ++++---- cockatrice/src/settings/cache_settings.cpp | 16 +++++++-------- cockatrice/src/settings/cache_settings.h | 20 ++++++++++++------- dbconverter/src/mocks.cpp | 4 ++-- tests/carddatabase/mocks.cpp | 4 ++-- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 7190bd043..c4690fb7c 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -125,8 +125,8 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, connect(&sortBySelector, static_cast(&QComboBox::currentIndexChanged), this, &ZoneViewWidget::processSortBy); connect(&pileViewCheckBox, &QCheckBox::QT_STATE_CHANGED, this, &ZoneViewWidget::processSetPileView); - groupBySelector.setCurrentIndex(groupBySelector.findData(SettingsCache::instance().getZoneViewGroupBy())); - sortBySelector.setCurrentIndex(sortBySelector.findData(SettingsCache::instance().getZoneViewSortBy())); + groupBySelector.setCurrentIndex(SettingsCache::instance().getZoneViewGroupByIndex()); + sortBySelector.setCurrentIndex(SettingsCache::instance().getZoneViewSortByIndex()); pileViewCheckBox.setChecked(SettingsCache::instance().getZoneViewPileView()); if (CardList::NoSort == static_cast(groupBySelector.currentData().toInt())) { @@ -154,7 +154,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, void ZoneViewWidget::processGroupBy(int index) { auto option = static_cast(groupBySelector.itemData(index).toInt()); - SettingsCache::instance().setZoneViewGroupBy(option); + SettingsCache::instance().setZoneViewGroupByIndex(index); zone->setGroupBy(option); // disable pile view checkbox if we're not grouping by anything @@ -178,7 +178,7 @@ void ZoneViewWidget::processSortBy(int index) return; } - SettingsCache::instance().setZoneViewSortBy(option); + SettingsCache::instance().setZoneViewSortByIndex(index); zone->setSortBy(option); } diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index c799ae85a..3a75af782 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -252,8 +252,8 @@ SettingsCache::SettingsCache() chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString(); chatHighlightColor = settings->value("chat/highlightcolor", "A6120D").toString(); - zoneViewGroupBy = settings->value("zoneview/groupby", 2).toInt(); - zoneViewSortBy = settings->value("zoneview/sortby", 1).toInt(); + zoneViewGroupByIndex = settings->value("zoneview/groupby", 1).toInt(); + zoneViewSortByIndex = settings->value("zoneview/sortby", 1).toInt(); zoneViewPileView = settings->value("zoneview/pileview", true).toBool(); soundEnabled = settings->value("sound/enabled", false).toBool(); @@ -582,16 +582,16 @@ void SettingsCache::setChatHighlightColor(const QString &_chatHighlightColor) settings->setValue("chat/highlightcolor", chatHighlightColor); } -void SettingsCache::setZoneViewGroupBy(int _zoneViewGroupBy) +void SettingsCache::setZoneViewGroupByIndex(int _zoneViewGroupByIndex) { - zoneViewGroupBy = _zoneViewGroupBy; - settings->setValue("zoneview/groupby", zoneViewGroupBy); + zoneViewGroupByIndex = _zoneViewGroupByIndex; + settings->setValue("zoneview/groupby", zoneViewGroupByIndex); } -void SettingsCache::setZoneViewSortBy(int _zoneViewSortBy) +void SettingsCache::setZoneViewSortByIndex(int _zoneViewSortByIndex) { - zoneViewSortBy = _zoneViewSortBy; - settings->setValue("zoneview/sortby", zoneViewSortBy); + zoneViewSortByIndex = _zoneViewSortByIndex; + settings->setValue("zoneview/sortby", zoneViewSortByIndex); } void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T _zoneViewPileView) diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index d87c4ba3c..77e826412 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -109,7 +109,7 @@ private: QString chatHighlightColor; bool chatMentionForeground; bool chatHighlightForeground; - int zoneViewSortBy, zoneViewGroupBy; + int zoneViewSortByIndex, zoneViewGroupByIndex; bool zoneViewPileView; bool soundEnabled; QString soundThemeName; @@ -328,13 +328,19 @@ public: { return chatHighlightForeground; } - int getZoneViewGroupBy() const + /** + * Currently selected index for the `Group by X` QComboBox + */ + int getZoneViewGroupByIndex() const { - return zoneViewGroupBy; + return zoneViewGroupByIndex; } - int getZoneViewSortBy() const + /** + * Currently selected index for the `Sort by X` QComboBox + */ + int getZoneViewSortByIndex() const { - return zoneViewSortBy; + return zoneViewSortByIndex; } /** Returns if the view should be sorted into pile view. @@ -564,8 +570,8 @@ public slots: void setChatMentionCompleter(QT_STATE_CHANGED_T _chatMentionCompleter); void setChatMentionForeground(QT_STATE_CHANGED_T _chatMentionForeground); void setChatHighlightForeground(QT_STATE_CHANGED_T _chatHighlightForeground); - void setZoneViewGroupBy(const int _zoneViewGroupBy); - void setZoneViewSortBy(const int _zoneViewSortBy); + void setZoneViewGroupByIndex(const int _zoneViewGroupByIndex); + void setZoneViewSortByIndex(const int _zoneViewSortByIndex); void setZoneViewPileView(QT_STATE_CHANGED_T _zoneViewPileView); void setSoundEnabled(QT_STATE_CHANGED_T _soundEnabled); void setSoundThemeName(const QString &_soundThemeName); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index 8f04ca68d..e13db95ff 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -184,10 +184,10 @@ void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */) void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */) { } -void SettingsCache::setZoneViewGroupBy(int /* _zoneViewSortByName */) +void SettingsCache::setZoneViewGroupByIndex(int /* _zoneViewGroupByIndex */) { } -void SettingsCache::setZoneViewSortBy(int /* _zoneViewSortByType */) +void SettingsCache::setZoneViewSortByIndex(int /* _zoneViewSortByIndex */) { } void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */) diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index b25a4ceca..0eed998b3 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -188,10 +188,10 @@ void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */) void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */) { } -void SettingsCache::setZoneViewGroupBy(int /* _zoneViewGroupBy */) +void SettingsCache::setZoneViewGroupByIndex(int /* _zoneViewGroupByIndex */) { } -void SettingsCache::setZoneViewSortBy(int /* _zoneViewSortBy */) +void SettingsCache::setZoneViewSortByIndex(int /* _zoneViewSortByIndex */) { } void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */) From d2bc7f6ac076a6a831aed6722f7ad88a89f838ff Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 29 Nov 2024 21:13:17 -0800 Subject: [PATCH 21/27] get retranslateUi to work with sort options (#5208) --- .../src/game/zones/view_zone_widget.cpp | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index c4690fb7c..d4b5b3b54 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -47,21 +47,12 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, QGraphicsLinearLayout *hFilterbox = new QGraphicsLinearLayout(Qt::Horizontal); // groupBy options - groupBySelector.addItem(tr("Group by ---"), CardList::NoSort); - groupBySelector.addItem(tr("Group by Type"), CardList::SortByType); - groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue); - QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; groupBySelectorProxy->setWidget(&groupBySelector); groupBySelectorProxy->setZValue(2000000008); hFilterbox->addItem(groupBySelectorProxy); // sortBy options - sortBySelector.addItem(tr("Sort by ---"), CardList::NoSort); - sortBySelector.addItem(tr("Sort by Name"), CardList::SortByName); - sortBySelector.addItem(tr("Sort by Type"), CardList::SortByType); - sortBySelector.addItem(tr("Sort by Mana Value"), CardList::SortByManaValue); - QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; sortBySelectorProxy->setWidget(&sortBySelector); sortBySelectorProxy->setZValue(2000000007); @@ -118,6 +109,8 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, connect(zone, SIGNAL(wheelEventReceived(QGraphicsSceneWheelEvent *)), scrollBarProxy, SLOT(recieveWheelEvent(QGraphicsSceneWheelEvent *))); + retranslateUi(); + // only wire up sort options after creating ZoneViewZone, since it segfaults otherwise. if (numberCards < 0) { connect(&groupBySelector, static_cast(&QComboBox::currentIndexChanged), this, @@ -134,7 +127,6 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, } } - retranslateUi(); setLayout(vbox); connect(zone, SIGNAL(optimumRectChanged()), this, SLOT(resizeToZoneContents())); @@ -191,6 +183,26 @@ void ZoneViewWidget::processSetPileView(QT_STATE_CHANGED_T value) void ZoneViewWidget::retranslateUi() { setWindowTitle(zone->getTranslatedName(false, CaseNominative)); + + { // We can't change the strings after they're put into the QComboBox, so this is our workaround + int oldIndex = sortBySelector.currentIndex(); + sortBySelector.clear(); + sortBySelector.addItem(tr("Sort by ---"), CardList::NoSort); + sortBySelector.addItem(tr("Sort by Name"), CardList::SortByName); + sortBySelector.addItem(tr("Sort by Type"), CardList::SortByType); + sortBySelector.addItem(tr("Sort by Mana Value"), CardList::SortByManaValue); + sortBySelector.setCurrentIndex(oldIndex); + } + + { + int oldIndex = groupBySelector.currentIndex(); + groupBySelector.clear(); + groupBySelector.addItem(tr("Group by ---"), CardList::NoSort); + groupBySelector.addItem(tr("Group by Type"), CardList::SortByType); + groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue); + groupBySelector.setCurrentIndex(oldIndex); + } + shuffleCheckBox.setText(tr("shuffle when closing")); pileViewCheckBox.setText(tr("pile view")); } From e33ff37c820030ab2661dd70e84a7151fbee9ef1 Mon Sep 17 00:00:00 2001 From: Zach H Date: Fri, 29 Nov 2024 22:36:38 -0800 Subject: [PATCH 22/27] Pass QTime objects instead of references (#5209) - References seem to go to 0 in newer Qt versions(?) https://doc.qt.io/qt-6/qtime.html > QTime objects should be passed by value rather than by reference to const; they simply package int. --- cockatrice/src/dialogs/dlg_filter_games.cpp | 2 +- cockatrice/src/dialogs/dlg_filter_games.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/dialogs/dlg_filter_games.cpp b/cockatrice/src/dialogs/dlg_filter_games.cpp index 6b99d0046..f988c5a49 100644 --- a/cockatrice/src/dialogs/dlg_filter_games.cpp +++ b/cockatrice/src/dialogs/dlg_filter_games.cpp @@ -276,7 +276,7 @@ int DlgFilterGames::getMaxPlayersFilterMax() const return maxPlayersFilterMaxSpinBox->value(); } -const QTime &DlgFilterGames::getMaxGameAge() const +QTime DlgFilterGames::getMaxGameAge() const { int index = maxGameAgeComboBox->currentIndex(); if (index < 0 || index >= gameAgeMap.size()) { // index is out of bounds diff --git a/cockatrice/src/dialogs/dlg_filter_games.h b/cockatrice/src/dialogs/dlg_filter_games.h index 0b1fd333c..77e5cbb2e 100644 --- a/cockatrice/src/dialogs/dlg_filter_games.h +++ b/cockatrice/src/dialogs/dlg_filter_games.h @@ -67,7 +67,7 @@ public: int getMaxPlayersFilterMin() const; int getMaxPlayersFilterMax() const; void setMaxPlayersFilter(int _maxPlayersFilterMin, int _maxPlayersFilterMax); - const QTime &getMaxGameAge() const; + QTime getMaxGameAge() const; const QMap gameAgeMap; bool getShowOnlyIfSpectatorsCanWatch() const; bool getShowSpectatorPasswordProtected() const; From f634177973c7351db8bd4208b2befb5eaf3067e8 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Sat, 30 Nov 2024 12:55:06 +0100 Subject: [PATCH 23/27] Updates for project Cockatrice and language nl (#5170) * Translate cockatrice/cockatrice_en@source.ts in nl 100% translated source file: 'cockatrice/cockatrice_en@source.ts' on 'nl'. * Translate cockatrice/cockatrice_en@source.ts in nl 100% translated source file: 'cockatrice/cockatrice_en@source.ts' on 'nl'. * Translate cockatrice/cockatrice_en@source.ts in nl 100% translated source file: 'cockatrice/cockatrice_en@source.ts' on 'nl'. --------- Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com> --- cockatrice/translations/cockatrice_nl.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cockatrice/translations/cockatrice_nl.ts b/cockatrice/translations/cockatrice_nl.ts index ada657826..de57fce86 100644 --- a/cockatrice/translations/cockatrice_nl.ts +++ b/cockatrice/translations/cockatrice_nl.ts @@ -45,7 +45,7 @@ Open themes folder - Open thema folder + Open themafolder @@ -55,7 +55,7 @@ Display card names on cards having a picture - Kaartnamen altijd weergeven + Kaartnamen altijd weergeven op kaarten met een afbeelding @@ -7063,7 +7063,7 @@ Gelieve af te zien van deelname aan deze activiteit of er kunnen verdere acties Replays - + Replays @@ -7749,12 +7749,12 @@ Gelieve af te zien van deelname aan deze activiteit of er kunnen verdere acties Play/Pause - + Start/Stop Toggle Fast Forward - + Versnelling Schakelen \ No newline at end of file From bb84b75db997d1c164ac747183e6baf7a52d0c75 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 30 Nov 2024 05:53:10 -0800 Subject: [PATCH 24/27] fix bug where card view window with pile view is too short (#5212) --- cockatrice/src/game/zones/view_zone.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 566b4896e..f04bda042 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -193,7 +193,9 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca longestRow = qMax(row, longestRow); } - return GridSize{longestRow, qMax(col + 1, 3)}; + // +1 because the row/col variables used in the calculations are 0-indexed but + // GridSize expects the actual row/col count + return GridSize{longestRow + 1, qMax(col + 1, 3)}; } else { int cols = qMin(qFloor(qSqrt((double)cardCount / 2)), 7); From 70559d32df6578ad48d744eb9cd6d2171e5bb02c Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 30 Nov 2024 05:53:30 -0800 Subject: [PATCH 25/27] fix bug where card view window with single card is too short (#5211) It was a divide by 0 bug lol --- cockatrice/src/game/zones/view_zone.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index f04bda042..0187e8ee5 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -198,7 +198,7 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca return GridSize{longestRow + 1, qMax(col + 1, 3)}; } else { - int cols = qMin(qFloor(qSqrt((double)cardCount / 2)), 7); + int cols = qBound(1, qFloor(qSqrt((double)cardCount / 2)), 7); int rows = qMax(qCeil((double)cardCount / cols), 1); if (minRows == 0) { minRows = rows; From b92047bc3f611dbb659d6e9c2e0e683995fc6778 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 30 Nov 2024 15:54:55 -0800 Subject: [PATCH 26/27] rename and refactor some stuff in ZoneViewWidget (#5213) * fix QComboBox creation order in retranslateUi * move bottom row creation closer to where it's used * rename QGraphicsLinearLayout variables hFilterbox and hPilebox don't make much sense now * add comment about #5204 --- .../src/game/zones/view_zone_widget.cpp | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index d4b5b3b54..e23a714b8 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -43,22 +43,22 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // If the number is < 0, then it means that we can give the option to make the area sorted if (numberCards < 0) { - QGraphicsLinearLayout *hPilebox = new QGraphicsLinearLayout(Qt::Horizontal); - QGraphicsLinearLayout *hFilterbox = new QGraphicsLinearLayout(Qt::Horizontal); + // top row + QGraphicsLinearLayout *hTopRow = new QGraphicsLinearLayout(Qt::Horizontal); // groupBy options QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; groupBySelectorProxy->setWidget(&groupBySelector); groupBySelectorProxy->setZValue(2000000008); - hFilterbox->addItem(groupBySelectorProxy); + hTopRow->addItem(groupBySelectorProxy); // sortBy options QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; sortBySelectorProxy->setWidget(&sortBySelector); sortBySelectorProxy->setZValue(2000000007); - hFilterbox->addItem(sortBySelectorProxy); + hTopRow->addItem(sortBySelectorProxy); - vbox->addItem(hFilterbox); + vbox->addItem(hTopRow); // line QGraphicsProxyWidget *lineProxy = new QGraphicsProxyWidget; @@ -68,20 +68,23 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, lineProxy->setWidget(line); vbox->addItem(lineProxy); + // bottom row + QGraphicsLinearLayout *hBottomRow = new QGraphicsLinearLayout(Qt::Horizontal); + // pile view options QGraphicsProxyWidget *pileViewProxy = new QGraphicsProxyWidget; pileViewProxy->setWidget(&pileViewCheckBox); - hPilebox->addItem(pileViewProxy); + hBottomRow->addItem(pileViewProxy); // shuffle options if (_origZone->getIsShufflable() && numberCards == -1) { shuffleCheckBox.setChecked(true); QGraphicsProxyWidget *shuffleProxy = new QGraphicsProxyWidget; shuffleProxy->setWidget(&shuffleCheckBox); - hPilebox->addItem(shuffleProxy); + hBottomRow->addItem(shuffleProxy); } - vbox->addItem(hPilebox); + vbox->addItem(hBottomRow); } extraHeight = vbox->sizeHint(Qt::PreferredSize).height(); @@ -133,6 +136,8 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, connect(zone, SIGNAL(beingDeleted()), this, SLOT(zoneDeleted())); zone->initializeCards(cardList); + // QLabel sizes aren't taken into account until the widget is rendered. + // Force refresh after 1ms to fix glitchy rendering with long QLabels. auto *lastResizeBeforeVisibleTimer = new QTimer(this); connect(lastResizeBeforeVisibleTimer, &QTimer::timeout, this, [=] { resizeToZoneContents(); @@ -185,6 +190,15 @@ void ZoneViewWidget::retranslateUi() setWindowTitle(zone->getTranslatedName(false, CaseNominative)); { // We can't change the strings after they're put into the QComboBox, so this is our workaround + int oldIndex = groupBySelector.currentIndex(); + groupBySelector.clear(); + groupBySelector.addItem(tr("Group by ---"), CardList::NoSort); + groupBySelector.addItem(tr("Group by Type"), CardList::SortByType); + groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue); + groupBySelector.setCurrentIndex(oldIndex); + } + + { int oldIndex = sortBySelector.currentIndex(); sortBySelector.clear(); sortBySelector.addItem(tr("Sort by ---"), CardList::NoSort); @@ -194,15 +208,6 @@ void ZoneViewWidget::retranslateUi() sortBySelector.setCurrentIndex(oldIndex); } - { - int oldIndex = groupBySelector.currentIndex(); - groupBySelector.clear(); - groupBySelector.addItem(tr("Group by ---"), CardList::NoSort); - groupBySelector.addItem(tr("Group by Type"), CardList::SortByType); - groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue); - groupBySelector.setCurrentIndex(oldIndex); - } - shuffleCheckBox.setText(tr("shuffle when closing")); pileViewCheckBox.setText(tr("pile view")); } From 5156495b47f1081a986b19b72085383457496e14 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:32:39 -0800 Subject: [PATCH 27/27] add more sort options (#5214) * distinguish between groupBy and sortBy options * add more sort options --- cockatrice/src/game/cards/card_list.cpp | 76 ++++++++++++++++++- cockatrice/src/game/cards/card_list.h | 14 +++- .../src/game/zones/view_zone_widget.cpp | 8 +- 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/game/cards/card_list.cpp b/cockatrice/src/game/cards/card_list.cpp index 5d5e5dbef..a4499a98c 100644 --- a/cockatrice/src/game/cards/card_list.cpp +++ b/cockatrice/src/game/cards/card_list.cpp @@ -61,6 +61,55 @@ void CardList::sortBy(const QList &option) std::sort(begin(), end(), comparator); } +/** + * Creates a String for the card such that when sorting cards using that string, it will result in the + * following sort order: + * - Unrecognized colors + * - Land cards + * - Colorless cards + * - Monocolor cards, in wubrg order + * - Monocolor cards of any custom colors + * - 2C cards (no internal order) + * - 3C cards (no internal order) + * - 4C cards (no internal order) + * - 5C cards (no internal order) + * + * @param c The card info + * @param appendAtEnd For multicolor cards, whether to also append the entire color string at the end. + */ +static QString getColorSortString(CardInfoPtr c, bool appendAtEnd) +{ + QString colors = c->getColors(); + switch (colors.size()) { + case 0: { + if (c->getCardType().contains("Land")) { + return "a_land"; + } else { + return "b_colorless"; + } + } + case 1: + // force wubrg order + switch (colors.at(0).toLatin1()) { + case 'W': + return "c_W"; + case 'U': + return "d_U"; + case 'B': + return "e_B"; + case 'R': + return "f_R"; + case 'G': + return "g_G"; + default: + // handle any custom colors + return QString("h_%1").arg(colors.at(0)); + } + default: + return QString("i%1_%2").arg(colors.size()).arg(appendAtEnd ? colors : ""); + } +} + /** * @brief returns the function that extracts the given property from the CardItem. */ @@ -69,13 +118,34 @@ std::function CardList::getExtractorFor(SortOption option) switch (option) { case NoSort: return [](CardItem *) { return ""; }; - case SortByName: - return [](CardItem *c) { return c->getName(); }; - case SortByType: + case SortByMainType: return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getMainCardType() : ""; }; case SortByManaValue: // getCmc returns the int as a string. We pad with 0's so that string comp also works on it return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getCmc().rightJustified(4, '0') : ""; }; + case SortByColorGrouping: + return [](CardItem *c) { return c->getInfo() ? getColorSortString(c->getInfo(), false) : ""; }; + case SortByName: + return [](CardItem *c) { return c->getName(); }; + case SortByType: + return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getCardType() : ""; }; + case SortByManaCost: + return [](CardItem *c) { + auto info = c->getInfo(); + if (!info) + return QString(""); + + // calculation copied from CardDatabaseModel. + // we pad the cmc and also append the mana cost to the end so same cmc cards still have a sort order + return QString("%1%2").arg(info->getCmc(), 4, QChar('0')).arg(info->getManaCost()); + }; + case SortByColors: + return [](CardItem *c) { return c->getInfo() ? getColorSortString(c->getInfo(), true) : ""; }; + case SortByPt: + // do the same padding trick as above + return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getPowTough().rightJustified(10, '0') : ""; }; + case SortBySet: + return [](CardItem *c) { return c->getInfo() ? c->getInfo()->getSetsNames() : ""; }; } // this line should never be reached diff --git a/cockatrice/src/game/cards/card_list.h b/cockatrice/src/game/cards/card_list.h index cbfb3ecff..5b44764d5 100644 --- a/cockatrice/src/game/cards/card_list.h +++ b/cockatrice/src/game/cards/card_list.h @@ -14,9 +14,21 @@ public: enum SortOption { NoSort, + + // Options that are used by groupBy + // Should partition all cards into a reasonable number of buckets + SortByMainType, + SortByManaValue, + SortByColorGrouping, + + // Options that are used by sortBy + // We don't care about buckets; we want as many distinct values as possible. SortByName, SortByType, - SortByManaValue + SortByManaCost, + SortByColors, + SortByPt, + SortBySet }; CardList(bool _contentsKnown); CardItem *findCard(const int cardId) const; diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index e23a714b8..e8276898b 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -193,8 +193,9 @@ void ZoneViewWidget::retranslateUi() int oldIndex = groupBySelector.currentIndex(); groupBySelector.clear(); groupBySelector.addItem(tr("Group by ---"), CardList::NoSort); - groupBySelector.addItem(tr("Group by Type"), CardList::SortByType); + groupBySelector.addItem(tr("Group by Type"), CardList::SortByMainType); groupBySelector.addItem(tr("Group by Mana Value"), CardList::SortByManaValue); + groupBySelector.addItem(tr("Group by Color"), CardList::SortByColorGrouping); groupBySelector.setCurrentIndex(oldIndex); } @@ -204,7 +205,10 @@ void ZoneViewWidget::retranslateUi() sortBySelector.addItem(tr("Sort by ---"), CardList::NoSort); sortBySelector.addItem(tr("Sort by Name"), CardList::SortByName); sortBySelector.addItem(tr("Sort by Type"), CardList::SortByType); - sortBySelector.addItem(tr("Sort by Mana Value"), CardList::SortByManaValue); + sortBySelector.addItem(tr("Sort by Mana Cost"), CardList::SortByManaCost); + sortBySelector.addItem(tr("Sort by Colors"), CardList::SortByColors); + sortBySelector.addItem(tr("Sort by P/T"), CardList::SortByPt); + sortBySelector.addItem(tr("Sort by Set"), CardList::SortBySet); sortBySelector.setCurrentIndex(oldIndex); }