mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-07 10:33:24 -08:00
Merge branch 'master' into master
This commit is contained in:
commit
9cfc1450cd
413 changed files with 12837 additions and 3590 deletions
|
|
@ -7,6 +7,7 @@
|
|||
#include <QProgressBar>
|
||||
#include <QtGlobal>
|
||||
#include <climits>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -32,8 +33,7 @@ QString stageName(CardUpdateStage stage, const QString &fallback)
|
|||
|
||||
CardDatabaseUpdateStatusBar::CardDatabaseUpdateStatusBar(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this);
|
||||
|
||||
stageLabel = new QLabel(this);
|
||||
progressBar = new QProgressBar(this);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QImageReader>
|
||||
#include <QList>
|
||||
#include <QMessageLogger>
|
||||
|
|
@ -19,35 +20,62 @@
|
|||
#include <QTimer>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
|
||||
static constexpr int REFRESH_INTERVAL_MS = 10 * 1000;
|
||||
static constexpr int REFRESH_DEBOUNCE_MS = 500;
|
||||
// Watching can fail on network and FUSE mounts; the slow poll still picks those
|
||||
// changes up without the old ten-second full rescan.
|
||||
static constexpr int FALLBACK_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
||||
|
||||
CardPictureLoaderLocal::CardPictureLoaderLocal(QObject *parent)
|
||||
: QObject(parent), picsPath(SettingsCache::instance().paths().getPicsPath()),
|
||||
customPicsPath(SettingsCache::instance().paths().getCustomPicsPath())
|
||||
: CardPictureLoaderLocal(parent,
|
||||
SettingsCache::instance().paths().getPicsPath(),
|
||||
SettingsCache::instance().paths().getCustomPicsPath())
|
||||
{
|
||||
// Hook up signals to settings
|
||||
connect(&SettingsCache::instance().paths(), &PathsSettings::picsPathChanged, this,
|
||||
&CardPictureLoaderLocal::picsPathChanged);
|
||||
}
|
||||
|
||||
CardPictureLoaderLocal::CardPictureLoaderLocal(QObject *parent,
|
||||
const QString &_picsPath,
|
||||
const QString &_customPicsPath)
|
||||
: QObject(parent), picsPath(_picsPath), customPicsPath(_customPicsPath),
|
||||
folderWatcher(new QFileSystemWatcher(this)), indexRefreshTimer(new QTimer(this)),
|
||||
fallbackRefreshTimer(new QTimer(this))
|
||||
{
|
||||
indexRefreshTimer->setSingleShot(true);
|
||||
indexRefreshTimer->setInterval(REFRESH_DEBOUNCE_MS);
|
||||
connect(indexRefreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
|
||||
|
||||
connect(folderWatcher, &QFileSystemWatcher::directoryChanged, indexRefreshTimer, qOverload<>(&QTimer::start));
|
||||
connect(folderWatcher, &QFileSystemWatcher::fileChanged, indexRefreshTimer, qOverload<>(&QTimer::start));
|
||||
|
||||
fallbackRefreshTimer->setInterval(FALLBACK_REFRESH_INTERVAL_MS);
|
||||
connect(fallbackRefreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
|
||||
fallbackRefreshTimer->start();
|
||||
|
||||
refreshIndex();
|
||||
|
||||
refreshTimer = new QTimer(this);
|
||||
connect(refreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
|
||||
refreshTimer->start(REFRESH_INTERVAL_MS);
|
||||
}
|
||||
|
||||
void CardPictureLoaderLocal::refreshIndex()
|
||||
{
|
||||
customFolderIndex.clear();
|
||||
QSet<QString> directories;
|
||||
|
||||
QDir customDir(customPicsPath);
|
||||
if (customDir.exists()) {
|
||||
directories.insert(customDir.absolutePath());
|
||||
}
|
||||
|
||||
QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
|
||||
// Recursively check all subdirectories of the CUSTOM folder
|
||||
while (it.hasNext()) {
|
||||
QString thisPath(it.next());
|
||||
QFileInfo thisFileInfo(thisPath);
|
||||
const QString thisPath = it.next();
|
||||
const QFileInfo thisFileInfo = it.fileInfo();
|
||||
|
||||
if (thisFileInfo.isFile()) {
|
||||
if (thisFileInfo.isDir()) {
|
||||
directories.insert(thisFileInfo.absoluteFilePath());
|
||||
} else if (thisFileInfo.isFile()) {
|
||||
// We don't know which name is the correctedName because there might be '.'s in the cardName.
|
||||
// Just add all possibilities to be sure.
|
||||
customFolderIndex.insert(thisFileInfo.baseName(), thisFileInfo.absoluteFilePath());
|
||||
|
|
@ -55,8 +83,34 @@ void CardPictureLoaderLocal::refreshIndex()
|
|||
}
|
||||
}
|
||||
|
||||
updateWatchedDirectories(directories);
|
||||
|
||||
qCDebug(CardPictureLoaderLocalLog) << "Finished indexing local image folder CUSTOM; map now has"
|
||||
<< customFolderIndex.size() << "entries.";
|
||||
emit indexChanged();
|
||||
}
|
||||
|
||||
void CardPictureLoaderLocal::updateWatchedDirectories(const QSet<QString> &directories)
|
||||
{
|
||||
const QSet<QString> stale = watchedDirectories - directories;
|
||||
if (!stale.isEmpty()) {
|
||||
folderWatcher->removePaths(QStringList(stale.begin(), stale.end()));
|
||||
}
|
||||
|
||||
const QSet<QString> added = directories - watchedDirectories;
|
||||
if (!added.isEmpty()) {
|
||||
const QStringList failed = folderWatcher->addPaths(QStringList(added.begin(), added.end()));
|
||||
if (!failed.isEmpty()) {
|
||||
qCDebug(CardPictureLoaderLocalLog) << "Could not watch custom picture directories:" << failed;
|
||||
}
|
||||
}
|
||||
|
||||
watchedDirectories = directories;
|
||||
}
|
||||
|
||||
QStringList CardPictureLoaderLocal::indexedPaths(const QString &baseName) const
|
||||
{
|
||||
return customFolderIndex.values(baseName);
|
||||
}
|
||||
|
||||
QImage CardPictureLoaderLocal::tryLoad(const ExactCard &toLoad) const
|
||||
|
|
@ -145,4 +199,5 @@ void CardPictureLoaderLocal::picsPathChanged()
|
|||
{
|
||||
picsPath = SettingsCache::instance().paths().getPicsPath();
|
||||
customPicsPath = SettingsCache::instance().paths().getCustomPicsPath();
|
||||
refreshIndex();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
#include <QLoggingCategory>
|
||||
#include <QMultiHash>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class ExactCard;
|
||||
class QFileSystemWatcher;
|
||||
class QTimer;
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CardPictureLoaderLocalLog, "card_picture_loader.local");
|
||||
|
|
@ -40,6 +42,18 @@ public:
|
|||
*/
|
||||
explicit CardPictureLoaderLocal(QObject *parent);
|
||||
|
||||
/**
|
||||
* @brief Constructs the loader against explicit folders without SettingsCache.
|
||||
*
|
||||
* Used by tests and by callers that already resolved the configured paths.
|
||||
*/
|
||||
CardPictureLoaderLocal(QObject *parent, const QString &picsPath, const QString &customPicsPath);
|
||||
|
||||
/**
|
||||
* @brief Returns every indexed file whose base name matches @p baseName.
|
||||
*/
|
||||
[[nodiscard]] QStringList indexedPaths(const QString &baseName) const;
|
||||
|
||||
/**
|
||||
* @brief Attempts to load a card image from local disk or custom folders.
|
||||
* @param toLoad ExactCard object representing the card to load.
|
||||
|
|
@ -54,7 +68,10 @@ private:
|
|||
QString customPicsPath; ///< Path to custom card image folder
|
||||
|
||||
QMultiHash<QString, QString> customFolderIndex; ///< Multimap from cardName to file paths in CUSTOM folder
|
||||
QTimer *refreshTimer; ///< Timer for periodic folder index refresh
|
||||
QFileSystemWatcher *folderWatcher; ///< Watches the custom folder tree for changes
|
||||
QTimer *indexRefreshTimer; ///< Debounces bursts of filesystem events
|
||||
QTimer *fallbackRefreshTimer; ///< Slow poll for filesystems without change notification
|
||||
QSet<QString> watchedDirectories; ///< Directories currently registered with folderWatcher
|
||||
|
||||
/**
|
||||
* @brief Rebuilds the index of the custom image folder.
|
||||
|
|
@ -65,6 +82,11 @@ private:
|
|||
*/
|
||||
void refreshIndex();
|
||||
|
||||
/**
|
||||
* @brief Keeps the watcher registered on exactly @p directories.
|
||||
*/
|
||||
void updateWatchedDirectories(const QSet<QString> &directories);
|
||||
|
||||
/**
|
||||
* @brief Attempts to load a card image from disk given its set and name info.
|
||||
* @param setName Corrected short name of the card's set.
|
||||
|
|
@ -82,6 +104,12 @@ private:
|
|||
const QString &collectorNumber,
|
||||
const QString &providerId) const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted after the custom folder index has been rebuilt.
|
||||
*/
|
||||
void indexChanged();
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Updates internal paths when the user changes picture settings.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <compare>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/cache_storage_settings.h>
|
||||
#include <libcockatrice/settings/download_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
|
|
@ -46,11 +47,10 @@ CardPictureLoaderWorker::CardPictureLoaderWorker()
|
|||
: QObject(nullptr), picDownload(SettingsCache::instance().downloads().getPicDownload()),
|
||||
hostRequestLimits(SettingsCache::instance().downloads().getHostRequestLimits())
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
networkManager = NetworkManagerFactory::create(this);
|
||||
// We need a timeout to ensure requests don't hang indefinitely in case of
|
||||
// cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397
|
||||
// Use Qt's default timeout (30s, as of 2023-02-22)
|
||||
networkManager->setTransferTimeout();
|
||||
cache = new QNetworkDiskCache(this);
|
||||
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
|
||||
cache->setMaximumCacheSize(1024L * 1024L *
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/download_settings.h>
|
||||
#include <utility>
|
||||
|
||||
CardPictureToLoad::CardPictureToLoad(const ExactCard &_card)
|
||||
: card(_card), urlTemplates(SettingsCache::instance().downloads().getAllURLs())
|
||||
|
|
@ -74,19 +75,20 @@ QList<CardSetPtr> CardPictureToLoad::extractSetsSorted(const ExactCard &card)
|
|||
//! \todo Check if going through all sets is still necessary after the ExactCard refactor.
|
||||
static PrintingInfo findPrintingForSet(const ExactCard &card, const QString &setName)
|
||||
{
|
||||
SetToPrintingsMap setsToPrintings = card.getInfo().getSets();
|
||||
const auto &setsToPrintings = card.getInfo().getSets();
|
||||
|
||||
if (!setsToPrintings.contains(setName)) {
|
||||
const auto setPrintings = setsToPrintings.constFind(setName);
|
||||
if (setPrintings == setsToPrintings.constEnd() || setPrintings.value().isEmpty()) {
|
||||
return PrintingInfo();
|
||||
}
|
||||
|
||||
for (const auto &printing : setsToPrintings[setName]) {
|
||||
for (const auto &printing : setPrintings.value()) {
|
||||
if (printing.getUuid() == card.getPrinting().getUuid()) {
|
||||
return printing;
|
||||
}
|
||||
}
|
||||
|
||||
return setsToPrintings[setName][0];
|
||||
return setPrintings.value().first();
|
||||
}
|
||||
|
||||
void CardPictureToLoad::populateSetUrls()
|
||||
|
|
@ -183,10 +185,14 @@ static int parse(const QString &urlTemplate,
|
|||
{
|
||||
static const QRegularExpression rxFillWith("^(.+)_fill_with_(.+)$");
|
||||
static const QRegularExpression rxSubStr("^(.+)_substr_(\\d+)_(\\d+)$");
|
||||
static const QRegularExpression rxCardProp("!prop:([^!]+)!");
|
||||
static const QRegularExpression rxCardSet("!set:([^!]+)!");
|
||||
|
||||
const QRegularExpression rxCardProp("!" + propType + ":([^!]+)!");
|
||||
// The property type only takes two values, so keep the expression that was
|
||||
// compiled for it instead of rebuilding one per URL template.
|
||||
const QRegularExpression &rxCardPropSelector = propType == "prop" ? rxCardProp : rxCardSet;
|
||||
|
||||
auto matches = rxCardProp.globalMatch(urlTemplate);
|
||||
auto matches = rxCardPropSelector.globalMatch(urlTemplate);
|
||||
while (matches.hasNext()) {
|
||||
auto match = matches.next();
|
||||
QString templatePropertyName = match.captured(1);
|
||||
|
|
@ -303,10 +309,10 @@ QString CardPictureToLoad::transformUrl(const QString &urlTemplate) const
|
|||
}
|
||||
|
||||
QString transformedUrl = urlTemplate;
|
||||
for (const QString &prop : transformMap.keys()) {
|
||||
for (const auto &[prop, value] : std::as_const(transformMap).asKeyValueRange()) {
|
||||
if (transformedUrl.contains(prop)) {
|
||||
if (!transformMap[prop].isEmpty()) {
|
||||
transformedUrl.replace(prop, QUrl::toPercentEncoding(transformMap[prop]));
|
||||
if (!value.isEmpty()) {
|
||||
transformedUrl.replace(prop, QUrl::toPercentEncoding(value));
|
||||
} else {
|
||||
/* This means the template is requesting information that is not
|
||||
* populated in this card, so it should return an empty string,
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
#include "deck_file_format.h"
|
||||
|
||||
#include <qnamespace.h>
|
||||
|
||||
DeckFileFormat::Format DeckFileFormat::getFormatFromName(const QString &fileName)
|
||||
{
|
||||
if (fileName.endsWith(".cod", Qt::CaseInsensitive)) {
|
||||
return Cockatrice;
|
||||
}
|
||||
return PlainText;
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#ifndef COCKATRICE_DECK_FILE_FORMAT_H
|
||||
#define COCKATRICE_DECK_FILE_FORMAT_H
|
||||
#include <QString>
|
||||
|
||||
namespace DeckFileFormat
|
||||
{
|
||||
|
||||
/**
|
||||
* The deck file formats that Cockatrice supports.
|
||||
*/
|
||||
enum Format
|
||||
{
|
||||
/**
|
||||
* Plaintext deck files, a format that is intended to be widely supported among different programs.
|
||||
* This format does not support Cockatrice specific features such as banner cards or tags.
|
||||
*/
|
||||
PlainText,
|
||||
|
||||
/**
|
||||
* This is Cockatrice's native deck file format, and supports deck metadata such as banner cards and tags.
|
||||
* Stored as .cod files.
|
||||
*/
|
||||
Cockatrice
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines what deck file format the given filename corresponds to.
|
||||
*
|
||||
* @param fileName The filename
|
||||
* @return The deck format
|
||||
*/
|
||||
Format getFormatFromName(const QString &fileName);
|
||||
|
||||
} // namespace DeckFileFormat
|
||||
|
||||
#endif // COCKATRICE_DECK_FILE_FORMAT_H
|
||||
|
|
@ -7,13 +7,14 @@
|
|||
#ifndef DECK_LOADER_H
|
||||
#define DECK_LOADER_H
|
||||
|
||||
#include "deck_file_format.h"
|
||||
#include "loaded_deck.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QObject>
|
||||
#include <QPrinter>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTextCursor>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <optional>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
#include "loaded_deck.h"
|
||||
|
||||
bool LoadedDeck::LoadInfo::isEmpty() const
|
||||
{
|
||||
return fileName.isEmpty() && remoteDeckId == NON_REMOTE_ID;
|
||||
}
|
||||
|
||||
bool LoadedDeck::isEmpty() const
|
||||
{
|
||||
return deckList.isEmpty() && lastLoadInfo.isEmpty();
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#ifndef COCKATRICE_LOADED_DECK_H
|
||||
#define COCKATRICE_LOADED_DECK_H
|
||||
|
||||
#include "deck_file_format.h"
|
||||
#include "libcockatrice/deck_list/deck_list.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* @brief Represents a deck that was loaded from somewhere.
|
||||
* Contains the DeckList itself, as well as info about where it was loaded from.
|
||||
*/
|
||||
struct LoadedDeck
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Information about where the deck was loaded from.
|
||||
*
|
||||
* For local decks, the remoteDeckId field will always be -1.
|
||||
* For remote decks, fileName will be empty and fileFormat will always be CockatriceFormat
|
||||
*/
|
||||
struct LoadInfo
|
||||
{
|
||||
static constexpr int NON_REMOTE_ID = -1;
|
||||
|
||||
QString fileName = "";
|
||||
DeckFileFormat::Format fileFormat = DeckFileFormat::Cockatrice;
|
||||
int remoteDeckId = NON_REMOTE_ID;
|
||||
|
||||
bool isEmpty() const;
|
||||
};
|
||||
|
||||
DeckList deckList; ///< The decklist itself
|
||||
LoadInfo lastLoadInfo = {}; ///< info about where the deck was loaded from
|
||||
|
||||
bool isEmpty() const;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_LOADED_DECK_H
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
#include "intent_open_local_deck.h"
|
||||
|
||||
#include "../deck_loader/deck_file_format.h"
|
||||
#include "../deck_loader/deck_loader.h"
|
||||
#include "../deck_loader/loaded_deck.h"
|
||||
#include "../widgets/tabs/tab_supervisor.h"
|
||||
#include "intent_wait_for_database_load.h"
|
||||
#include "libcockatrice/card/database/card_database.h"
|
||||
#include "libcockatrice/card/database/card_database_loader.h"
|
||||
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <optional>
|
||||
|
||||
IntentOpenLocalDeck::IntentOpenLocalDeck(TabSupervisor *_tabSupervisor, const QString &_file)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <libcockatrice/protocol/pb/response_deck_share_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_deck_share_item.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <optional>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
|
@ -184,13 +185,11 @@ void IntentOpenSharedDeck::onItemFailure(const QString &reason)
|
|||
}
|
||||
|
||||
const int downloadedCount = loadedDecks.size();
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
previewDialog, tr("Open shared decks"),
|
||||
tr("Could not download the deck \"%1\".\n\n%n deck(s) were already downloaded. Open them?", "", downloadedCount)
|
||||
.arg(itemNames.value(currentItemId, tr("Unknown deck"))),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
if (answer == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(previewDialog, tr("Open shared decks"),
|
||||
tr("Could not download the deck \"%1\".\n\n%n deck(s) were already downloaded. Open them?", "",
|
||||
downloadedCount)
|
||||
.arg(itemNames.value(currentItemId, tr("Unknown deck"))),
|
||||
QMessageBox::Yes)) {
|
||||
finishAll();
|
||||
} else {
|
||||
previewDialog->deleteLater();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef COCKATRICE_INTENT_OPEN_SHARED_DECK_H
|
||||
#define COCKATRICE_INTENT_OPEN_SHARED_DECK_H
|
||||
|
||||
#include "../deck_loader/loaded_deck.h"
|
||||
#include "contexts/context_open_deck.h"
|
||||
#include "intent.h"
|
||||
|
||||
|
|
@ -9,6 +8,7 @@
|
|||
#include <QMap>
|
||||
#include <QScopedPointer>
|
||||
#include <QString>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <memory>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/settings/servers_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <memory>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -115,9 +116,7 @@ Intent *IntentUrlParser::createJoinGameIntent(const QUrlQuery &query, PendingInt
|
|||
const QString gameDescription = query.queryItemValue("game", QUrl::FullyDecoded);
|
||||
const QString message = generateJoinGameMessage(*ctx, gameDescription);
|
||||
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
mainWindow, tr("Join game"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
if (!QtUtils::confirm(mainWindow, tr("Join game"), message, QMessageBox::Yes)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -231,11 +230,10 @@ Intent *IntentUrlParser::createOpenDeckIntent(const QUrlQuery &query, PendingInt
|
|||
if (client->getStatus() == StatusLoggedIn) {
|
||||
const QString current =
|
||||
QStringLiteral("%1:%2").arg(client->serverName(), QString::number(client->serverPort()));
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
mainWindow, tr("Open shared deck"),
|
||||
tr("Opening this share link connects you to %1 instead of %2.\n\nContinue?").arg(target, current),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
if (!QtUtils::confirm(
|
||||
mainWindow, tr("Open shared deck"),
|
||||
tr("Opening this share link connects you to %1 instead of %2.\n\nContinue?").arg(target, current),
|
||||
QMessageBox::Yes)) {
|
||||
return nullptr;
|
||||
}
|
||||
chain.migrationTargetHost = ctx->serverContext.hostname;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFlags>
|
||||
#include <QIODevice>
|
||||
#include <QLocale>
|
||||
|
|
@ -18,8 +20,11 @@
|
|||
|
||||
#define LOGGER_MAX_ENTRIES 128
|
||||
#define LOGGER_FILENAME "qdebug.txt"
|
||||
#define LOGGER_SESSION_MARKER "Log session started at"
|
||||
#define LOGGER_PERSISTENT_MAX_BYTES (512 * 1024)
|
||||
#define LOGGER_PERSISTENT_KEEP_LINES 2000
|
||||
|
||||
Logger::Logger() : logToFileEnabled(false)
|
||||
Logger::Logger() : logToFileEnabled(false), persistentLogEnabled(false)
|
||||
{
|
||||
logBuffer.append(getClientVersion());
|
||||
logBuffer.append(getSystemArchitecture());
|
||||
|
|
@ -35,6 +40,7 @@ Logger::Logger() : logToFileEnabled(false)
|
|||
Logger::~Logger()
|
||||
{
|
||||
closeLogfileSession();
|
||||
closePersistentLogSession();
|
||||
logBuffer.clear();
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +88,137 @@ void Logger::closeLogfileSession()
|
|||
fileHandle.close();
|
||||
}
|
||||
|
||||
void Logger::setPersistentLogPath(const QString &path)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (path.isEmpty() || (persistentLogEnabled && persistentLogPath == path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDir().mkpath(QFileInfo(path).absolutePath());
|
||||
persistentLogPath = path;
|
||||
openPersistentLog(path);
|
||||
}
|
||||
|
||||
void Logger::prunePersistentLog(const QString &path)
|
||||
{
|
||||
QFileInfo info(path);
|
||||
if (!info.exists() || info.size() <= LOGGER_PERSISTENT_MAX_BYTES) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
QList<QString> allLines;
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
allLines.append(in.readLine());
|
||||
}
|
||||
file.close();
|
||||
|
||||
const int start =
|
||||
allLines.size() > LOGGER_PERSISTENT_KEEP_LINES ? allLines.size() - LOGGER_PERSISTENT_KEEP_LINES : 0;
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
QTextStream out(&file);
|
||||
for (int i = start; i < allLines.size(); ++i) {
|
||||
out << allLines.at(i) << Qt::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void Logger::openPersistentLog(const QString &path)
|
||||
{
|
||||
prunePersistentLog(path);
|
||||
|
||||
persistentFileHandle.setFileName(path);
|
||||
if (!persistentFileHandle.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||
qWarning() << "Logger failed to open persistent log" << path << "for writing";
|
||||
return;
|
||||
}
|
||||
persistentFileStream.setDevice(&persistentFileHandle);
|
||||
persistentFileStream << LOGGER_SESSION_MARKER << " " << QDateTime::currentDateTime().toString() << Qt::endl;
|
||||
persistentFileStream << getClientVersion() << Qt::endl;
|
||||
persistentFileStream << getSystemArchitecture() << Qt::endl;
|
||||
persistentFileStream << getClientInstallInfo() << Qt::endl;
|
||||
persistentFileStream.flush();
|
||||
persistentLogEnabled = true;
|
||||
}
|
||||
|
||||
void Logger::closePersistentLogSession()
|
||||
{
|
||||
if (!persistentLogEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
persistentLogEnabled = false;
|
||||
persistentFileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << Qt::endl;
|
||||
persistentFileStream.flush();
|
||||
persistentFileHandle.close();
|
||||
}
|
||||
|
||||
QList<QString> Logger::getPreviousSessionTail(int maxLines) const
|
||||
{
|
||||
QString path;
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
path = persistentLogPath;
|
||||
}
|
||||
if (path.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
return extractPreviousSessionTail(path, maxLines);
|
||||
}
|
||||
|
||||
QList<QString> Logger::extractPreviousSessionTail(const QString &filePath, int maxLines)
|
||||
{
|
||||
QList<QString> result;
|
||||
if (maxLines <= 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return result;
|
||||
}
|
||||
QList<QString> allLines;
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
allLines.append(in.readLine());
|
||||
}
|
||||
file.close();
|
||||
|
||||
QList<int> markerIndexes;
|
||||
for (int i = 0; i < allLines.size(); ++i) {
|
||||
if (allLines.at(i).contains(QLatin1String(LOGGER_SESSION_MARKER))) {
|
||||
markerIndexes.append(i);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QString> tail;
|
||||
if (markerIndexes.isEmpty()) {
|
||||
tail = allLines;
|
||||
} else if (markerIndexes.size() == 1) {
|
||||
// Only the current session has been recorded; there is no previous session yet.
|
||||
return result;
|
||||
} else {
|
||||
const int previousStart = markerIndexes.at(markerIndexes.size() - 2);
|
||||
const int currentStart = markerIndexes.last();
|
||||
for (int i = previousStart; i < currentStart; ++i) {
|
||||
tail.append(allLines.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
const int start = tail.size() > maxLines ? tail.size() - maxLines : 0;
|
||||
for (int i = start; i < tail.size(); ++i) {
|
||||
result.append(tail.at(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Logger::log(QtMsgType /* type */, const QMessageLogContext & /* ctx */, const QString &message)
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "internalLog", Qt::QueuedConnection, Q_ARG(const QString &, message));
|
||||
|
|
@ -102,6 +239,11 @@ void Logger::internalLog(const QString &message)
|
|||
if (logToFileEnabled) {
|
||||
fileStream << message << Qt::endl; // Print to fileStream
|
||||
}
|
||||
|
||||
if (persistentLogEnabled) {
|
||||
persistentFileStream << message << Qt::endl;
|
||||
persistentFileStream.flush();
|
||||
}
|
||||
}
|
||||
|
||||
QString Logger::getSystemArchitecture()
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ public:
|
|||
{
|
||||
return logBuffer;
|
||||
}
|
||||
void setPersistentLogPath(const QString &path);
|
||||
QList<QString> getPreviousSessionTail(int maxLines = 60) const;
|
||||
static QList<QString> extractPreviousSessionTail(const QString &filePath, int maxLines = 60);
|
||||
|
||||
private:
|
||||
Logger();
|
||||
|
|
@ -61,11 +64,18 @@ private:
|
|||
QTextStream fileStream;
|
||||
QFile fileHandle;
|
||||
QList<QString> logBuffer;
|
||||
QMutex mutex;
|
||||
mutable QMutex mutex;
|
||||
QString persistentLogPath;
|
||||
bool persistentLogEnabled;
|
||||
QTextStream persistentFileStream;
|
||||
QFile persistentFileHandle;
|
||||
|
||||
protected:
|
||||
void openLogfileSession();
|
||||
void closeLogfileSession();
|
||||
void openPersistentLog(const QString &path);
|
||||
void prunePersistentLog(const QString &path);
|
||||
void closePersistentLogSession();
|
||||
|
||||
protected slots:
|
||||
void internalLog(const QString &message);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <initializer_list>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QString &_themeName, QWidget *parent)
|
||||
|
|
@ -57,9 +58,7 @@ PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QSt
|
|||
|
||||
void PaletteEditorDialog::setupUi()
|
||||
{
|
||||
auto *root = new QVBoxLayout(this);
|
||||
root->setSpacing(0);
|
||||
root->setContentsMargins(0, 0, 0, 0);
|
||||
auto *root = QtUtils::makeLayout<QVBoxLayout>(this, 0, 0);
|
||||
|
||||
// Header
|
||||
header = new QWidget;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -65,8 +66,7 @@ static const QMap<AppColor::Role, const char *> APP_ROLE_DESCRIPTIONS = {
|
|||
|
||||
PaletteGridWidget::PaletteGridWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
scroll = new QScrollArea(this);
|
||||
scroll->setWidgetResizable(true);
|
||||
scroll = QtUtils::makeScrollArea(this, Qt::ScrollBarAsNeeded, Qt::ScrollBarAsNeeded);
|
||||
scroll->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
gridHost = new QWidget;
|
||||
|
|
@ -74,8 +74,7 @@ PaletteGridWidget::PaletteGridWidget(QWidget *parent) : QWidget(parent)
|
|||
refreshChromePalettes();
|
||||
scroll->setWidget(gridHost);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QVBoxLayout>(this);
|
||||
layout->addWidget(scroll);
|
||||
}
|
||||
|
||||
|
|
@ -102,9 +101,7 @@ void PaletteGridWidget::buildGrid(QWidget *host)
|
|||
auto *label = new QLabel(groupHeaders[col], host);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setToolTip(groupTips[col]);
|
||||
QFont f = label->font();
|
||||
f.setBold(true);
|
||||
label->setFont(f);
|
||||
label->setFont(QtUtils::boldFont(label->font()));
|
||||
label->setAutoFillBackground(true);
|
||||
label->setContentsMargins(4, 4, 4, 4);
|
||||
grid->addWidget(label, 0, col + 1);
|
||||
|
|
@ -149,9 +146,7 @@ void PaletteGridWidget::buildGrid(QWidget *host)
|
|||
|
||||
auto *appHeader = new QLabel(tr("App colors"), host);
|
||||
appHeader->setToolTip(tr("Application-specific colors layered on top of the Qt palette"));
|
||||
QFont appHeaderFont = appHeader->font();
|
||||
appHeaderFont.setBold(true);
|
||||
appHeader->setFont(appHeaderFont);
|
||||
appHeader->setFont(QtUtils::boldFont(appHeader->font()));
|
||||
appHeader->setAutoFillBackground(true);
|
||||
appHeader->setContentsMargins(4, 4, 4, 4);
|
||||
grid->addWidget(appHeader, appHeaderRow, 0, 1, 4);
|
||||
|
|
|
|||
|
|
@ -558,6 +558,9 @@ void ThemeManager::applyStyleAndPalette(const QString &themeName,
|
|||
widget->update();
|
||||
}
|
||||
|
||||
// Palette-only previews do not run themeChangedSlot(), so drop snapshots
|
||||
// generated under the previous palette here as well.
|
||||
clearPixmapGeneratorCaches();
|
||||
emit paletteChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@
|
|||
ColorIdentityWidget::ColorIdentityWidget(QWidget *parent, const QString &_colorIdentity)
|
||||
: QWidget(parent), colorIdentity(_colorIdentity)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(5); // Small spacing between icons
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this, 5);
|
||||
layout->setAlignment(Qt::AlignCenter); // Ensure icons are centered
|
||||
setLayout(layout);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@
|
|||
#include <QResizeEvent>
|
||||
#include <QSize>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
ManaCostWidget::ManaCostWidget(QWidget *parent, CardInfoPtr _card) : QWidget(parent), card(_card)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(5); // Small spacing between icons
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this, 5);
|
||||
setLayout(layout);
|
||||
|
||||
setFixedHeight(50); // Fixed height
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
class QWidget;
|
||||
|
||||
|
|
@ -30,9 +31,8 @@ CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *pa
|
|||
text->setObjectName("text");
|
||||
connect(text, &CardInfoTextWidget::linkActivated, this, [this](const QString &card) { setCard({card}); });
|
||||
|
||||
auto *layout = new QVBoxLayout();
|
||||
auto *layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
layout->setObjectName("layout");
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(pic, 0, Qt::AlignCenter);
|
||||
layout->addWidget(text, 0, Qt::AlignCenter);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
struct CardRef;
|
||||
|
|
@ -49,15 +50,13 @@ CardInfoFrameWidget::CardInfoFrameWidget(QWidget *parent)
|
|||
insertTab(ImageAndTextView, tab3, QString());
|
||||
connect(this, &CardInfoFrameWidget::currentChanged, this, &CardInfoFrameWidget::setViewMode);
|
||||
|
||||
tab1Layout = new QVBoxLayout();
|
||||
tab1Layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
tab1Layout->setObjectName("tab1Layout");
|
||||
tab1Layout->setContentsMargins(0, 0, 0, 0);
|
||||
tab1Layout->setSpacing(0);
|
||||
tab1->setLayout(tab1Layout);
|
||||
|
||||
tab2Layout = new QVBoxLayout();
|
||||
tab2Layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
tab2Layout->setObjectName("tab2Layout");
|
||||
tab2Layout->setContentsMargins(0, 0, 0, 0);
|
||||
tab2Layout->setSpacing(0);
|
||||
tab2->setLayout(tab2Layout);
|
||||
|
||||
|
|
@ -65,9 +64,8 @@ CardInfoFrameWidget::CardInfoFrameWidget(QWidget *parent)
|
|||
splitter->setObjectName("splitter");
|
||||
splitter->setOrientation(Qt::Vertical);
|
||||
|
||||
tab3Layout = new QVBoxLayout();
|
||||
tab3Layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
tab3Layout->setObjectName("tab3Layout");
|
||||
tab3Layout->setContentsMargins(0, 0, 0, 0);
|
||||
tab3Layout->setSpacing(0);
|
||||
tab3Layout->addWidget(splitter);
|
||||
tab3->setLayout(tab3Layout);
|
||||
|
|
|
|||
|
|
@ -491,7 +491,14 @@ void CardInfoPictureWidget::showEnlargedPixmap()
|
|||
enlargedPixmapWidget->setCardPixmap(exactCard, enlargedSize);
|
||||
|
||||
const QPoint cursorPos = QCursor::pos();
|
||||
const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry();
|
||||
QScreen *screen = QGuiApplication::screenAt(cursorPos);
|
||||
if (!screen) {
|
||||
screen = QGuiApplication::primaryScreen();
|
||||
}
|
||||
if (!screen) {
|
||||
return;
|
||||
}
|
||||
const QRect screenGeometry = screen->geometry();
|
||||
const QSize widgetSize = enlargedPixmapWidget->size();
|
||||
|
||||
int newX = cursorPos.x() + ENLARGED_PIXMAP_OFFSET;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <QWidget>
|
||||
#include <libcockatrice/card/game_specific_terms.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobjectdefs.h>
|
||||
|
||||
|
|
@ -33,9 +34,7 @@ CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent)
|
|||
textLabel->setReadOnly(true);
|
||||
textLabel->setMinimumSize(35, 35);
|
||||
|
||||
propsScroll = new QScrollArea(this);
|
||||
propsScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
propsScroll->setWidgetResizable(true);
|
||||
propsScroll = QtUtils::makeScrollArea(this);
|
||||
propsScroll->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
propsScroll->setContentsMargins(0, 0, 0, 0);
|
||||
propsScroll->setFrameStyle(QFrame::NoFrame);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/models/deck_list/deck_list_model.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class CardSizeWidget;
|
||||
|
|
@ -179,15 +180,7 @@ void DeckCardZoneDisplayWidget::displayCards()
|
|||
void DeckCardZoneDisplayWidget::refreshDisplayType(const DisplayType &_displayType)
|
||||
{
|
||||
displayType = _displayType;
|
||||
QLayoutItem *item;
|
||||
while ((item = cardGroupLayout->takeAt(0)) != nullptr) {
|
||||
if (item->widget()) {
|
||||
item->widget()->deleteLater();
|
||||
} else if (item->layout()) {
|
||||
item->layout()->deleteLater();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(cardGroupLayout);
|
||||
|
||||
indexToWidgetMap.clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <QWidget>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/utility/color.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -47,12 +48,7 @@ ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListStatisticsAnalyzer *anal
|
|||
void ManaBaseWidget::updateDisplay()
|
||||
{
|
||||
// Clear previous widgets
|
||||
while (QLayoutItem *item = barLayout->takeAt(0)) {
|
||||
if (item->widget()) {
|
||||
item->widget()->deleteLater();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(barLayout);
|
||||
|
||||
auto &pipCount = analyzer->getProductionPipCount();
|
||||
auto &cardCount = analyzer->getProductionCardCount();
|
||||
|
|
|
|||
|
|
@ -16,14 +16,13 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QVector>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
constexpr int MIN_ROW_HEIGHT = 100; // Minimum readable height per row
|
||||
|
||||
ManaCurveCategoryWidget::ManaCurveCategoryWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(4);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QVBoxLayout>(this, 0, 4);
|
||||
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
}
|
||||
|
|
@ -95,9 +94,7 @@ void ManaCurveCategoryWidget::updateDisplay(int minCmc,
|
|||
|
||||
row->setFixedHeight(MIN_ROW_HEIGHT);
|
||||
|
||||
QHBoxLayout *rowLayout = new QHBoxLayout(row);
|
||||
rowLayout->setContentsMargins(0, 0, 0, 0);
|
||||
rowLayout->setSpacing(4);
|
||||
QHBoxLayout *rowLayout = QtUtils::makeLayout<QHBoxLayout>(row, 4);
|
||||
|
||||
QLabel *categoryLabel = new QLabel(cat, row);
|
||||
categoryLabel->setFixedWidth(80);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -47,12 +48,7 @@ ManaDevotionWidget::ManaDevotionWidget(QWidget *parent, DeckListStatisticsAnalyz
|
|||
void ManaDevotionWidget::updateDisplay()
|
||||
{
|
||||
// Clear previous widgets
|
||||
while (QLayoutItem *item = barLayout->takeAt(0)) {
|
||||
if (item->widget()) {
|
||||
item->widget()->deleteLater();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(barLayout);
|
||||
|
||||
auto &pipCount = analyzer->getDevotionPipCount();
|
||||
auto &cardCount = analyzer->getDevotionCardCount();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <QSettings>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnalyzer *_statsAnalyzer)
|
||||
|
|
@ -54,8 +55,7 @@ DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnal
|
|||
connect(includeSideboardCheckBox, &QCheckBox::clicked, this, &DeckAnalyticsWidget::includeSideboardChanged);
|
||||
|
||||
// Scroll area and container
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea = QtUtils::makeScrollArea(this, Qt::ScrollBarAsNeeded, Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
panelContainer = new QWidget(scrollArea);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWidget *analyticsPanel, QWidget *parent)
|
||||
|
|
@ -35,18 +36,14 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid
|
|||
{
|
||||
setAcceptDrops(true);
|
||||
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
auto *mainLayout = QtUtils::makeLayout<QVBoxLayout>(this, 0);
|
||||
|
||||
// Frame for selection highlight
|
||||
frame = new QFrame(this);
|
||||
frame->setFrameShape(QFrame::Box);
|
||||
frame->setLineWidth(2);
|
||||
|
||||
auto *frameLayout = new QVBoxLayout(frame);
|
||||
frameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
frameLayout->setSpacing(0);
|
||||
auto *frameLayout = QtUtils::makeLayout<QVBoxLayout>(frame, 0);
|
||||
|
||||
// Add the analytics panel
|
||||
frameLayout->addWidget(analyticsPanel);
|
||||
|
|
@ -63,9 +60,7 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid
|
|||
|
||||
// Bottom bar with drag button and resize handle
|
||||
auto *bottomBar = new QWidget(frame);
|
||||
auto *bottomLayout = new QHBoxLayout(bottomBar);
|
||||
bottomLayout->setContentsMargins(0, 0, 0, 0);
|
||||
bottomLayout->setSpacing(0);
|
||||
auto *bottomLayout = QtUtils::makeLayout<QHBoxLayout>(bottomBar, 0);
|
||||
|
||||
// Drag button on the left
|
||||
dragButton = new QPushButton("☰", bottomBar);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
DeckEditorFilterDockWidget::DeckEditorFilterDockWidget(AbstractTabDeckEditor *parent)
|
||||
|
|
@ -80,9 +81,8 @@ void DeckEditorFilterDockWidget::createFiltersDock()
|
|||
filterDelAll->setDefaultAction(aClearFilterAll);
|
||||
filterDelAll->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
|
||||
auto *filterLayout = new QGridLayout;
|
||||
auto *filterLayout = QtUtils::makeLayout<QGridLayout>();
|
||||
filterLayout->setObjectName("filterLayout");
|
||||
filterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
filterLayout->addWidget(filterBuilder, 0, 0, 1, 3);
|
||||
filterLayout->addWidget(filterView, 1, 0, 1, 3);
|
||||
filterLayout->addWidget(filterDelOne, 2, 0, 1, 1);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "deck_state_manager.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../deck_loader/loaded_deck.h"
|
||||
#include "deck_list_model.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
|
|
@ -18,6 +17,7 @@
|
|||
#include <QVariant>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_list_history_manager.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef COCKATRICE_DECK_STATE_MANAGER_H
|
||||
#define COCKATRICE_DECK_STATE_MANAGER_H
|
||||
|
||||
#include "../../deck_loader/loaded_deck.h"
|
||||
#include "deck_list_model.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QObject>
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QStringList>
|
||||
#include <functional>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class DeckListHistoryManager;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <QStringLiteral>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_querier.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
SharedDeckPreviewWidget::SharedDeckPreviewWidget(QWidget *parent,
|
||||
|
|
@ -62,8 +63,7 @@ SharedDeckPreviewWidget::SharedDeckPreviewWidget(QWidget *parent,
|
|||
selectionRow->addWidget(selectionCheckBox);
|
||||
selectionRow->addStretch(1);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *layout = QtUtils::makeLayout<QVBoxLayout>(this);
|
||||
layout->addLayout(selectionRow);
|
||||
layout->addWidget(bannerCardDisplayWidget, 0, Qt::AlignHCenter);
|
||||
layout->addWidget(colorIdentityWidget, 0, Qt::AlignHCenter);
|
||||
|
|
|
|||
|
|
@ -295,9 +295,15 @@ void DlgConnect::updateDisplayInfo(const QString &saveName)
|
|||
const QString stored = _data.at(4);
|
||||
if (stored.startsWith("$")) {
|
||||
storedVerifier = stored;
|
||||
passwordEdit->clear();
|
||||
passwordEdit->setPlaceholderText(tr("Password saved"));
|
||||
} else {
|
||||
passwordEdit->setText(stored);
|
||||
passwordEdit->setPlaceholderText(QString());
|
||||
}
|
||||
} else {
|
||||
passwordEdit->clear();
|
||||
passwordEdit->setPlaceholderText(QString());
|
||||
}
|
||||
|
||||
if (!_data.at(6).isEmpty()) {
|
||||
|
|
@ -323,6 +329,7 @@ void DlgConnect::newHostSelected(bool state)
|
|||
portEdit->setDisabled(false);
|
||||
playernameEdit->clear();
|
||||
passwordEdit->clear();
|
||||
passwordEdit->setPlaceholderText(QString());
|
||||
storedVerifier.clear();
|
||||
saveEdit->clear();
|
||||
saveEdit->setPlaceholderText(tr("Unique Server Name"));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "dlg_convert_deck_to_cod_format.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../deck_loader/deck_file_format.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
|
|
@ -12,7 +11,9 @@
|
|||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/settings/visual_deck_storage_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
DialogConvertDeckToCodFormat::DialogConvertDeckToCodFormat(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
|
|
@ -57,11 +58,8 @@ bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
|||
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
|
||||
|
||||
if (QFile::exists(newFileName)) {
|
||||
QMessageBox::StandardButton reply =
|
||||
QMessageBox::question(parent, QObject::tr("Overwrite Existing File?"),
|
||||
QObject::tr("A .cod version of this deck already exists. Overwrite it?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
return reply == QMessageBox::Yes;
|
||||
return QtUtils::confirm(parent, QObject::tr("Overwrite Existing File?"),
|
||||
QObject::tr("A .cod version of this deck already exists. Overwrite it?"));
|
||||
}
|
||||
return true; // Safe to proceed
|
||||
}
|
||||
|
|
|
|||
245
cockatrice/src/interface/widgets/dialogs/dlg_crash_report.cpp
Normal file
245
cockatrice/src/interface/widgets/dialogs/dlg_crash_report.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
#include "dlg_crash_report.h"
|
||||
|
||||
#include "../../../client/crash/crash_reporter.h"
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../logger.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QFile>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QList>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
|
||||
DlgCrashReport::DlgCrashReport(QWidget *parent, bool _realSending, CrashReporter *_reporter)
|
||||
: QDialog(parent), realSending(_realSending), reporter(_reporter)
|
||||
{
|
||||
infoLabel = new QLabel(this);
|
||||
infoLabel->setWordWrap(true);
|
||||
|
||||
accountLabel = new QLabel(this);
|
||||
accountLabel->setWordWrap(true);
|
||||
|
||||
// Constructed before previewArea: Qt derives the default Tab chain from construction
|
||||
// order, and note sits visually above the log excerpt, so this keeps Tab moving downward.
|
||||
noteArea = new QPlainTextEdit(this);
|
||||
// Optional free text ("what I was doing when it crashed") — a few lines are
|
||||
// enough for a description, leave the rest of the space to the log excerpt.
|
||||
noteArea->setMaximumHeight(70);
|
||||
|
||||
previewArea = new QPlainTextEdit(this);
|
||||
previewArea->setReadOnly(true);
|
||||
previewArea->setPlainText(buildRedactedPreview());
|
||||
|
||||
rememberBox = new QCheckBox(this);
|
||||
|
||||
sendButton = new QPushButton(this);
|
||||
sendButton->setDefault(true);
|
||||
connect(sendButton, &QPushButton::clicked, this, &DlgCrashReport::actSend);
|
||||
|
||||
dontSendButton = new QPushButton(this);
|
||||
dontSendButton->setAutoDefault(false);
|
||||
connect(dontSendButton, &QPushButton::clicked, this, &DlgCrashReport::actDontSend);
|
||||
|
||||
auto *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addWidget(rememberBox);
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(dontSendButton);
|
||||
buttonLayout->addWidget(sendButton);
|
||||
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(infoLabel);
|
||||
mainLayout->addWidget(accountLabel);
|
||||
mainLayout->addWidget(noteArea);
|
||||
mainLayout->addWidget(previewArea);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
resize(700, 500);
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void DlgCrashReport::changeEvent(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
retranslateUi();
|
||||
}
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
||||
void DlgCrashReport::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Send crash report"));
|
||||
if (realSending) {
|
||||
// The preview below prefers the crashed session's persisted log tail
|
||||
// (Logger::getPreviousSessionTail), falling back to this restart's in-memory buffer only
|
||||
// when no previous session was saved. On send it is attached as crash-log.txt to the
|
||||
// pending native crash event (dump + stack + log + note in one report); when no native
|
||||
// crash event exists, it rides the fallback message event instead.
|
||||
infoLabel->setText(tr("Oops, we crashed — and now we need your help to make Cockatrice better!\n\n"
|
||||
"The good thing is, helping is easy and convenient. Sending uploads what the "
|
||||
"previous run left behind when it crashed: the crash dump with the stack trace, "
|
||||
"the app version and operating system info, the redacted log excerpt shown below, "
|
||||
"and anything you write in the note box. If no log from the crashed session was "
|
||||
"saved, this restart's early logs are shown instead.\n\n"
|
||||
"Reports are what let us fix crashes like this one. Check \u201cRemember my choice\u201d "
|
||||
"and Send, and future crash reports are sent automatically without interruption."));
|
||||
} else {
|
||||
infoLabel->setText(tr("(Mock preview — nothing will be uploaded.)\n\n"
|
||||
"Cockatrice closed unexpectedly. A real report would contain what the previous run "
|
||||
"left behind when it crashed: the crash dump with the stack trace, the app version "
|
||||
"and operating system info, the redacted log excerpt shown below, and anything you "
|
||||
"write in the note box. If no log from the crashed session was saved, this restart's "
|
||||
"early logs are shown instead.\n\n"
|
||||
"A mock file is written locally for inspection."));
|
||||
}
|
||||
accountLabel->setText(tr("No account needed, and no contact info is collected. Login links "
|
||||
"(cockatrice://...) are scrubbed before sending."));
|
||||
noteArea->setPlaceholderText(tr("(Optional) Describe what you were doing when it crashed"));
|
||||
// Direction-neutral on purpose: the same box also remembers a "Don't send" decision
|
||||
// (reject() persists NeverSend when it is checked), so it must not promise sending.
|
||||
rememberBox->setText(tr("Remember my choice"));
|
||||
sendButton->setText(tr("Send"));
|
||||
dontSendButton->setText(tr("Don't send"));
|
||||
}
|
||||
|
||||
QString DlgCrashReport::buildRedactedPreview()
|
||||
{
|
||||
Logger &logger = Logger::getInstance();
|
||||
|
||||
QStringList header;
|
||||
header.append(logger.getClientVersion());
|
||||
header.append(logger.getSystemArchitecture());
|
||||
header.append(logger.getSystemLocale());
|
||||
header.append(logger.getClientInstallInfo());
|
||||
header.append(QStringLiteral("---"));
|
||||
|
||||
QList<QString> logLines = logger.getPreviousSessionTail();
|
||||
if (logLines.isEmpty()) {
|
||||
logLines = logger.getLogBuffer();
|
||||
}
|
||||
const int previewLines = 60;
|
||||
int start = logLines.size() > previewLines ? logLines.size() - previewLines : 0;
|
||||
|
||||
QStringList lines = header;
|
||||
for (int i = start; i < logLines.size(); ++i) {
|
||||
lines.append(CrashReporter::redactCockatriceUrls(CrashReporter::sanitizeLogLine(logLines.at(i))));
|
||||
}
|
||||
return lines.join(QStringLiteral("\n"));
|
||||
}
|
||||
|
||||
QString DlgCrashReport::writeMockReport(const QString &previewText, const QString &userNote)
|
||||
{
|
||||
Logger &logger = Logger::getInstance();
|
||||
|
||||
QJsonObject report;
|
||||
report[QStringLiteral("mock")] = true;
|
||||
report[QStringLiteral("timestamp")] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
|
||||
report[QStringLiteral("clientVersion")] = logger.getClientVersion();
|
||||
report[QStringLiteral("systemArchitecture")] = logger.getSystemArchitecture();
|
||||
report[QStringLiteral("systemLocale")] = logger.getSystemLocale();
|
||||
report[QStringLiteral("installInfo")] = logger.getClientInstallInfo();
|
||||
report[QStringLiteral("logPreview")] = previewText;
|
||||
report[QStringLiteral("userNote")] = userNote;
|
||||
|
||||
QString fileName = QStringLiteral("crash-report-mock-%1.json")
|
||||
.arg(QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd-HHmmss")));
|
||||
QString filePath = QDir::tempPath() + QStringLiteral("/") + fileName;
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return QString();
|
||||
}
|
||||
file.write(QJsonDocument(report).toJson());
|
||||
file.close();
|
||||
return filePath;
|
||||
}
|
||||
|
||||
void DlgCrashReport::actSend()
|
||||
{
|
||||
if (rememberBox->isChecked()) {
|
||||
SettingsCache::instance().personal().setCrashReportingMode(PersonalSettings::CrashReportingMode::AlwaysSend);
|
||||
}
|
||||
const QString preview = previewArea->toPlainText();
|
||||
const QString userNote = noteArea->toPlainText().trimmed();
|
||||
if (realSending && reporter != nullptr) {
|
||||
// Fold log and note into the pending native crash event(s) BEFORE consent:
|
||||
// giveConsent() releases the held envelopes for upload, so dump, stack,
|
||||
// log and note arrive as one Sentry event. Without a pending crash event
|
||||
// (unclean exit that produced no native crash) this returns 0 and
|
||||
// sendReport() falls back to a standalone message event with the same
|
||||
// attachments.
|
||||
const int attached = reporter->attachToPendingCrashEnvelopes(preview, userNote);
|
||||
reporter->giveConsent();
|
||||
if (attached > 0 || reporter->sendReport(preview, userNote)) {
|
||||
accept();
|
||||
return;
|
||||
}
|
||||
qCWarning(CrashReportLog) << "Failed to send crash report";
|
||||
// Outcome mapping for the failure box: Save = store locally and close
|
||||
// as accepted (the follow-up info box shows the path, so the user
|
||||
// knows it was NOT sent); Discard = revoke consent and close as
|
||||
// rejected (dropped); Cancel/X = back to the dialog, neither sent nor
|
||||
// dropped. The Remember checkbox governs only the original
|
||||
// Send/Don't-send decision, so the Discard path below revokes and
|
||||
// closes via QDialog::reject() without touching the setting (calling
|
||||
// our reject() override here would wrongly persist NeverSend over the
|
||||
// AlwaysSend stored above when Remember was checked).
|
||||
QMessageBox failureBox(QMessageBox::Warning, tr("Send crash report"),
|
||||
tr("Sending the crash report failed. Save the report locally instead?"),
|
||||
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, this);
|
||||
failureBox.setDefaultButton(QMessageBox::Save);
|
||||
failureBox.setEscapeButton(QMessageBox::Cancel);
|
||||
const QMessageBox::StandardButton choice = static_cast<QMessageBox::StandardButton>(failureBox.exec());
|
||||
if (choice == QMessageBox::Save) {
|
||||
const QString savedPath = writeMockReport(preview, userNote);
|
||||
if (savedPath.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Send crash report"), tr("Could not save the crash report locally."));
|
||||
} else {
|
||||
QMessageBox::information(this, tr("Send crash report"),
|
||||
tr("Crash report saved locally to:\n%1").arg(savedPath));
|
||||
}
|
||||
accept();
|
||||
return;
|
||||
}
|
||||
if (choice == QMessageBox::Discard) {
|
||||
reporter->revokeConsent();
|
||||
QDialog::reject();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const QString savedPath = writeMockReport(preview, userNote);
|
||||
if (savedPath.isEmpty()) {
|
||||
qCWarning(CrashReportLog) << "Could not save the mock crash report locally";
|
||||
} else {
|
||||
qCInfo(CrashReportLog) << "Mock crash report saved locally to" << savedPath;
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
void DlgCrashReport::actDontSend()
|
||||
{
|
||||
reject();
|
||||
}
|
||||
|
||||
void DlgCrashReport::reject()
|
||||
{
|
||||
if (rememberBox->isChecked()) {
|
||||
SettingsCache::instance().personal().setCrashReportingMode(PersonalSettings::CrashReportingMode::NeverSend);
|
||||
}
|
||||
if (realSending && reporter != nullptr) {
|
||||
reporter->revokeConsent();
|
||||
}
|
||||
QDialog::reject();
|
||||
}
|
||||
63
cockatrice/src/interface/widgets/dialogs/dlg_crash_report.h
Normal file
63
cockatrice/src/interface/widgets/dialogs/dlg_crash_report.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file dlg_crash_report.h
|
||||
* @ingroup Dialogs
|
||||
*/
|
||||
|
||||
#ifndef DLG_CRASH_REPORT_H
|
||||
#define DLG_CRASH_REPORT_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class QCheckBox;
|
||||
class QLabel;
|
||||
class QPlainTextEdit;
|
||||
class QPushButton;
|
||||
|
||||
class CrashReporter;
|
||||
|
||||
/**
|
||||
* @brief Crash-report dialog: mock fallback or real Sentry send.
|
||||
*
|
||||
* Mock mode (default): the Send path writes a redacted JSON file to the temp
|
||||
* dir instead of uploading anything. Real mode (realSending, with a live
|
||||
* CrashReporter): Send first folds the redacted log and the optional user note
|
||||
* into the pending native crash event(s), then gives consent so dump, stack,
|
||||
* log and note upload as one Sentry event; when no pending crash event exists
|
||||
* it falls back to a standalone message event with the same attachments.
|
||||
* Failure offers a local save instead.
|
||||
*/
|
||||
class DlgCrashReport : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DlgCrashReport(QWidget *parent = nullptr, bool realSending = false, CrashReporter *_reporter = nullptr);
|
||||
|
||||
static QString buildRedactedPreview();
|
||||
static QString writeMockReport(const QString &previewText, const QString &userNote = QString());
|
||||
|
||||
void reject() override;
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
QLabel *infoLabel;
|
||||
QLabel *accountLabel;
|
||||
QPlainTextEdit *previewArea;
|
||||
QPlainTextEdit *noteArea;
|
||||
QCheckBox *rememberBox;
|
||||
QPushButton *sendButton;
|
||||
QPushButton *dontSendButton;
|
||||
bool realSending = false;
|
||||
CrashReporter *reporter = nullptr;
|
||||
|
||||
void retranslateUi();
|
||||
|
||||
private slots:
|
||||
void actSend();
|
||||
void actDontSend();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/settings/visual_deck_storage_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
DlgDefaultTagsEditor::DlgDefaultTagsEditor(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
|
|
@ -120,8 +121,7 @@ void DlgDefaultTagsEditor::addItem()
|
|||
// Add new tag
|
||||
auto *item = new QListWidgetItem(listWidget);
|
||||
QWidget *widget = new QWidget(this);
|
||||
QHBoxLayout *layout = new QHBoxLayout(widget);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
QHBoxLayout *layout = QtUtils::makeLayout<QHBoxLayout>(widget);
|
||||
|
||||
// Editable tag label
|
||||
QLineEdit *lineEdit = new QLineEdit(newTag, this);
|
||||
|
|
|
|||
|
|
@ -71,7 +71,21 @@ void DlgEditAvatar::actBrowse()
|
|||
|
||||
QImageReader imgReader;
|
||||
imgReader.setDecideFormatFromContent(true);
|
||||
// Honour the EXIF orientation phone cameras write; otherwise portrait
|
||||
// photos are shown and stored sideways.
|
||||
imgReader.setAutoTransform(true);
|
||||
imgReader.setFileName(fileName);
|
||||
|
||||
// Decode straight to the maximum avatar size. Scaling afterwards decodes
|
||||
// the full-resolution image first, which can be tens of megabytes for a
|
||||
// modern camera photo.
|
||||
const QSize sourceSize = imgReader.size();
|
||||
if (sourceSize.isValid() &&
|
||||
(sourceSize.width() > MAX_AVATAR_DIMENSION || sourceSize.height() > MAX_AVATAR_DIMENSION)) {
|
||||
const QSize targetSize = sourceSize.scaled(MAX_AVATAR_DIMENSION, MAX_AVATAR_DIMENSION, Qt::KeepAspectRatio);
|
||||
imgReader.setScaledSize(targetSize);
|
||||
}
|
||||
|
||||
if (!imgReader.read(&image)) {
|
||||
qCWarning(DlgEditAvatarLog) << "Avatar image loading failed for file:" << fileName;
|
||||
imageLabel->setText(tr("Invalid image chosen."));
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@
|
|||
|
||||
#include "libcockatrice/deck_list/deck_list.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class QPlainTextEdit;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/import/card_name_normalizer.h>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <qnamespace.h>
|
||||
#include <version_string.h>
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ class QWidget;
|
|||
|
||||
DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
nam = new QNetworkAccessManager(this);
|
||||
nam = NetworkManagerFactory::create(this);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
#include "libcockatrice/protocol/pb/serverinfo_report.pb.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
|
|
@ -26,6 +24,7 @@
|
|||
#include <libcockatrice/protocol/pb/response_report_details.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_report_my_list.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -70,13 +69,7 @@ DlgMyReports::DlgMyReports(AbstractClient *_client, QWidget *parent)
|
|||
auto *chatGroup = new QGroupBox(tr("Chat Log Context"));
|
||||
chatLogEdit = new QTextEdit;
|
||||
chatLogEdit->setReadOnly(true);
|
||||
QFont monoFont("monospace");
|
||||
monoFont.setStyleHint(QFont::Monospace);
|
||||
const int systemPointSize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize();
|
||||
if (systemPointSize > 0) {
|
||||
monoFont.setPointSize(systemPointSize);
|
||||
}
|
||||
chatLogEdit->setFont(monoFont);
|
||||
chatLogEdit->setFont(QtUtils::monospaceFont());
|
||||
auto *chatLayout = new QVBoxLayout(chatGroup);
|
||||
chatLayout->setContentsMargins(4, 4, 4, 4);
|
||||
chatLayout->addWidget(chatLogEdit);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
|
|
@ -21,6 +19,7 @@
|
|||
#include <QVariant>
|
||||
#include <libcockatrice/protocol/pb/command_report.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <libcockatrice/utility/report_categories.h>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
|
@ -92,13 +91,7 @@ DlgReportUser::DlgReportUser(AbstractClient *_client,
|
|||
auto *chatLayout = new QVBoxLayout(chatGroup);
|
||||
chatLogEdit = new QTextEdit;
|
||||
chatLogEdit->setReadOnly(true);
|
||||
QFont monoFont("monospace");
|
||||
monoFont.setStyleHint(QFont::Monospace);
|
||||
const int systemPointSize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize();
|
||||
if (systemPointSize > 0) {
|
||||
monoFont.setPointSize(systemPointSize);
|
||||
}
|
||||
chatLogEdit->setFont(monoFont);
|
||||
chatLogEdit->setFont(QtUtils::monospaceFont());
|
||||
if (!_autoChatLog.isEmpty()) {
|
||||
chatLogEdit->setPlainText(_autoChatLog);
|
||||
} else {
|
||||
|
|
@ -135,12 +128,8 @@ void DlgReportUser::actSubmit()
|
|||
return;
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton reply =
|
||||
QMessageBox::question(this, tr("Confirm Report"),
|
||||
tr("Submit report against %1 for %2?").arg(reportedUser, categoryBox->currentText()),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (reply != QMessageBox::Yes) {
|
||||
if (!QtUtils::confirm(this, tr("Confirm Report"),
|
||||
tr("Submit report against %1 for %2?").arg(reportedUser, categoryBox->currentText()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qalgorithms.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobjectdefs.h>
|
||||
|
|
@ -71,10 +72,7 @@ DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckStateManager *de
|
|||
QSplitter *splitter = new QSplitter(Qt::Vertical, this);
|
||||
|
||||
// Top scroll area
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
listContainer = new QWidget(scrollArea);
|
||||
listLayout = new QVBoxLayout(listContainer);
|
||||
|
|
@ -86,14 +84,10 @@ DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckStateManager *de
|
|||
|
||||
// Left container
|
||||
QWidget *leftContainer = new QWidget(this);
|
||||
QVBoxLayout *leftLayout = new QVBoxLayout(leftContainer);
|
||||
leftLayout->setContentsMargins(0, 0, 0, 0);
|
||||
QVBoxLayout *leftLayout = QtUtils::makeLayout<QVBoxLayout>(leftContainer);
|
||||
|
||||
uneditedCardsLabel = new QLabel(this);
|
||||
uneditedCardsArea = new QScrollArea(this);
|
||||
uneditedCardsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
uneditedCardsArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
uneditedCardsArea->setWidgetResizable(true);
|
||||
uneditedCardsArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
uneditedCardsFlowWidget =
|
||||
new FlowWidget(uneditedCardsArea, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
|
@ -105,14 +99,10 @@ DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckStateManager *de
|
|||
|
||||
// Right container
|
||||
QWidget *rightContainer = new QWidget(this);
|
||||
QVBoxLayout *rightLayout = new QVBoxLayout(rightContainer);
|
||||
rightLayout->setContentsMargins(0, 0, 0, 0);
|
||||
QVBoxLayout *rightLayout = QtUtils::makeLayout<QVBoxLayout>(rightContainer);
|
||||
|
||||
modifiedCardsLabel = new QLabel(this);
|
||||
modifiedCardsArea = new QScrollArea(this);
|
||||
modifiedCardsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
modifiedCardsArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
modifiedCardsArea->setWidgetResizable(true);
|
||||
modifiedCardsArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
modifiedCardsFlowWidget =
|
||||
new FlowWidget(modifiedCardsArea, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
|
@ -274,9 +264,9 @@ QMap<QString, int> DlgSelectSetForCards::getSetsForCards()
|
|||
continue;
|
||||
}
|
||||
|
||||
SetToPrintingsMap setMap = infoPtr->getSets();
|
||||
for (auto &setName : setMap.keys()) {
|
||||
setCounts[setName]++;
|
||||
const auto &setMap = infoPtr->getSets();
|
||||
for (auto it = setMap.constBegin(); it != setMap.constEnd(); ++it) {
|
||||
setCounts[it.key()]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,8 +383,8 @@ QMap<QString, QStringList> DlgSelectSetForCards::getCardsForSets()
|
|||
continue;
|
||||
}
|
||||
|
||||
SetToPrintingsMap setMap = infoPtr->getSets();
|
||||
for (auto it = setMap.begin(); it != setMap.end(); ++it) {
|
||||
const auto &setMap = infoPtr->getSets();
|
||||
for (auto it = setMap.constBegin(); it != setMap.constEnd(); ++it) {
|
||||
setCards[it.key()].append(cardName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <google/protobuf/repeated_ptr_field.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_deck_share_item.pb.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QWidget;
|
||||
|
|
@ -34,8 +35,7 @@ DlgSharedDecksPreview::DlgSharedDecksPreview(QWidget *parent,
|
|||
// QLabels render AutoText and markup would otherwise be shown as rich text.
|
||||
auto *titleLabel =
|
||||
new QLabel(tr("Share: %1").arg((shareName.isEmpty() ? tr("Untitled") : shareName).toHtmlEscaped()), this);
|
||||
QFont titleFont = titleLabel->font();
|
||||
titleFont.setBold(true);
|
||||
QFont titleFont = QtUtils::boldFont(titleLabel->font());
|
||||
titleFont.setPointSize(titleFont.pointSize() + 2);
|
||||
titleLabel->setFont(titleFont);
|
||||
mainLayout->addWidget(titleLabel);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <version_string.h>
|
||||
|
||||
|
|
@ -170,16 +171,13 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas
|
|||
|
||||
publishDate = release->getPublishDate().toString(QLocale().dateFormat(QLocale::LongFormat));
|
||||
if (isCompatible) {
|
||||
int reply;
|
||||
reply = QMessageBox::question(
|
||||
window(), tr("Update Available"),
|
||||
tr("A new version of Cockatrice is available!") + "<br><br>" + "<b>" + tr("New version") +
|
||||
QString(":</b> %1<br>").arg(release->getName()) + "<b>" + tr("Released") +
|
||||
QString(":</b> %1 (<a href=\"%2\">").arg(publishDate, release->getDescriptionUrl()) + tr("Changelog") +
|
||||
"</a>)<br><br>" + tr("Do you want to update now?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
const QString message = tr("A new version of Cockatrice is available!") + "<br><br>" + "<b>" +
|
||||
tr("New version") + QString(":</b> %1<br>").arg(release->getName()) + "<b>" +
|
||||
tr("Released") +
|
||||
QString(":</b> %1 (<a href=\"%2\">").arg(publishDate, release->getDescriptionUrl()) +
|
||||
tr("Changelog") + "</a>)<br><br>" + tr("Do you want to update now?");
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(window(), tr("Update Available"), message)) {
|
||||
downloadUpdate(release->getName());
|
||||
} else {
|
||||
closeDialog();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
class QWidget;
|
||||
|
||||
|
|
@ -30,10 +31,7 @@ bool OverridePrintingWarning::execMessageBox(QWidget *parent, bool enable)
|
|||
"Are you sure you would like to disable this feature?");
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton result =
|
||||
QMessageBox::question(parent, QObject::tr("Confirm Change"), message, QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (result == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(parent, QObject::tr("Confirm Change"), message)) {
|
||||
SettingsCache::instance().cardsDisplay().setOverrideAllCardArtWithPersonalPreference(enable);
|
||||
// Caches are now invalid.
|
||||
CardPictureLoader::clearPixmapCache();
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
/**
|
||||
|
|
@ -71,10 +72,8 @@ static QScrollArea *makeScrollable(QWidget *widget)
|
|||
{
|
||||
widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Maximum);
|
||||
|
||||
auto *scrollArea = new QScrollArea;
|
||||
scrollArea->setWidgetResizable(true);
|
||||
auto *scrollArea = QtUtils::makeScrollArea();
|
||||
scrollArea->setContentsMargins(0, 0, 0, 0);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->horizontalScrollBar()->setEnabled(false);
|
||||
scrollArea->setWidget(widget);
|
||||
return scrollArea;
|
||||
|
|
@ -625,13 +624,8 @@ void SettingsView::scrollToAndFlash(QWidget *target)
|
|||
if (!target) {
|
||||
return;
|
||||
}
|
||||
QWidget *widget = target;
|
||||
while (widget) {
|
||||
if (auto *scrollArea = qobject_cast<QScrollArea *>(widget)) {
|
||||
scrollArea->ensureWidgetVisible(target);
|
||||
break;
|
||||
}
|
||||
widget = widget->parentWidget();
|
||||
if (auto *scrollArea = QtUtils::findParentOfType<QScrollArea>(target, true)) {
|
||||
scrollArea->ensureWidgetVisible(target);
|
||||
}
|
||||
flashWidget(target);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../interface/widgets/tabs/tab_supervisor.h"
|
||||
#include "../../deck_loader/deck_file_format.h"
|
||||
#include "../../deck_loader/deck_loader.h"
|
||||
#include "../../deck_loader/loaded_deck.h"
|
||||
#include "../../pixel_map_generator.h"
|
||||
#include "../../theme_config.h"
|
||||
#include "../../theme_manager.h"
|
||||
|
|
@ -57,6 +55,8 @@
|
|||
#include <QtGlobal>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/network/client/remote/remote_client.h>
|
||||
#include <libcockatrice/settings/appearance_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
|
|
@ -113,6 +113,32 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
|||
&HomeWidget::updateButtonsToBackgroundColor);
|
||||
}
|
||||
|
||||
void HomeWidget::retranslateUi()
|
||||
{
|
||||
visualDeckEditorButton->setText(tr("Create New Deck"));
|
||||
visualDeckStorageButton->setText(tr("Browse Decks"));
|
||||
visualDatabaseDisplayButton->setText(tr("Browse Card Database"));
|
||||
edhrecButton->setText(tr("Browse EDHRec"));
|
||||
archidektButton->setText(tr("Browse Archidekt"));
|
||||
replayButton->setText(tr("View Replays"));
|
||||
if (exitButton) {
|
||||
exitButton->setText(tr("Quit"));
|
||||
}
|
||||
|
||||
switch (connectionStatus) {
|
||||
case StatusDisconnected:
|
||||
connectButton->setText(tr("Connect"));
|
||||
break;
|
||||
case StatusLoggedIn:
|
||||
connectButton->setText(tr("Play"));
|
||||
break;
|
||||
default:
|
||||
// Connecting, registering, activating, logging in, etc.
|
||||
connectButton->setText(tr("Connecting..."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWidget::initializeBackgroundFromSource()
|
||||
{
|
||||
// The featured logo is theme/scheme-derived too; reload it alongside the
|
||||
|
|
@ -284,32 +310,32 @@ QGroupBox *HomeWidget::createButtons()
|
|||
updateLogoOverlay();
|
||||
boxLayout->addSpacing(25);
|
||||
|
||||
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
|
||||
connectButton = new HomeStyledButton(QString(), gradientColors);
|
||||
boxLayout->addWidget(connectButton);
|
||||
|
||||
auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
|
||||
visualDeckEditorButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
|
||||
[this] { tabSupervisor->openDeckInNewTab(LoadedDeck()); });
|
||||
boxLayout->addWidget(visualDeckEditorButton);
|
||||
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
|
||||
visualDeckStorageButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
|
||||
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
|
||||
boxLayout->addWidget(visualDeckStorageButton);
|
||||
auto visualDatabaseDisplayButton = new HomeStyledButton(tr("Browse Card Database"), gradientColors);
|
||||
visualDatabaseDisplayButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
|
||||
&TabSupervisor::addVisualDatabaseDisplayTab);
|
||||
boxLayout->addWidget(visualDatabaseDisplayButton);
|
||||
auto edhrecButton = new HomeStyledButton(tr("Browse EDHRec"), gradientColors);
|
||||
edhrecButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
|
||||
boxLayout->addWidget(edhrecButton);
|
||||
auto archidektButton = new HomeStyledButton(tr("Browse Archidekt"), gradientColors);
|
||||
archidektButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(archidektButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addArchidektTab);
|
||||
boxLayout->addWidget(archidektButton);
|
||||
auto replaybutton = new HomeStyledButton(tr("View Replays"), gradientColors);
|
||||
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
|
||||
boxLayout->addWidget(replaybutton);
|
||||
replayButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(replayButton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
|
||||
boxLayout->addWidget(replayButton);
|
||||
if (qobject_cast<MainWindow *>(tabSupervisor->parentWidget())) {
|
||||
auto exitButton = new HomeStyledButton(tr("Quit"), gradientColors);
|
||||
exitButton = new HomeStyledButton(QString(), gradientColors);
|
||||
connect(exitButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
|
||||
&MainWindow::actExit);
|
||||
boxLayout->addWidget(exitButton);
|
||||
|
|
@ -321,20 +347,18 @@ QGroupBox *HomeWidget::createButtons()
|
|||
|
||||
void HomeWidget::updateConnectButton(const ClientStatus status)
|
||||
{
|
||||
connectionStatus = status;
|
||||
disconnect(connectButton, &QPushButton::clicked, nullptr, nullptr);
|
||||
switch (status) {
|
||||
case StatusConnecting:
|
||||
connectButton->setText(tr("Connecting..."));
|
||||
connectButton->setEnabled(false);
|
||||
break;
|
||||
case StatusDisconnected:
|
||||
connectButton->setText(tr("Connect"));
|
||||
connectButton->setEnabled(true);
|
||||
connect(connectButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
|
||||
&MainWindow::actConnect);
|
||||
break;
|
||||
case StatusLoggedIn:
|
||||
connectButton->setText(tr("Play"));
|
||||
connectButton->setEnabled(true);
|
||||
connect(connectButton, &QPushButton::clicked, tabSupervisor,
|
||||
&TabSupervisor::switchToFirstAvailableNetworkTab);
|
||||
|
|
@ -342,6 +366,7 @@ void HomeWidget::updateConnectButton(const ClientStatus status)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
QPair<QColor, QColor> HomeWidget::extractDominantColors(const QPixmap &pixmap)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ class HomeWidget : public QWidget
|
|||
public:
|
||||
HomeWidget(QWidget *parent, TabSupervisor *tabSupervisor);
|
||||
void updateRandomCard();
|
||||
void retranslateUi();
|
||||
static QPair<QColor, QColor> extractDominantColors(const QPixmap &pixmap);
|
||||
|
||||
public slots:
|
||||
|
|
@ -53,7 +54,15 @@ private:
|
|||
DeckList backgroundSourceDeck;
|
||||
QLabel *logoLabel = nullptr;
|
||||
QPair<QColor, QColor> gradientColors;
|
||||
HomeStyledButton *connectButton;
|
||||
HomeStyledButton *connectButton = nullptr;
|
||||
HomeStyledButton *visualDeckEditorButton = nullptr;
|
||||
HomeStyledButton *visualDeckStorageButton = nullptr;
|
||||
HomeStyledButton *visualDatabaseDisplayButton = nullptr;
|
||||
HomeStyledButton *edhrecButton = nullptr;
|
||||
HomeStyledButton *archidektButton = nullptr;
|
||||
HomeStyledButton *replayButton = nullptr;
|
||||
HomeStyledButton *exitButton = nullptr;
|
||||
ClientStatus connectionStatus = StatusDisconnected;
|
||||
|
||||
void setRandomCard(ExactCard &newCard);
|
||||
void loadBackgroundSourceDeck();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <QSize>
|
||||
#include <QSizePolicy>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
/**
|
||||
* @brief Constructs a FlowWidget.
|
||||
|
|
@ -47,21 +48,16 @@ FlowWidget::FlowWidget(QWidget *parent,
|
|||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
}
|
||||
|
||||
mainLayout = new QHBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout = QtUtils::makeLayout<QHBoxLayout>(this);
|
||||
setLayout(mainLayout);
|
||||
|
||||
const bool useScrollArea = (horizontalPolicy != Qt::ScrollBarAlwaysOff || verticalPolicy != Qt::ScrollBarAlwaysOff);
|
||||
|
||||
// Scroll area (optional)
|
||||
if (useScrollArea) {
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea = QtUtils::makeScrollArea(this, verticalPolicy, horizontalPolicy);
|
||||
scrollArea->setMinimumSize(0, 0);
|
||||
scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
// Set scrollbar policies
|
||||
scrollArea->setHorizontalScrollBarPolicy(horizontalPolicy);
|
||||
scrollArea->setVerticalScrollBarPolicy(verticalPolicy);
|
||||
} else {
|
||||
scrollArea = nullptr;
|
||||
}
|
||||
|
|
@ -105,9 +101,9 @@ void FlowWidget::addWidget(QWidget *widget_to_add)
|
|||
*
|
||||
* The widget is filtered for arrow-key events so keyboard navigation between
|
||||
* the flow items keeps working even when the flow sits inside a QScrollArea,
|
||||
* which swallows arrow keys before they can reach FlowWidget::keyPressEvent.
|
||||
* Only widgets added through this method are affected; anything that needs its
|
||||
* own arrow keys should use plain addWidget.
|
||||
* which swallows arrow keys before they can bubble up. Only widgets added
|
||||
* through this method are affected; anything that needs its own arrow keys
|
||||
* should use plain addWidget.
|
||||
*
|
||||
* @param widget_to_add The widget to add to the flow layout.
|
||||
*/
|
||||
|
|
@ -136,13 +132,7 @@ void FlowWidget::removeWidget(QWidget *widgetToRemove) const
|
|||
void FlowWidget::clearLayout()
|
||||
{
|
||||
if (flowLayout) {
|
||||
QLayoutItem *item;
|
||||
while ((item = flowLayout->takeAt(0))) {
|
||||
if (item->widget()) {
|
||||
item->widget()->deleteLater();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(flowLayout);
|
||||
} else {
|
||||
// Defensive fallback: recreate the layout if it was deleted externally.
|
||||
flowLayout = new FlowLayout(container, flowDirection);
|
||||
|
|
@ -207,17 +197,12 @@ QLayoutItem *FlowWidget::itemAt(int index) const
|
|||
return flowLayout->itemAt(index);
|
||||
}
|
||||
|
||||
void FlowWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (moveFocus(event)) {
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
QWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
bool FlowWidget::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
// Arrow-key navigation is deliberately scoped to widgets that opted in via
|
||||
// addNavigableWidget(). Handling arrow keys in keyPressEvent() as well would
|
||||
// also catch the events ordinary children (QLineEdit, ...) ignore and move
|
||||
// focus away from the control the user is typing in.
|
||||
if (event->type() == QEvent::KeyPress && moveFocus(static_cast<QKeyEvent *>(event))) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ public slots:
|
|||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
private:
|
||||
/// @brief Moves keyboard focus to an adjacent flow item for an arrow-key event.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <QLayoutItem>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
/**
|
||||
* @class OverlapWidget
|
||||
|
|
@ -80,11 +81,7 @@ void OverlapWidget::removeWidget(QWidget *widgetToRemove) const
|
|||
void OverlapWidget::clearLayout()
|
||||
{
|
||||
if (overlapLayout != nullptr) {
|
||||
QLayoutItem *item;
|
||||
while ((item = overlapLayout->takeAt(0)) != nullptr) {
|
||||
item->widget()->deleteLater();
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(overlapLayout);
|
||||
}
|
||||
|
||||
// If layout is null, create a new layout; otherwise, reuse the existing one
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -31,9 +32,8 @@ FirstRunWizard::FirstRunWizard(QWidget *parent) : QDialog(parent)
|
|||
bannerHost = new BannerHost(this);
|
||||
|
||||
titleLabel = new QLabel(this);
|
||||
QFont titleFont = titleLabel->font();
|
||||
QFont titleFont = QtUtils::boldFont(titleLabel->font());
|
||||
titleFont.setPointSizeF(titleFont.pointSizeF() * 1.4);
|
||||
titleFont.setBold(true);
|
||||
titleLabel->setFont(titleFont);
|
||||
|
||||
subtitleLabel = new QLabel(this);
|
||||
|
|
@ -51,8 +51,7 @@ FirstRunWizard::FirstRunWizard(QWidget *parent) : QDialog(parent)
|
|||
connect(skipButton, &QPushButton::clicked, this, &FirstRunWizard::skip);
|
||||
connect(nextButton, &QPushButton::clicked, this, &FirstRunWizard::goNext);
|
||||
|
||||
auto *headerLayout = new QVBoxLayout;
|
||||
headerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *headerLayout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
headerLayout->addWidget(bannerHost);
|
||||
headerLayout->addSpacing(12);
|
||||
headerLayout->addWidget(titleLabel);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -68,9 +69,8 @@ PreferencesSetupPage::PreferencesSetupPage(QWidget *parent) : FirstRunWizardPage
|
|||
|
||||
contentLayout->addStretch();
|
||||
|
||||
auto *scrollArea = new QScrollArea(this);
|
||||
auto *scrollArea = QtUtils::makeScrollArea(this, Qt::ScrollBarAsNeeded, Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setWidget(content);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../settings_page/general_settings_page.h"
|
||||
#include "../first_run_wizard_page.h"
|
||||
#include "libcockatrice/settings/personal_settings.h"
|
||||
#include "libcockatrice/utility/translation_loader.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QChar>
|
||||
|
|
@ -30,7 +31,7 @@ WelcomePage::WelcomePage(QWidget *parent) : FirstRunWizardPage(parent)
|
|||
languageLabel = new QLabel(this);
|
||||
langCombo = new QComboBox(this);
|
||||
for (const QString &code : GeneralSettingsPage::findQmFiles()) {
|
||||
langCombo->addItem(GeneralSettingsPage::languageName(code), code);
|
||||
langCombo->addItem(TranslationLoader::languageDisplayName(code), code);
|
||||
}
|
||||
|
||||
QString current = SettingsCache::instance().personal().getLang();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qmath.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qnumeric.h>
|
||||
|
|
@ -151,8 +152,7 @@ BannerHost::BannerHost(QWidget *parent) : QWidget(parent)
|
|||
{
|
||||
setFixedHeight(150);
|
||||
|
||||
stack = new QStackedLayout(this);
|
||||
stack->setContentsMargins(0, 0, 0, 0);
|
||||
stack = QtUtils::makeLayout<QStackedLayout>(this);
|
||||
|
||||
fallback = new GradientFallbackWidget(this);
|
||||
stack->addWidget(fallback);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <QVariant>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QModelIndex;
|
||||
|
|
@ -42,9 +43,7 @@ CardAmountWidget::CardAmountWidget(QWidget *parent,
|
|||
: QWidget(parent), deckStateManager(deckStateManager), cardSizeSlider(cardSizeSlider), rootCard(rootCard),
|
||||
zoneName(zoneName), hovered(false)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(10);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this, 10);
|
||||
this->setLayout(layout);
|
||||
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
layout->setAlignment(Qt::AlignHCenter);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/macros.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
/**
|
||||
|
|
@ -84,9 +85,7 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
|
|||
displayOptionsWidget->addSettingsWidget(cardSizeWidget);
|
||||
|
||||
sortAndOptionsContainer = new QWidget(this);
|
||||
sortAndOptionsLayout = new QHBoxLayout(sortAndOptionsContainer);
|
||||
sortAndOptionsLayout->setSpacing(3);
|
||||
sortAndOptionsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
sortAndOptionsLayout = QtUtils::makeLayout<QHBoxLayout>(sortAndOptionsContainer, 0, 3);
|
||||
sortAndOptionsContainer->setLayout(sortAndOptionsLayout);
|
||||
|
||||
searchBar = new PrintingSelectorCardSearchWidget(this);
|
||||
|
|
@ -229,7 +228,7 @@ void PrintingSelector::getAllSetsForCurrentCard()
|
|||
return;
|
||||
}
|
||||
|
||||
SetToPrintingsMap setMap = selectedCard->getSets();
|
||||
const auto &setMap = selectedCard->getSets();
|
||||
const QList<PrintingInfo> sortedPrintings = sortToolBar->sortSets(setMap);
|
||||
const QList<PrintingInfo> filteredPrintings =
|
||||
sortToolBar->filterSets(sortedPrintings, searchBar->getSearchText().trimmed().toLower());
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/settings/card_override_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class DeckStateManager;
|
||||
|
|
@ -108,9 +109,7 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa
|
|||
: QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard)
|
||||
{
|
||||
// Set up the main layout
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
auto *mainLayout = QtUtils::makeLayout<QVBoxLayout>(this, 0);
|
||||
setLayout(mainLayout);
|
||||
|
||||
// Add CardInfoPictureWidget
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ QList<PrintingInfo> PrintingSelectorCardSortingWidget::prependPrintingsInDeck(co
|
|||
return {};
|
||||
}
|
||||
|
||||
SetToPrintingsMap setMap = selectedCard->getSets();
|
||||
const auto &setMap = selectedCard->getSets();
|
||||
QList<QPair<PrintingInfo, int>> countList;
|
||||
|
||||
// Collect sets with their counts
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <QRect>
|
||||
#include <QSizePolicy>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
/**
|
||||
|
|
@ -19,8 +20,7 @@ SetNameAndCollectorsNumberDisplayWidget::SetNameAndCollectorsNumberDisplayWidget
|
|||
: QWidget(parent)
|
||||
{
|
||||
// Set up the layout for the widget
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QVBoxLayout>(this);
|
||||
layout->setSpacing(1);
|
||||
setLayout(layout);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <QSizePolicy>
|
||||
#include <QStringLiteral>
|
||||
#include <QToolButton>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
SettingsButtonWidget::SettingsButtonWidget(QWidget *parent)
|
||||
|
|
@ -26,9 +27,8 @@ SettingsButtonWidget::SettingsButtonWidget(QWidget *parent)
|
|||
connect(button, &QToolButton::clicked, this, &SettingsButtonWidget::togglePopup);
|
||||
connect(popup, &SettingsPopupWidget::aboutToClose, this, &SettingsButtonWidget::onPopupClosed);
|
||||
|
||||
layout = new QHBoxLayout(this);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this);
|
||||
layout->addWidget(button);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <QScrollArea>
|
||||
#include <QSize>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
SettingsPopupWidget::SettingsPopupWidget(QWidget *parent) : QWidget(parent, Qt::Popup | Qt::FramelessWindowHint)
|
||||
|
|
@ -54,11 +55,8 @@ void SettingsPopupWidget::adjustSizeToFitScreen()
|
|||
if (contentHeight > maxHeight) {
|
||||
// Create a scroll area and add the container widget to it if not already created
|
||||
if (!scrollArea) {
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea = QtUtils::makeScrollArea(this, Qt::ScrollBarAlwaysOn);
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // No horizontal scrollbar
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); // Enable vertical scrollbar when needed
|
||||
}
|
||||
|
||||
// Set maximum height for the scroll area and show vertical scrollbar
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#include "replay_manager.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../game/player/event_processing_options.h"
|
||||
#include "libcockatrice/protocol/pb/game_event.pb.h"
|
||||
#include "libcockatrice/protocol/pb/game_event_container.pb.h"
|
||||
#include "libcockatrice/protocol/pb/game_replay.pb.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/game/player/event_processing_options.h>
|
||||
#include <libcockatrice/protocol/get_pb_extension.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <qnumeric.h>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
#ifndef COCKATRICE_REPLAY_MANAGER_H
|
||||
#define COCKATRICE_REPLAY_MANAGER_H
|
||||
|
||||
#include "../../../game/player/event_processing_options.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/game/player/event_processing_options.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class GameReplay;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QtGlobal>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
ReplayQuickSettingsWidget::ReplayQuickSettingsWidget(QWidget *parent) : SettingsButtonWidget(parent)
|
||||
{
|
||||
|
|
@ -28,8 +29,7 @@ ReplayQuickSettingsWidget::ReplayQuickSettingsWidget(QWidget *parent) : Settings
|
|||
|
||||
// putting it all together
|
||||
auto *widget = new QWidget;
|
||||
auto *grid = new QGridLayout(widget);
|
||||
grid->setContentsMargins(0, 0, 0, 0);
|
||||
auto *grid = QtUtils::makeLayout<QGridLayout>(widget);
|
||||
grid->addWidget(&fastForwardSpeedLabel, 0, 0, 1, 1);
|
||||
grid->addWidget(&fastForwardSpeedBox, 0, 1, 1, 1);
|
||||
grid->addWidget(&skipEmptyCheckBox, 1, 0, 1, 2);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
#ifndef REPLAY_WIDGET_H
|
||||
#define REPLAY_WIDGET_H
|
||||
|
||||
#include "../../../game/player/event_processing_options.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/game/player/event_processing_options.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class ReplayManager;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include <libcockatrice/protocol/pb/serverinfo_game.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
|
@ -420,10 +421,8 @@ void GameSelector::joinGame(const ServerInfo_Game &game, const bool asSpectator,
|
|||
// spectator join, so ask first instead of surprising the player.
|
||||
const bool gameFull = game.player_count() == game.max_players();
|
||||
if (gameFull && !asSpectator && !asJudge && !overrideRestrictions) {
|
||||
const QMessageBox::StandardButton answer =
|
||||
QMessageBox::question(this, tr("Join game"), tr("The game is full. Join as a spectator instead?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
if (!QtUtils::confirm(this, tr("Join game"), tr("The game is full. Join as a spectator instead?"),
|
||||
QMessageBox::No)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
class TabSupervisor;
|
||||
template <class Key, class T> class QMap;
|
||||
|
|
@ -24,8 +25,7 @@ GameSelectorQuickFilterToolBar::GameSelectorQuickFilterToolBar(QWidget *parent,
|
|||
const QMap<int, QString> &allGameTypes)
|
||||
: QWidget(parent), tabSupervisor(_tabSupervisor), model(_model)
|
||||
{
|
||||
mainLayout = new QHBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout = QtUtils::makeLayout<QHBoxLayout>(this);
|
||||
mainLayout->setSpacing(5);
|
||||
|
||||
const GameFilterConfigs &filters = model->getFilters();
|
||||
|
|
|
|||
|
|
@ -13,13 +13,14 @@
|
|||
#include <QUrl>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/servers_settings.h>
|
||||
#include <qlogging.h>
|
||||
|
||||
#define PUBLIC_SERVERS_JSON "https://cockatrice.github.io/public-servers.json"
|
||||
|
||||
HandlePublicServers::HandlePublicServers(QObject *parent)
|
||||
: QObject(parent), nam(new QNetworkAccessManager(this)), reply(nullptr)
|
||||
: QObject(parent), nam(NetworkManagerFactory::create(this)), reply(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
#include "user_context_menu.h"
|
||||
|
||||
#include "../../../../game/abstract_game.h"
|
||||
#include "../../../../game/game_event_handler.h"
|
||||
#include "../../../../game/game_meta_info.h"
|
||||
#include "../../dialogs/dlg_report_user.h"
|
||||
#include "../../interface/widgets/tabs/tab_supervisor.h"
|
||||
#include "../chat_view/chat_view.h"
|
||||
|
|
@ -37,6 +34,9 @@
|
|||
#include <QTableWidget>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/game_event_handler.h>
|
||||
#include <libcockatrice/game/game_meta_info.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/command_kick_from_game.pb.h>
|
||||
#include <libcockatrice/protocol/pb/commands.pb.h>
|
||||
|
|
@ -49,6 +49,7 @@
|
|||
#include <libcockatrice/protocol/pb/response_warn_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/session_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -685,10 +686,8 @@ void UserContextMenu::execRemoveFromIgnore(const QString &userName)
|
|||
|
||||
void UserContextMenu::execKick(int playerId)
|
||||
{
|
||||
auto result = QMessageBox::question(static_cast<QWidget *>(parent()), tr("Kick Player"),
|
||||
tr("Are you sure you want to kick this player from the game?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (result == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(static_cast<QWidget *>(parent()), tr("Kick Player"),
|
||||
tr("Are you sure you want to kick this player from the game?"), QMessageBox::No)) {
|
||||
Command_KickFromGame cmd;
|
||||
cmd.set_player_id(playerId);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <libcockatrice/protocol/pb/session_commands.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/passwordhasher.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
|
@ -53,8 +54,7 @@ UserInfoBox::UserInfoBox(AbstractClient *_client, bool _editable, QWidget *paren
|
|||
avatarPic.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
avatarPic.setAlignment(Qt::AlignCenter);
|
||||
|
||||
auto *avatarLayout = new QHBoxLayout;
|
||||
avatarLayout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *avatarLayout = QtUtils::makeLayout<QHBoxLayout>();
|
||||
avatarLayout->addStretch(1);
|
||||
avatarLayout->addWidget(&avatarPic, 3);
|
||||
avatarLayout->addStretch(1);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#include <libcockatrice/protocol/pb/response_get_games_of_user.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnumeric.h>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -402,9 +403,7 @@ UserInfoPopup::UserInfoPopup(TabSupervisor *_ts,
|
|||
|
||||
void UserInfoPopup::buildUi()
|
||||
{
|
||||
auto *root = new QVBoxLayout(this);
|
||||
root->setContentsMargins(0, 0, 0, 0);
|
||||
root->setSpacing(0);
|
||||
auto *root = QtUtils::makeLayout<QVBoxLayout>(this, 0);
|
||||
|
||||
// Header
|
||||
header = new UserInfoHeaderWidget(this);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <QStringLiteral>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -31,9 +32,7 @@ QString sectionKey(UserListWidget::Section section)
|
|||
UserListPanelWidget::UserListPanelWidget(TabSupervisor *_tabSupervisor, AbstractClient *_client, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(2);
|
||||
auto *mainLayout = QtUtils::makeLayout<QVBoxLayout>(this, 2);
|
||||
|
||||
searchBar = new QLineEdit(this);
|
||||
searchBar->setClearButtonEnabled(true);
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@
|
|||
#include "user_list_painter.h"
|
||||
#include "user_list_proxy.h"
|
||||
|
||||
#include <QAbstractAnimation>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QCollator>
|
||||
#include <QColor>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QCursor>
|
||||
|
|
@ -41,7 +41,6 @@
|
|||
#include <QPalette>
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QRect>
|
||||
#include <QScreen>
|
||||
#include <QScrollBar>
|
||||
|
|
@ -57,7 +56,9 @@
|
|||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/models/natural_sort_collator.h>
|
||||
#include <libcockatrice/settings/appearance_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QPixmap;
|
||||
|
|
@ -273,6 +274,11 @@ void UserListTWI::setOnline(bool online)
|
|||
*/
|
||||
bool UserListTWI::operator<(const QTreeWidgetItem &other) const
|
||||
{
|
||||
// QCollator caches the locale data, unlike QString::localeAwareCompare,
|
||||
// which rebuilds it for every pair. The tree sorts one list at a time, so a
|
||||
// single shared, immutable collator is enough.
|
||||
static const QCollator collator = NaturalSort::makeNaturalSortCollator();
|
||||
|
||||
// Sort by online/offline
|
||||
if (data(0, UserListRoles::Online) != other.data(0, UserListRoles::Online)) {
|
||||
return data(0, UserListRoles::Online).toBool();
|
||||
|
|
@ -288,8 +294,7 @@ bool UserListTWI::operator<(const QTreeWidgetItem &other) const
|
|||
for (const auto &userLevelEntry : userLevelWithNoOtherPrefOrder) {
|
||||
if (lhsUserLevelFlags.testFlag(userLevelEntry) &&
|
||||
lhsUserLevelFlags.testFlag(userLevelEntry) == rhsUserLevelFlags.testFlag(userLevelEntry)) {
|
||||
return QString::localeAwareCompare(data(2, Qt::UserRole).toString(),
|
||||
other.data(2, Qt::UserRole).toString()) < 0;
|
||||
return collator.compare(data(2, Qt::UserRole).toString(), other.data(2, Qt::UserRole).toString()) < 0;
|
||||
} else if (lhsUserLevelFlags.testFlag(userLevelEntry) != rhsUserLevelFlags.testFlag(userLevelEntry)) {
|
||||
return lhsUserLevelFlags.testFlag(userLevelEntry) > rhsUserLevelFlags.testFlag(userLevelEntry);
|
||||
}
|
||||
|
|
@ -314,7 +319,7 @@ bool UserListTWI::operator<(const QTreeWidgetItem &other) const
|
|||
}
|
||||
|
||||
// Sort by name
|
||||
return QString::localeAwareCompare(data(2, Qt::UserRole).toString(), other.data(2, Qt::UserRole).toString()) < 0;
|
||||
return collator.compare(data(2, Qt::UserRole).toString(), other.data(2, Qt::UserRole).toString()) < 0;
|
||||
}
|
||||
|
||||
UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
|
||||
|
|
@ -895,11 +900,7 @@ void UserListWidget::showPopupForUser(UserListTWI *item)
|
|||
return;
|
||||
}
|
||||
|
||||
auto *fade = new QPropertyAnimation(userInfoPopup, "windowOpacity", userInfoPopup);
|
||||
fade->setDuration(120);
|
||||
fade->setStartValue(0.0);
|
||||
fade->setEndValue(1.0);
|
||||
fade->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
QtUtils::fadeWindow(userInfoPopup, 0.0, 1.0, 120);
|
||||
}
|
||||
|
||||
void UserListWidget::positionPopup(UserListTWI *item)
|
||||
|
|
@ -976,12 +977,7 @@ void UserListWidget::hidePopup(bool immediate)
|
|||
}
|
||||
|
||||
// Fade out
|
||||
auto *fade = new QPropertyAnimation(userInfoPopup, "windowOpacity", userInfoPopup);
|
||||
fade->setDuration(100);
|
||||
fade->setStartValue(userInfoPopup->windowOpacity());
|
||||
fade->setEndValue(0.0);
|
||||
connect(fade, &QPropertyAnimation::finished, userInfoPopup, &QWidget::hide);
|
||||
fade->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
QtUtils::fadeWindow(userInfoPopup, userInfoPopup->windowOpacity(), 0.0, 100, true);
|
||||
}
|
||||
|
||||
bool UserListWidget::isPressInsideListUi(const QWidget *widget) const
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include <QColor>
|
||||
#include <QColorDialog>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
|
|
@ -30,7 +29,6 @@
|
|||
#include <QStringLiteral>
|
||||
#include <QStyleFactory>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
|
|
@ -39,6 +37,7 @@
|
|||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
AppearanceSettingsPage::AppearanceSettingsPage()
|
||||
|
|
@ -446,7 +445,7 @@ void AppearanceSettingsPage::openThemeLocation()
|
|||
dirDir.cdUp();
|
||||
// open if dir exists, create if parent dir does exist
|
||||
if (dirDir.exists() && dirDir.mkpath(dir)) {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
|
||||
QtUtils::openPathInFileManager(dir);
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Could not create themes directory at '%1'.").arg(dir));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
#include "update/client/release_channel.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QCoreApplication>
|
||||
#include <QDate>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
|
@ -22,7 +21,6 @@
|
|||
#include <QPushButton>
|
||||
#include <QSharedPointer>
|
||||
#include <QSizePolicy>
|
||||
#include <QTranslator>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
|
|
@ -35,6 +33,7 @@
|
|||
#include <libcockatrice/settings/tabs_settings.h>
|
||||
#include <libcockatrice/settings/updates_settings.h>
|
||||
#include <libcockatrice/utility/macros.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <libcockatrice/utility/translation_loader.h>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -50,18 +49,24 @@ enum startupCardUpdateCheckBehaviorIndex
|
|||
GeneralSettingsPage::GeneralSettingsPage()
|
||||
{
|
||||
// language settings
|
||||
QStringList languageCodes = findQmFiles();
|
||||
for (const QString &code : languageCodes) {
|
||||
QString langName = languageName(code);
|
||||
languageBox.addItem(langName, code);
|
||||
for (const QString &code : findQmFiles()) {
|
||||
languageBox.addItem(TranslationLoader::languageDisplayName(code), code);
|
||||
}
|
||||
|
||||
QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME);
|
||||
int index = languageBox.findText(setLanguage, Qt::MatchExactly);
|
||||
if (index == -1) {
|
||||
qWarning() << "could not find language" << setLanguage;
|
||||
} else {
|
||||
// Select the language the client runs with: its own entry, the base language for a variant without a
|
||||
// file of its own (de_CH -> de), and the default language for an unset setting, which names no file.
|
||||
const QString configuredLanguage = SettingsCache::instance().personal().getLang();
|
||||
int index = languageBox.findData(configuredLanguage);
|
||||
if (index < 0) {
|
||||
index = languageBox.findData(configuredLanguage.section('_', 0, 0));
|
||||
}
|
||||
if (index < 0) {
|
||||
index = languageBox.findData(TranslationLoader::defaultLanguageCode());
|
||||
}
|
||||
if (index >= 0) {
|
||||
languageBox.setCurrentIndex(index);
|
||||
} else {
|
||||
qCWarning(GeneralSettingsPageLog) << "could not find language" << configuredLanguage;
|
||||
}
|
||||
|
||||
advertiseTranslationPageLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||
|
|
@ -95,12 +100,12 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
auto *languageGrid = new QGridLayout;
|
||||
languageGrid->addWidget(&languageLabel, 0, 0);
|
||||
languageGrid->addWidget(&languageBox, 0, 1);
|
||||
languageGrid->addWidget(&cardLanguageLabel, 1, 0);
|
||||
languageGrid->addWidget(&cardLanguageBox, 1, 1);
|
||||
languageGrid->addWidget(&cardLanguageNoteLabel, 2, 1);
|
||||
languageGrid->addWidget(&cardSearchLanguageLabel, 3, 0);
|
||||
languageGrid->addWidget(&cardSearchLanguageBox, 3, 1);
|
||||
languageGrid->addWidget(&advertiseTranslationPageLabel, 4, 1, Qt::AlignRight);
|
||||
languageGrid->addWidget(&advertiseTranslationPageLabel, 1, 1, Qt::AlignRight);
|
||||
languageGrid->addWidget(&cardLanguageLabel, 2, 0);
|
||||
languageGrid->addWidget(&cardLanguageBox, 2, 1);
|
||||
languageGrid->addWidget(&cardLanguageNoteLabel, 3, 1);
|
||||
languageGrid->addWidget(&cardSearchLanguageLabel, 4, 0);
|
||||
languageGrid->addWidget(&cardSearchLanguageBox, 4, 1);
|
||||
|
||||
cardLanguageNoteLabel.setWordWrap(true);
|
||||
cardLanguageNoteLabel.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
|
|
@ -133,6 +138,27 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
versionGroupBox = new QGroupBox;
|
||||
versionGroupBox->setLayout(versionGrid);
|
||||
|
||||
// crash reporting consent (stored in personal.ini, not debug.ini)
|
||||
crashReportingBox.addItem(""); // texts set in retranslateUi
|
||||
crashReportingBox.addItem("");
|
||||
crashReportingBox.addItem("");
|
||||
crashReportingBox.setCurrentIndex(static_cast<int>(settings.personal().getCrashReportingMode()));
|
||||
|
||||
connect(&crashReportingBox, qOverload<int>(&QComboBox::currentIndexChanged), this, [](int index) {
|
||||
if (index < 0 || index > static_cast<int>(PersonalSettings::CrashReportingMode::NeverSend)) {
|
||||
return;
|
||||
}
|
||||
SettingsCache::instance().personal().setCrashReportingMode(
|
||||
static_cast<PersonalSettings::CrashReportingMode>(index));
|
||||
});
|
||||
|
||||
auto *crashReportingGrid = new QGridLayout;
|
||||
crashReportingGrid->addWidget(&crashReportingLabel, 0, 0);
|
||||
crashReportingGrid->addWidget(&crashReportingBox, 0, 1);
|
||||
|
||||
crashReportingGroupBox = new QGroupBox;
|
||||
crashReportingGroupBox->setLayout(crashReportingGrid);
|
||||
|
||||
// card database settings
|
||||
startupCardUpdateCheckBehaviorSelector.addItem(""); // these will be set in retranslateUI
|
||||
startupCardUpdateCheckBehaviorSelector.addItem("");
|
||||
|
|
@ -336,6 +362,7 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(languageGroupBox);
|
||||
mainLayout->addWidget(versionGroupBox);
|
||||
mainLayout->addWidget(crashReportingGroupBox);
|
||||
mainLayout->addWidget(cardDatabaseGroupBox);
|
||||
mainLayout->addWidget(startupGroupBox);
|
||||
mainLayout->addWidget(pathsGroupBox);
|
||||
|
|
@ -361,18 +388,6 @@ QStringList GeneralSettingsPage::findQmFiles()
|
|||
TranslationLoader::applicationTranslationPaths(translationPath));
|
||||
}
|
||||
|
||||
QString GeneralSettingsPage::languageName(const QString &lang)
|
||||
{
|
||||
QTranslator qTranslator;
|
||||
|
||||
// Only the language name is needed here, so the file is loaded without logging a line per
|
||||
// language: findQmFiles() asked for languages whose files exist in the first place.
|
||||
TranslationLoader::loadFrom(qTranslator, translationPrefix + "_" + lang,
|
||||
TranslationLoader::applicationTranslationPaths(translationPath));
|
||||
|
||||
return qTranslator.translate("i18n", DEFAULT_LANG_NAME);
|
||||
}
|
||||
|
||||
void GeneralSettingsPage::deckPathButtonClicked()
|
||||
{
|
||||
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), deckPathEdit->text());
|
||||
|
|
@ -525,12 +540,9 @@ void GeneralSettingsPage::cardLanguageBoxChanged(int index)
|
|||
}
|
||||
message += tr("<p>Run Oracle now?</p>");
|
||||
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
this, tr("Card text & images language changed"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
// The answer only controls whether Oracle starts right away; the caches stay
|
||||
// cleared so the next import or launch rebuilds them in the new language.
|
||||
if (answer == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(this, tr("Card text & images language changed"), message, QMessageBox::Yes)) {
|
||||
emit cardDatabaseUpdateRequested();
|
||||
}
|
||||
}
|
||||
|
|
@ -556,7 +568,7 @@ void GeneralSettingsPage::retranslateUi()
|
|||
const auto &settings = SettingsCache::instance();
|
||||
|
||||
languageGroupBox->setTitle(tr("Language settings"));
|
||||
languageLabel.setText(tr("Language:"));
|
||||
languageLabel.setText(tr("Interface language:"));
|
||||
cardLanguageBox.setItemText(0, tr("English"));
|
||||
cardLanguageLabel.setText(tr("Card text & images language:"));
|
||||
cardLanguageNoteLabel.setText(
|
||||
|
|
@ -569,13 +581,19 @@ void GeneralSettingsPage::retranslateUi()
|
|||
tr("English and selected card language"));
|
||||
advertiseTranslationPageLabel.setText(
|
||||
QString("<a href='%1'>%2</a>").arg(WIKI_TRANSLATION_FAQ).arg(tr("How to help with translations")));
|
||||
|
||||
versionGroupBox->setTitle(tr("Version settings"));
|
||||
updateReleaseChannelLabel.setText(tr("Update channel"));
|
||||
startupUpdateCheckCheckBox.setText(tr("Check for client updates on startup"));
|
||||
updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client"));
|
||||
newVersionOracleCheckBox.setText(tr("Automatically run Oracle when running a new version of Cockatrice"));
|
||||
|
||||
crashReportingGroupBox->setTitle(tr("Crash reporting"));
|
||||
crashReportingLabel.setText(tr("Send crash reports:"));
|
||||
crashReportingBox.setItemText(static_cast<int>(PersonalSettings::CrashReportingMode::Ask), tr("Ask every time"));
|
||||
crashReportingBox.setItemText(static_cast<int>(PersonalSettings::CrashReportingMode::AlwaysSend),
|
||||
tr("Always send"));
|
||||
crashReportingBox.setItemText(static_cast<int>(PersonalSettings::CrashReportingMode::NeverSend), tr("Never send"));
|
||||
|
||||
// We can't change the strings after they're put into the QComboBox, so this is our workaround
|
||||
int oldIndex = updateReleaseChannelBox.currentIndex();
|
||||
updateReleaseChannelBox.clear();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ public:
|
|||
void retranslateUi() override;
|
||||
|
||||
static QStringList findQmFiles();
|
||||
static QString languageName(const QString &lang);
|
||||
|
||||
signals:
|
||||
/// Request to re-import the card database with the newly selected card language
|
||||
|
|
@ -49,6 +48,7 @@ private slots:
|
|||
private:
|
||||
QGroupBox *languageGroupBox;
|
||||
QGroupBox *versionGroupBox;
|
||||
QGroupBox *crashReportingGroupBox;
|
||||
QGroupBox *cardDatabaseGroupBox;
|
||||
QGroupBox *startupGroupBox;
|
||||
QGroupBox *pathsGroupBox;
|
||||
|
|
@ -70,6 +70,9 @@ private:
|
|||
QCheckBox updateNotificationCheckBox;
|
||||
QCheckBox newVersionOracleCheckBox;
|
||||
|
||||
QLabel crashReportingLabel;
|
||||
QComboBox crashReportingBox;
|
||||
|
||||
QLabel startupCardUpdateCheckBehaviorLabel;
|
||||
QComboBox startupCardUpdateCheckBehaviorSelector;
|
||||
QLabel cardUpdateCheckIntervalLabel;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <QStringLiteral>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
ShortcutSettingsPage::ShortcutSettingsPage()
|
||||
|
|
@ -112,16 +113,15 @@ void ShortcutSettingsPage::currentItemChanged(const QString &key)
|
|||
|
||||
void ShortcutSettingsPage::resetShortcuts()
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Restore all default shortcuts"),
|
||||
tr("Do you really want to restore all default shortcuts?")) == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(this, tr("Restore all default shortcuts"),
|
||||
tr("Do you really want to restore all default shortcuts?"))) {
|
||||
SettingsCache::instance().shortcuts().resetAllShortcuts();
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutSettingsPage::clearShortcuts()
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Clear all default shortcuts"),
|
||||
tr("Do you really want to clear all shortcuts?")) == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(this, tr("Clear all default shortcuts"), tr("Do you really want to clear all shortcuts?"))) {
|
||||
SettingsCache::instance().shortcuts().clearAllShortcuts();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/personal_settings.h>
|
||||
#include <libcockatrice/settings/visual_deck_storage_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
enum visualDeckStoragePromptForConversionIndex
|
||||
|
|
@ -248,8 +249,7 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
|
|||
|
||||
updateCommanderSpellbookUiState();
|
||||
|
||||
auto *labelLayout = new QHBoxLayout;
|
||||
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *labelLayout = QtUtils::makeLayout<QHBoxLayout>();
|
||||
labelLayout->addWidget(&commanderSpellbookIntegrationUseOfficialBracketNamesLabel);
|
||||
labelLayout->addWidget(&commanderSpellbookIntegrationUseOfficialBracketNamesExplainer);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../deck_loader/deck_file_format.h"
|
||||
#include "../../deck_loader/deck_loader.h"
|
||||
#include "../../deck_loader/loaded_deck.h"
|
||||
#include "../cards/additional_info/deck_color_identity.h"
|
||||
#include "../cards/card_info_frame_widget.h"
|
||||
#include "../client/network/interfaces/deck_stats_interface.h"
|
||||
|
|
@ -55,6 +53,8 @@
|
|||
#include <QUrl>
|
||||
#include <google/protobuf/stubs/port.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/models/database/card_database_model.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_upload.pb.h>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "../../../../../../client/settings/cache_settings.h"
|
||||
#include "../../../../../deck_loader/card_node_function.h"
|
||||
#include "../../../../../deck_loader/loaded_deck.h"
|
||||
#include "../../../../cards/deck_card_zone_display_widget.h"
|
||||
#include "../../../../visual_deck_editor/visual_deck_display_options_widget.h"
|
||||
#include "../../../../visual_deck_editor/visual_deck_editor_widget.h"
|
||||
|
|
@ -30,7 +29,9 @@
|
|||
#include <QVariant>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/import/card_name_normalizer.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
|
|
@ -66,10 +67,7 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
|
|||
connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::groupCriteriaChanged, this,
|
||||
&ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange);
|
||||
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
layout->addWidget(scrollArea);
|
||||
|
||||
|
|
@ -240,7 +238,5 @@ void ArchidektApiResponseDeckDisplayWidget::constructZoneWidgetsFromDeckListMode
|
|||
void ArchidektApiResponseDeckDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <version_string.h>
|
||||
|
||||
|
|
@ -241,9 +242,7 @@ void ArchidektApiResponseDeckEntryDisplayWidget::updateScaledPreview()
|
|||
void ArchidektApiResponseDeckEntryDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
}
|
||||
|
||||
void ArchidektApiResponseDeckEntryDisplayWidget::actRequestNavigationToDeck()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSlider>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsDisplayWidget(
|
||||
|
|
@ -19,16 +21,14 @@ ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsD
|
|||
CardSizeWidget *_cardSizeSlider)
|
||||
: QWidget(parent), cardSizeSlider(_cardSizeSlider)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this);
|
||||
setLayout(layout);
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
||||
cardSizeSlider->enableCtrlScrollResize(flowWidget);
|
||||
|
||||
imageNetworkManager = new QNetworkAccessManager(this);
|
||||
imageNetworkManager->setTransferTimeout(); // Use Qt's default timeout
|
||||
imageNetworkManager = NetworkManagerFactory::create(this);
|
||||
imageNetworkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
|
||||
// Add widgets for deck listings
|
||||
|
|
@ -64,7 +64,5 @@ void ArchidektApiResponseDeckListingsDisplayWidget::append(const ArchidektDeckLi
|
|||
void ArchidektApiResponseDeckListingsDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@
|
|||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
#include <version_string.h>
|
||||
|
|
@ -57,8 +59,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor)
|
|||
: Tab(_tabSupervisor), currentPage(1), isLoadingMore(false), isListMode(true)
|
||||
{
|
||||
// Initialize network
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
networkManager->setTransferTimeout();
|
||||
networkManager = NetworkManagerFactory::create(this);
|
||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
connect(networkManager, &QNetworkAccessManager::finished, this, &TabArchidekt::processApiJson);
|
||||
|
||||
|
|
@ -80,9 +81,7 @@ void TabArchidekt::initializeUi()
|
|||
{
|
||||
// Main container
|
||||
container = new QWidget(this);
|
||||
mainLayout = new QVBoxLayout(container);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout = QtUtils::makeLayout<QVBoxLayout>(container, 0);
|
||||
|
||||
// Primary toolbar (most important filters)
|
||||
primaryToolbar = new QWidget(container);
|
||||
|
|
@ -105,9 +104,7 @@ void TabArchidekt::initializeUi()
|
|||
|
||||
// Color filter (inline)
|
||||
QWidget *colorWidget = new QWidget(primaryToolbar);
|
||||
QHBoxLayout *colorLayout = new QHBoxLayout(colorWidget);
|
||||
colorLayout->setContentsMargins(0, 0, 0, 0);
|
||||
colorLayout->setSpacing(2);
|
||||
QHBoxLayout *colorLayout = QtUtils::makeLayout<QHBoxLayout>(colorWidget, 2);
|
||||
|
||||
QString colorIdentity = "WUBRG";
|
||||
for (const QChar &color : colorIdentity) {
|
||||
|
|
@ -186,15 +183,10 @@ void TabArchidekt::initializeUi()
|
|||
secondaryToolbarLayout->setSpacing(6);
|
||||
|
||||
// Scrollable results area
|
||||
scrollArea = new QScrollArea(container);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea = QtUtils::makeScrollArea(container);
|
||||
|
||||
resultsContainer = new QWidget();
|
||||
resultsLayout = new QVBoxLayout(resultsContainer);
|
||||
resultsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
resultsLayout->setSpacing(0);
|
||||
resultsLayout = QtUtils::makeLayout<QVBoxLayout>(resultsContainer, 0);
|
||||
|
||||
scrollArea->setWidget(resultsContainer);
|
||||
|
||||
|
|
@ -228,9 +220,9 @@ void TabArchidekt::setupFilterWidgets()
|
|||
// Advanced filters (in secondary toolbar)
|
||||
|
||||
// EDH Bracket
|
||||
auto *bracketLabel = new QLabel(tr("Bracket:"), secondaryToolbar);
|
||||
bracketLabel = new QLabel(secondaryToolbar);
|
||||
edhBracketCombo = new QComboBox(secondaryToolbar);
|
||||
edhBracketCombo->addItem(tr("Any"));
|
||||
edhBracketCombo->addItem(QString());
|
||||
edhBracketCombo->addItems({"1", "2", "3", "4", "5"});
|
||||
edhBracketCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
|
|
@ -284,7 +276,6 @@ void TabArchidekt::setupFilterWidgets()
|
|||
sizeLayout->setContentsMargins(4, 4, 4, 4);
|
||||
|
||||
minDeckSizeSpin = new QSpinBox(sizeContainer);
|
||||
minDeckSizeSpin->setSpecialValueText(tr("Any"));
|
||||
minDeckSizeSpin->setRange(0, 200);
|
||||
minDeckSizeSpin->setValue(0);
|
||||
|
||||
|
|
@ -292,7 +283,8 @@ void TabArchidekt::setupFilterWidgets()
|
|||
minDeckSizeLogicCombo->addItems({"Exact", "≥", "≤"});
|
||||
minDeckSizeLogicCombo->setCurrentIndex(1);
|
||||
|
||||
sizeLayout->addWidget(new QLabel(tr("Cards:"), sizeContainer));
|
||||
cardsLabel = new QLabel(sizeContainer);
|
||||
sizeLayout->addWidget(cardsLabel);
|
||||
sizeLayout->addWidget(minDeckSizeSpin);
|
||||
sizeLayout->addWidget(minDeckSizeLogicCombo);
|
||||
|
||||
|
|
@ -349,9 +341,9 @@ void TabArchidekt::connectSignals()
|
|||
});
|
||||
|
||||
// Format filters trigger search
|
||||
connect(edhBracketCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
||||
connect(edhBracketCombo, &QComboBox::currentIndexChanged, this, &TabArchidekt::doSearch);
|
||||
connect(minDeckSizeSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||
connect(minDeckSizeLogicCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
||||
connect(minDeckSizeLogicCombo, &QComboBox::currentIndexChanged, this, &TabArchidekt::doSearch);
|
||||
|
||||
// Allow Enter key in text fields to trigger search
|
||||
connect(nameField, &QLineEdit::returnPressed, this, &TabArchidekt::doSearchImmediate);
|
||||
|
|
@ -401,8 +393,15 @@ void TabArchidekt::retranslateUi()
|
|||
commandersField->setPlaceholderText(tr("Commander..."));
|
||||
deckTagNameField->setPlaceholderText(tr("Tag..."));
|
||||
|
||||
bracketLabel->setText(tr("Bracket:"));
|
||||
// Index 0 is the "Any" placeholder added in setupFilterWidgets()
|
||||
edhBracketCombo->setItemText(0, tr("Any"));
|
||||
|
||||
formatButton->setButtonText(tr("Formats"));
|
||||
deckSizeButton->setButtonText(tr("Deck Size"));
|
||||
cardsLabel->setText(tr("Cards:"));
|
||||
minDeckSizeSpin->setSpecialValueText(tr("Any"));
|
||||
minDeckSizeLogicCombo->setItemText(0, tr("Exact"));
|
||||
|
||||
searchButton->setText(tr("Search"));
|
||||
|
||||
|
|
@ -492,13 +491,17 @@ QString TabArchidekt::buildSearchUrl()
|
|||
query.addQueryItem("size", QString::number(minDeckSizeSpin->value()));
|
||||
|
||||
QString logic = "GTE";
|
||||
QString selected = minDeckSizeLogicCombo->currentText();
|
||||
if (selected == "≥") {
|
||||
logic = "GTE";
|
||||
} else if (selected == "≤") {
|
||||
logic = "LTE";
|
||||
} else {
|
||||
logic = "";
|
||||
// Index order matches setupFilterWidgets(): 0 = Exact, 1 = ≥, 2 = ≤
|
||||
switch (minDeckSizeLogicCombo->currentIndex()) {
|
||||
case 1:
|
||||
logic = "GTE";
|
||||
break;
|
||||
case 2:
|
||||
logic = "LTE";
|
||||
break;
|
||||
default:
|
||||
logic = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!logic.isEmpty()) {
|
||||
|
|
@ -599,11 +602,7 @@ void TabArchidekt::processTopDecksResponse(QJsonObject reply)
|
|||
|
||||
// New search → clear everything
|
||||
if (currentPage == 1) {
|
||||
QLayoutItem *item;
|
||||
while ((item = resultsLayout->takeAt(0)) != nullptr) {
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(resultsLayout, QtUtils::DeletePolicy::Immediate);
|
||||
|
||||
listingsWidget = new ArchidektApiResponseDeckListingsDisplayWidget(resultsContainer, deckData, cardSizeSlider);
|
||||
|
||||
|
|
@ -629,11 +628,7 @@ void TabArchidekt::processDeckResponse(QJsonObject reply)
|
|||
isListMode = false;
|
||||
|
||||
// Clear existing results for single deck view
|
||||
QLayoutItem *item;
|
||||
while ((item = resultsLayout->takeAt(0)) != nullptr) {
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(resultsLayout, QtUtils::DeletePolicy::Immediate);
|
||||
|
||||
auto display = new ArchidektApiResponseDeckDisplayWidget(resultsContainer, deckData, cardSizeSlider);
|
||||
connect(display, &ArchidektApiResponseDeckDisplayWidget::requestNavigation, this, &TabArchidekt::actNavigatePage);
|
||||
|
|
|
|||
|
|
@ -287,9 +287,11 @@ private:
|
|||
|
||||
SettingsButtonWidget *formatButton; ///< Collapsible button for format filters
|
||||
QVector<QCheckBox *> formatChecks; ///< Individual checkboxes for each format
|
||||
QLabel *bracketLabel; ///< Label for EDH bracket selection
|
||||
QComboBox *edhBracketCombo; ///< Dropdown for EDH bracket selection
|
||||
|
||||
SettingsButtonWidget *deckSizeButton; ///< Collapsible button for deck size filter
|
||||
QLabel *cardsLabel; ///< Label for deck size selection
|
||||
QSpinBox *minDeckSizeSpin; ///< Spinner to select minimum deck size
|
||||
QComboBox *minDeckSizeLogicCombo; ///< Combo box for size comparison logic
|
||||
|
||||
|
|
@ -300,4 +302,4 @@ private:
|
|||
CardInfoPtr cardToQuery; ///< Optional pre-selected card for initial filtering
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_TAB_ARCHIDEKT_H
|
||||
#endif // COCKATRICE_TAB_ARCHIDEKT_H
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/settings/deck_editor_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
template <class T> class QSharedPointer;
|
||||
|
|
@ -44,8 +45,7 @@ CommanderBracketWidget::CommanderBracketWidget(QWidget *parent) : QWidget(parent
|
|||
|
||||
connect(bracketRefreshButton, &QToolButton::clicked, this, &CommanderBracketWidget::requestBracketEstimate);
|
||||
|
||||
auto *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *layout = QtUtils::makeLayout<QGridLayout>(this);
|
||||
layout->addWidget(bracketLabel, 0, 0);
|
||||
|
||||
auto *bracketRow = new QHBoxLayout;
|
||||
|
|
@ -110,8 +110,7 @@ bool CommanderBracketWidget::promptCommanderSpellbookIntegration()
|
|||
|
||||
// Create label + explainer button
|
||||
auto *labelWidget = new QWidget(&dialog);
|
||||
auto *labelLayout = new QHBoxLayout(labelWidget);
|
||||
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *labelLayout = QtUtils::makeLayout<QHBoxLayout>(labelWidget);
|
||||
|
||||
auto *namingLabel = new QLabel(tr("Bracket naming:"), labelWidget);
|
||||
auto *explainerButton = new QToolButton(labelWidget);
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@
|
|||
#include <QStringLiteral>
|
||||
#include <QUrl>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/commander_bracket_settings.h>
|
||||
|
||||
static const QUrl COMMANDER_BRACKET_JSON_URL(QStringLiteral("https://cockatrice.github.io/commander-brackets.json"));
|
||||
|
||||
HandleCommanderBrackets::HandleCommanderBrackets(QObject *parent)
|
||||
: QObject(parent), nam(new QNetworkAccessManager(this)), reply(nullptr)
|
||||
: QObject(parent), nam(NetworkManagerFactory::create(this)), reply(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <QSlider>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWidget(
|
||||
|
|
@ -47,15 +48,7 @@ EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWi
|
|||
|
||||
layout->addWidget(backgroundPlateWidget);
|
||||
|
||||
QWidget *currentParent = parentWidget();
|
||||
TabEdhRecMain *parentTab = nullptr;
|
||||
|
||||
while (currentParent) {
|
||||
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
auto *parentTab = QtUtils::findParentOfType<TabEdhRecMain>(this);
|
||||
|
||||
if (parentTab) {
|
||||
cardPictureWidget->setScaleFactor(parentTab->getCardSizeSlider()->getSlider()->value());
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <QSlider>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCommanderDetailsDisplayWidget(
|
||||
|
|
@ -33,15 +34,7 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom
|
|||
commanderPicture = new CardInfoPictureWidget(this);
|
||||
commanderPicture->setCard(CardDatabaseManager::query()->getCard({commanderDetails.getName()}));
|
||||
|
||||
QWidget *currentParent = parentWidget();
|
||||
TabEdhRecMain *parentTab = nullptr;
|
||||
|
||||
while (currentParent) {
|
||||
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
auto *parentTab = QtUtils::findParentOfType<TabEdhRecMain>(this);
|
||||
|
||||
if (parentTab) {
|
||||
connect(parentTab->getCardSizeSlider()->getSlider(), &QSlider::valueChanged, commanderPicture,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <QStringList>
|
||||
#include <QStringListModel>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
|
|
@ -61,10 +62,7 @@ EdhrecCommanderApiResponseDisplayWidget::EdhrecCommanderApiResponseDisplayWidget
|
|||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(splitter);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea = QtUtils::makeScrollArea(splitter);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
|
|
@ -109,9 +107,7 @@ void EdhrecCommanderApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
|||
{
|
||||
QWidget::resizeEvent(event);
|
||||
qDebug() << event->size();
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <QGridLayout>
|
||||
#include <QObject>
|
||||
#include <QPushButton>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigationWidget(
|
||||
QWidget *parent,
|
||||
|
|
@ -40,15 +41,7 @@ EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigation
|
|||
layout->addWidget(bracketNavigationWidget, 1, 0, 1, 2);
|
||||
layout->addWidget(budgetNavigationWidget, 2, 0, 1, 2);
|
||||
|
||||
QWidget *currentParent = parentWidget();
|
||||
TabEdhRecMain *parentTab = nullptr;
|
||||
|
||||
while (currentParent) {
|
||||
if ((parentTab = qobject_cast<TabEdhRecMain *>(currentParent))) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
auto *parentTab = QtUtils::findParentOfType<TabEdhRecMain>(this);
|
||||
|
||||
if (parentTab) {
|
||||
connect(comboPushButton, &QPushButton::clicked, this,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
EdhrecTopCardsApiResponseDisplayWidget::EdhrecTopCardsApiResponseDisplayWidget(QWidget *parent,
|
||||
|
|
@ -29,10 +30,7 @@ EdhrecTopCardsApiResponseDisplayWidget::EdhrecTopCardsApiResponseDisplayWidget(Q
|
|||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
|
|
@ -45,9 +43,7 @@ EdhrecTopCardsApiResponseDisplayWidget::EdhrecTopCardsApiResponseDisplayWidget(Q
|
|||
void EdhrecTopCardsApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <QScrollArea>
|
||||
#include <QSize>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
|
|
@ -33,10 +34,7 @@ EdhrecTopCommandersApiResponseDisplayWidget::EdhrecTopCommandersApiResponseDispl
|
|||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
|
|
@ -50,9 +48,7 @@ void EdhrecTopCommandersApiResponseDisplayWidget::resizeEvent(QResizeEvent *even
|
|||
{
|
||||
QWidget::resizeEvent(event);
|
||||
qDebug() << event->size();
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
EdhrecTopTagsApiResponseDisplayWidget::EdhrecTopTagsApiResponseDisplayWidget(QWidget *parent,
|
||||
|
|
@ -29,10 +30,7 @@ EdhrecTopTagsApiResponseDisplayWidget::EdhrecTopTagsApiResponseDisplayWidget(QWi
|
|||
}
|
||||
|
||||
// Create a QScrollArea to hold the card display widgets
|
||||
scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea = QtUtils::makeScrollArea(this);
|
||||
|
||||
// Set the cardDisplayLayout inside the scroll area
|
||||
auto scrollWidget = new QWidget(scrollArea);
|
||||
|
|
@ -45,9 +43,7 @@ EdhrecTopTagsApiResponseDisplayWidget::EdhrecTopTagsApiResponseDisplayWidget(QWi
|
|||
void EdhrecTopTagsApiResponseDisplayWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
layout->update();
|
||||
QtUtils::relayout(layout);
|
||||
if (scrollArea && scrollArea->widget()) {
|
||||
scrollArea->widget()->resize(event->size());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@
|
|||
#include <QRegularExpression>
|
||||
#include <QSharedPointer>
|
||||
#include <QUrl>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <qlogging.h>
|
||||
|
||||
class TabSupervisor;
|
||||
|
||||
TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
networkManager->setTransferTimeout(); // Use Qt's default timeout
|
||||
networkManager = NetworkManagerFactory::create(this);
|
||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
connect(networkManager, &QNetworkAccessManager::finished, this, &TabEdhRec::processApiJson);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "tab_edhrec_main.h"
|
||||
|
||||
#include "../../../../../client/settings/cache_settings.h"
|
||||
#include "../../../../deck_loader/loaded_deck.h"
|
||||
#include "../../../cards/card_size_widget.h"
|
||||
#include "../../../quick_settings/settings_button_widget.h"
|
||||
#include "../../../utility/completer_utils.h"
|
||||
|
|
@ -28,10 +27,12 @@
|
|||
#include <QRegularExpression>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/database/card_database_querier.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/models/database/card/card_completer_proxy_model.h>
|
||||
#include <libcockatrice/models/database/card/card_search_model.h>
|
||||
#include <libcockatrice/models/database/card_database_display_model.h>
|
||||
#include <libcockatrice/models/database/card_database_model.h>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <version_string.h>
|
||||
|
||||
|
|
@ -44,8 +45,7 @@ static bool canBeCommander(const CardInfoPtr &cardInfo)
|
|||
|
||||
TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
networkManager->setTransferTimeout(); // Use Qt's default timeout
|
||||
networkManager = NetworkManagerFactory::create(this);
|
||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
||||
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *)));
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue