fix: Always prefer local cards if available (#5762)

* Try to better reproduce pre-provider ID behavior

If "override all card art with personal preference" setting is set, look
for custom art for all sets instead of just the most preferred set.

* Warning when using both custom art and the printing selector

* QDirIterator::nextFileInfo is Qt 6.3+

* Translation
This commit is contained in:
Basile Clement 2025-03-26 02:23:09 +01:00 committed by GitHub
parent 91ee6097d2
commit 1ada5ea424
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 305 additions and 205 deletions

File diff suppressed because it is too large Load diff

View file

@ -195,3 +195,31 @@ void PictureLoader::picsPathChanged()
{ {
QPixmapCache::clear(); QPixmapCache::clear();
} }
bool PictureLoader::hasCustomArt()
{
auto picsPath = SettingsCache::instance().getPicsPath();
QDirIterator it(picsPath, QDir::Dirs | QDir::NoDotAndDotDot);
// Check if there is at least one non-directory file in the pics path, other
// than in the "downloadedPics" subdirectory.
while (it.hasNext()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
QFileInfo dir(it.nextFileInfo());
#else
// nextFileInfo() is only available in Qt 6.3+, for previous versions, we build
// the QFileInfo from a QString which requires more system calls.
QFileInfo dir(it.next());
#endif
if (it.fileName() == "downloadedPics")
continue;
QDirIterator subIt(it.filePath(), QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
if (subIt.hasNext()) {
return true;
}
}
return false;
}

View file

@ -36,6 +36,7 @@ public:
static void clearPixmapCache(CardInfoPtr card); static void clearPixmapCache(CardInfoPtr card);
static void clearPixmapCache(); static void clearPixmapCache();
static void cacheCardPixmaps(QList<CardInfoPtr> cards); static void cacheCardPixmaps(QList<CardInfoPtr> cards);
static bool hasCustomArt();
public slots: public slots:
static void clearNetworkCache(); static void clearNetworkCache();

View file

@ -16,11 +16,14 @@ QStringList PictureLoaderWorker::md5Blacklist = QStringList() << "db0c48db407a90
PictureLoaderWorker::PictureLoaderWorker() PictureLoaderWorker::PictureLoaderWorker()
: QObject(nullptr), picsPath(SettingsCache::instance().getPicsPath()), : QObject(nullptr), picsPath(SettingsCache::instance().getPicsPath()),
customPicsPath(SettingsCache::instance().getCustomPicsPath()), customPicsPath(SettingsCache::instance().getCustomPicsPath()),
picDownload(SettingsCache::instance().getPicDownload()), downloadRunning(false), loadQueueRunning(false) picDownload(SettingsCache::instance().getPicDownload()), downloadRunning(false), loadQueueRunning(false),
overrideAllCardArtWithPersonalPreference(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference())
{ {
connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection);
connect(&SettingsCache::instance(), SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); connect(&SettingsCache::instance(), SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this,
&PictureLoaderWorker::setOverrideAllCardArtWithPersonalPreference);
networkManager = new QNetworkAccessManager(this); networkManager = new QNetworkAccessManager(this);
// We need a timeout to ensure requests don't hang indefinitely in case of // We need a timeout to ensure requests don't hang indefinitely in case of
@ -83,11 +86,24 @@ void PictureLoaderWorker::processLoadQueue()
qCDebug(PictureLoaderWorkerLog).nospace() qCDebug(PictureLoaderWorkerLog).nospace()
<< "[card: " << cardName << " set: " << setName << "]: Trying to load picture"; << "[card: " << cardName << " set: " << setName << "]: Trying to load picture";
if (CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting( // FIXME: This is a hack so that to keep old Cockatrice behavior
cardName, cardBeingLoaded.getCard()->getPixmapCacheKey())) { // (ignoring provider ID) when the "override all card art with personal
if (cardImageExistsOnDisk(setName, correctedCardName)) { // preference" is set.
continue; //
} // Figure out a proper way to integrate the two systems at some point.
//
// Note: need to go through a member for
// overrideAllCardArtWithPersonalPreference as reading from the
// SettingsCache instance from the PictureLoaderWorker thread could
// cause race conditions.
//
// XXX: Reading from the CardDatabaseManager instance from the
// PictureLoaderWorker thread might not be safe either
bool searchCustomPics = overrideAllCardArtWithPersonalPreference ||
CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(
cardName, cardBeingLoaded.getCard()->getPixmapCacheKey());
if (searchCustomPics && cardImageExistsOnDisk(setName, correctedCardName, searchCustomPics)) {
continue;
} }
qCDebug(PictureLoaderWorkerLog).nospace() qCDebug(PictureLoaderWorkerLog).nospace()
@ -100,23 +116,26 @@ void PictureLoaderWorker::processLoadQueue()
} }
} }
bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &correctedCardname) bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &correctedCardname, bool searchCustomPics)
{ {
QImage image; QImage image;
QImageReader imgReader; QImageReader imgReader;
imgReader.setDecideFormatFromContent(true); imgReader.setDecideFormatFromContent(true);
QList<QString> picsPaths = QList<QString>(); QList<QString> picsPaths = QList<QString>();
QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
// Recursively check all subdirectories of the CUSTOM folder if (searchCustomPics) {
while (it.hasNext()) { QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
QString thisPath(it.next());
QFileInfo thisFileInfo(thisPath);
if (thisFileInfo.isFile() && // Recursively check all subdirectories of the CUSTOM folder
(thisFileInfo.fileName() == correctedCardname || thisFileInfo.completeBaseName() == correctedCardname || while (it.hasNext()) {
thisFileInfo.baseName() == correctedCardname)) { QString thisPath(it.next());
picsPaths << thisPath; // Card found in the CUSTOM directory, somewhere QFileInfo thisFileInfo(thisPath);
if (thisFileInfo.isFile() &&
(thisFileInfo.fileName() == correctedCardname || thisFileInfo.completeBaseName() == correctedCardname ||
thisFileInfo.baseName() == correctedCardname)) {
picsPaths << thisPath; // Card found in the CUSTOM directory, somewhere
}
} }
} }
@ -451,6 +470,11 @@ void PictureLoaderWorker::picsPathChanged()
customPicsPath = SettingsCache::instance().getCustomPicsPath(); customPicsPath = SettingsCache::instance().getCustomPicsPath();
} }
void PictureLoaderWorker::setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference)
{
overrideAllCardArtWithPersonalPreference = _overrideAllCardArtWithPersonalPreference;
}
void PictureLoaderWorker::clearNetworkCache() void PictureLoaderWorker::clearNetworkCache()
{ {
networkManager->cache()->clear(); networkManager->cache()->clear();

View file

@ -42,8 +42,16 @@ private:
PictureToLoad cardBeingLoaded; PictureToLoad cardBeingLoaded;
PictureToLoad cardBeingDownloaded; PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning; bool picDownload, downloadRunning, loadQueueRunning;
bool overrideAllCardArtWithPersonalPreference;
void startNextPicDownload(); void startNextPicDownload();
bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName);
/** Emit the `imageLoaded` signal and return `true` if a picture is found on
disk, return `false` otherwise.
If `searchCustomPics` is `true`, the CUSTOM folder is searched for a
matching image first; otherwise, only the set-based folders are used. */
bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName, bool searchCustomPics);
bool imageIsBlackListed(const QByteArray &); bool imageIsBlackListed(const QByteArray &);
QNetworkReply *makeRequest(const QUrl &url); QNetworkReply *makeRequest(const QUrl &url);
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl); void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
@ -58,6 +66,7 @@ private slots:
void picDownloadChanged(); void picDownloadChanged();
void picsPathChanged(); void picsPathChanged();
void setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference);
public slots: public slots:
void processLoadQueue(); void processLoadQueue();

View file

@ -1,12 +1,15 @@
#include "printing_selector.h" #include "printing_selector.h"
#include "../../../../settings/cache_settings.h" #include "../../../../settings/cache_settings.h"
#include "../../picture_loader/picture_loader.h"
#include "printing_selector_card_display_widget.h" #include "printing_selector_card_display_widget.h"
#include "printing_selector_card_search_widget.h" #include "printing_selector_card_search_widget.h"
#include "printing_selector_card_selection_widget.h" #include "printing_selector_card_selection_widget.h"
#include "printing_selector_card_sorting_widget.h" #include "printing_selector_card_sorting_widget.h"
#include <QFrame>
#include <QScrollBar> #include <QScrollBar>
#include <qboxlayout.h>
/** /**
* @brief Constructs a PrintingSelector widget to display and manage card printings. * @brief Constructs a PrintingSelector widget to display and manage card printings.
@ -29,6 +32,24 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
layout = new QVBoxLayout(this); layout = new QVBoxLayout(this);
setLayout(layout); setLayout(layout);
if (PictureLoader::hasCustomArt()) {
QFrame *warningFrame = new QFrame(this);
warningFrame->setFrameShape(QFrame::StyledPanel);
warningLabel = new QLabel(this);
warningLabel->setTextFormat(Qt::RichText);
warningLabel->setWordWrap(true);
auto *warningLayout = new QVBoxLayout(warningFrame);
warningFrame->setLayout(warningLayout);
warningLayout->addWidget(warningLabel);
layout->addWidget(warningFrame);
} else {
warningLabel = nullptr;
}
widgetLoadingBufferTimer = new QTimer(this); widgetLoadingBufferTimer = new QTimer(this);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
@ -83,6 +104,12 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
void PrintingSelector::retranslateUi() void PrintingSelector::retranslateUi()
{ {
navigationCheckBox->setText(tr("Display Navigation Buttons")); navigationCheckBox->setText(tr("Display Navigation Buttons"));
if (warningLabel) {
warningLabel->setText(
tr("<b>Warning:</b> You appear to be using custom card art, which has known bugs when also "
"using the printing selector in this version of Cockatrice."));
}
} }
void PrintingSelector::printingsInDeckChanged() void PrintingSelector::printingsInDeckChanged()

View file

@ -46,6 +46,7 @@ private:
QWidget *sortAndOptionsContainer; QWidget *sortAndOptionsContainer;
QHBoxLayout *sortAndOptionsLayout; QHBoxLayout *sortAndOptionsLayout;
QCheckBox *navigationCheckBox; QCheckBox *navigationCheckBox;
QLabel *warningLabel;
PrintingSelectorCardSortingWidget *sortToolBar; PrintingSelectorCardSortingWidget *sortToolBar;
PrintingSelectorCardSearchWidget *searchBar; PrintingSelectorCardSearchWidget *searchBar;
FlowWidget *flowWidget; FlowWidget *flowWidget;

View file

@ -632,7 +632,7 @@ void SettingsCache::setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED
{ {
overrideAllCardArtWithPersonalPreference = static_cast<bool>(_overrideAllCardArt); overrideAllCardArtWithPersonalPreference = static_cast<bool>(_overrideAllCardArt);
settings->setValue("cards/overrideallcardartwithpersonalpreference", overrideAllCardArtWithPersonalPreference); settings->setValue("cards/overrideallcardartwithpersonalpreference", overrideAllCardArtWithPersonalPreference);
emit overrideAllCardArtWithPersonalPreferenceChanged(); emit overrideAllCardArtWithPersonalPreferenceChanged(overrideAllCardArtWithPersonalPreference);
} }
void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T _bumpSetsWithCardsInDeckToTop) void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T _bumpSetsWithCardsInDeckToTop)

View file

@ -55,7 +55,7 @@ signals:
void themeChanged(); void themeChanged();
void picDownloadChanged(); void picDownloadChanged();
void displayCardNamesChanged(); void displayCardNamesChanged();
void overrideAllCardArtWithPersonalPreferenceChanged(); void overrideAllCardArtWithPersonalPreferenceChanged(bool _overrideAllCardArtWithPersonalPreference);
void bumpSetsWithCardsInDeckToTopChanged(); void bumpSetsWithCardsInDeckToTopChanged();
void printingSelectorSortOrderChanged(); void printingSelectorSortOrderChanged();
void printingSelectorCardSizeChanged(); void printingSelectorCardSizeChanged();