mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
[Fonts] Retire max font size setting into theme CardText min/max range
The per-card text size lives in the theme now, so a theme can pick the size it was designed for instead of every user overriding it globally. - CardText stores family + max point size; CardTextMinSize stores the overlay shrink-to-fit floor (omitted from fonts.toml when 1). - ThemeManager::cardTextMaxSize()/cardTextMinSize() resolve the range with the retired defaults (12 max, 1 min), and cardTextMaxSize() falls back to the CardText role size that #7541 made live. - The overlay widget starts at the explicit size when one is set (deck preview), else the theme max, and shrinks down to the theme floor. - The appearance page's "Maximum font size" spin is gone; the range is edited in the theme's font grid, whose min/max spins now keep the saved range consistent with each other. - Migration: appearance/maxFontSize is seeded into every user theme folder and into the active theme's folder (created when the theme is a shipped one that only exists in the system directory). The retired game/maxfontsize -> appearance/maxFontSize mapping stays, so users still on global.ini keep their value through the chain.
This commit is contained in:
parent
ed119cc940
commit
e87cd80c9b
19 changed files with 697 additions and 49 deletions
|
|
@ -137,6 +137,9 @@ SettingsCache::SettingsCache()
|
|||
// before that rename (idempotent, safe to run on every startup, portable builds included).
|
||||
SettingsMigration::migrateGameFiltersToCamelCase(settingsPath);
|
||||
|
||||
// Retire appearance/maxFontSize into per-theme CardText max (idempotent, safe every startup).
|
||||
SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath);
|
||||
|
||||
shortcutsSettings = new ShortcutsSettings(settingsPath, this);
|
||||
cardDatabaseSettings = new CardDatabaseSettings(settingsPath, this);
|
||||
serversSettings = new ServersSettings(settingsPath, this);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/settings/appearance_settings.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/debug_settings.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -144,8 +143,8 @@ QSizeF AbstractCardItem::getTranslatedSize(QPainter *painter) const
|
|||
|
||||
void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle)
|
||||
{
|
||||
const int MAX_FONT_SIZE = SettingsCache::instance().appearance().getMaxFontSize();
|
||||
const int fontSize = std::max(9, MAX_FONT_SIZE);
|
||||
const int minSize = std::max(1, themeManager->cardTextMinSize());
|
||||
const int fontSize = std::max(minSize, std::max(1, themeManager->cardTextMaxSize()));
|
||||
|
||||
QRectF totalBoundingRect = painter->combinedTransform().mapRect(boundingRect());
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
static constexpr int MIN_FONT_SIZE = 6;
|
||||
static constexpr int MAX_FONT_SIZE = 72;
|
||||
static constexpr int CARD_TEXT_MIN_SIZE = 1;
|
||||
static constexpr int CARD_TEXT_MAX_SIZE = 12;
|
||||
static constexpr int FALLBACK_FONT_SIZE = 10;
|
||||
|
||||
static const QMap<AppFont::Role, const char *> APP_FONT_DESCRIPTIONS = {
|
||||
|
|
@ -46,6 +48,7 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
|
|||
grid->setColumnStretch(0, 0);
|
||||
grid->setColumnStretch(1, 1);
|
||||
grid->setColumnStretch(2, 0);
|
||||
grid->setColumnStretch(3, 0);
|
||||
|
||||
QMetaEnum fontEnum = QMetaEnum::fromType<AppFont::Role>();
|
||||
for (int i = 0; i < fontEnum.keyCount(); ++i) {
|
||||
|
|
@ -58,6 +61,30 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
|
|||
grid->addWidget(familyCombo, i, 1);
|
||||
connect(familyCombo, &QFontComboBox::currentFontChanged, this, [this] { emit fontsChanged(); });
|
||||
|
||||
if (role == AppFont::CardText) {
|
||||
auto *minSpin = new QSpinBox(this);
|
||||
minSpin->setRange(CARD_TEXT_MIN_SIZE, MAX_FONT_SIZE);
|
||||
grid->addWidget(minSpin, i, 2);
|
||||
connect(minSpin, &QSpinBox::valueChanged, this, [this] { emit fontsChanged(); });
|
||||
|
||||
auto *maxSpin = new QSpinBox(this);
|
||||
maxSpin->setRange(MIN_FONT_SIZE, MAX_FONT_SIZE);
|
||||
grid->addWidget(maxSpin, i, 3);
|
||||
connect(maxSpin, &QSpinBox::valueChanged, this, [this] { emit fontsChanged(); });
|
||||
|
||||
// Keep the range consistent: otherwise min 20 / max 12 saves as-is
|
||||
// and cardTextMaxSize() silently clamps the max up to the min, so
|
||||
// the max spin shows a size that is never used. Moving one spin
|
||||
// pulls the other's bound along.
|
||||
connect(minSpin, &QSpinBox::valueChanged, this, [maxSpin](int value) { maxSpin->setMinimum(value); });
|
||||
connect(maxSpin, &QSpinBox::valueChanged, this, [minSpin](int value) { minSpin->setMaximum(value); });
|
||||
|
||||
label->setBuddy(familyCombo);
|
||||
|
||||
fontRows[role] = {label, familyCombo, maxSpin, minSpin};
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *sizeSpin = new QSpinBox(this);
|
||||
sizeSpin->setRange(MIN_FONT_SIZE, MAX_FONT_SIZE);
|
||||
grid->addWidget(sizeSpin, i, 2);
|
||||
|
|
@ -65,7 +92,7 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
|
|||
|
||||
label->setBuddy(familyCombo);
|
||||
|
||||
fontRows[role] = {label, familyCombo, sizeSpin};
|
||||
fontRows[role] = {label, familyCombo, sizeSpin, nullptr};
|
||||
}
|
||||
|
||||
retranslateUi();
|
||||
|
|
@ -89,6 +116,37 @@ void FontGridWidget::loadFonts(const FontConfig &cfg)
|
|||
const QFont font = cfg.fonts.value(role, fallback);
|
||||
row.familyCombo->setCurrentFont(font);
|
||||
|
||||
if (role == AppFont::CardText && row.minSpin != nullptr) {
|
||||
// A previous load may have narrowed the linked bounds (min 20 pulls
|
||||
// max up to 20), which would clamp the values written below. Reset
|
||||
// both to the full range first; the valueChanged lambdas re-link
|
||||
// them as the values are set.
|
||||
row.minSpin->setRange(CARD_TEXT_MIN_SIZE, MAX_FONT_SIZE);
|
||||
row.sizeSpin->setRange(MIN_FONT_SIZE, MAX_FONT_SIZE);
|
||||
|
||||
// The max spin is a size-only override. Without CardTextMaxSize it
|
||||
// follows the CardText family size, and without CardText at all it
|
||||
// must match cardTextMaxSize()'s default rather than the system
|
||||
// startup size, which currentFontConfig() would otherwise bake in.
|
||||
const int fallbackMax = cfg.fonts.contains(role) ? font.pointSize() : CARD_TEXT_MAX_SIZE;
|
||||
int maxSize = cfg.cardTextMaxSize > 0 ? cfg.cardTextMaxSize : fallbackMax;
|
||||
if (maxSize <= 0) {
|
||||
maxSize = CARD_TEXT_MAX_SIZE;
|
||||
}
|
||||
int minSize = cfg.cardTextMinSize > 0 ? cfg.cardTextMinSize : themeManager->cardTextMinSize();
|
||||
minSize = qBound(CARD_TEXT_MIN_SIZE, minSize, MAX_FONT_SIZE);
|
||||
maxSize = qBound(MIN_FONT_SIZE, maxSize, MAX_FONT_SIZE);
|
||||
if (maxSize < minSize) {
|
||||
maxSize = minSize;
|
||||
}
|
||||
|
||||
// Max first, then min: each value re-applies the linked bound, so
|
||||
// the pair ends up consistent instead of clamping the smaller one.
|
||||
row.sizeSpin->setValue(maxSize);
|
||||
row.minSpin->setValue(minSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
int size = font.pointSize();
|
||||
if (size <= 0) {
|
||||
size = qApp->font().pointSize();
|
||||
|
|
@ -114,6 +172,11 @@ FontConfig FontGridWidget::currentFontConfig() const
|
|||
QFont font(row.familyCombo->currentFont().family());
|
||||
font.setPointSize(row.sizeSpin->value());
|
||||
cfg.fonts[role] = font;
|
||||
|
||||
if (role == AppFont::CardText && row.minSpin != nullptr) {
|
||||
cfg.cardTextMinSize = row.minSpin->value();
|
||||
cfg.cardTextMaxSize = row.sizeSpin->value();
|
||||
}
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
|
@ -129,6 +192,15 @@ void FontGridWidget::retranslateUi()
|
|||
row.label->setToolTip(tr(APP_FONT_DESCRIPTIONS.value(role, "")));
|
||||
row.familyCombo->setToolTip(tr(APP_FONT_DESCRIPTIONS.value(role, "")));
|
||||
row.familyCombo->setAccessibleName(roleDisplayName(role));
|
||||
if (role == AppFont::CardText && row.minSpin != nullptr) {
|
||||
row.minSpin->setToolTip(tr("Minimum card text size"));
|
||||
row.minSpin->setAccessibleName(tr("Minimum card text size"));
|
||||
row.minSpin->setSuffix(tr(" pt"));
|
||||
row.sizeSpin->setToolTip(tr("Maximum card text size"));
|
||||
row.sizeSpin->setAccessibleName(tr("Maximum card text size"));
|
||||
row.sizeSpin->setSuffix(tr(" pt"));
|
||||
continue;
|
||||
}
|
||||
row.sizeSpin->setToolTip(tr("Point size for %1").arg(roleDisplayName(role)));
|
||||
row.sizeSpin->setAccessibleName(tr("Point size for %1").arg(roleDisplayName(role)));
|
||||
row.sizeSpin->setSuffix(tr(" pt"));
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ private:
|
|||
QLabel *label = nullptr;
|
||||
QFontComboBox *familyCombo = nullptr;
|
||||
QSpinBox *sizeSpin = nullptr;
|
||||
QSpinBox *minSpin = nullptr; // CardText only: shrink-to-fit floor
|
||||
};
|
||||
|
||||
QMap<AppFont::Role, FontRow> fontRows;
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ QPalette PaletteConfig::apply(QPalette base) const
|
|||
|
||||
bool FontConfig::hasFonts() const
|
||||
{
|
||||
return !fonts.isEmpty();
|
||||
return !fonts.isEmpty() || cardTextMinSize != 1 || cardTextMaxSize > 0;
|
||||
}
|
||||
|
||||
QString FontConfig::toToml() const
|
||||
|
|
@ -334,6 +334,14 @@ QString FontConfig::toToml() const
|
|||
entries += QString("%1 = \"%2, %3\"\n").arg(QString(roleName), -20).arg(family).arg(pointSize);
|
||||
}
|
||||
|
||||
if (cardTextMinSize != 1) {
|
||||
entries += QString("%1 = %2\n").arg(QStringLiteral("CardTextMinSize"), -20).arg(cardTextMinSize);
|
||||
}
|
||||
|
||||
if (cardTextMaxSize > 0) {
|
||||
entries += QString("%1 = %2\n").arg(QStringLiteral("CardTextMaxSize"), -20).arg(cardTextMaxSize);
|
||||
}
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
|
@ -401,6 +409,31 @@ FontConfig FontConfig::fromFile(const QString &filePath)
|
|||
value = value.mid(1, value.size() - 2).trimmed();
|
||||
}
|
||||
|
||||
// CardText size bounds: bare integers, e.g. CardTextMinSize = 2.
|
||||
// Tolerate quoting like the family entries; skip invalid lines.
|
||||
QString sizeKey = key;
|
||||
if (sizeKey.startsWith("AppFont::")) {
|
||||
sizeKey = sizeKey.mid(QStringLiteral("AppFont::").size());
|
||||
}
|
||||
if (sizeKey.compare("CardTextMinSize", Qt::CaseInsensitive) == 0) {
|
||||
bool ok = false;
|
||||
int minSize = value.trimmed().toInt(&ok);
|
||||
if (!ok || minSize <= 0) {
|
||||
continue;
|
||||
}
|
||||
cfg.cardTextMinSize = minSize;
|
||||
continue;
|
||||
}
|
||||
if (sizeKey.compare("CardTextMaxSize", Qt::CaseInsensitive) == 0) {
|
||||
bool ok = false;
|
||||
int maxSize = value.trimmed().toInt(&ok);
|
||||
if (!ok || maxSize <= 0) {
|
||||
continue;
|
||||
}
|
||||
cfg.cardTextMaxSize = maxSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
int comma = value.lastIndexOf(',');
|
||||
|
||||
if (comma < 0) {
|
||||
|
|
|
|||
|
|
@ -84,15 +84,23 @@ struct PaletteConfig
|
|||
* @code
|
||||
* [Fonts]
|
||||
* InterfaceDefault = "Inter, 10"
|
||||
* CardText = "Noto Serif, 12"
|
||||
* CardTextMinSize = 1
|
||||
* @endcode
|
||||
* CardText stores family + MAX size; cardTextMinSize stores the overlay
|
||||
* shrink-to-fit floor (omitted from TOML when 1).
|
||||
*/
|
||||
struct FontConfig
|
||||
{
|
||||
QMap<AppFont::Role, QFont> fonts;
|
||||
int cardTextMinSize = 1;
|
||||
/// Optional size-only maximum for CardText, used by the retired maxFontSize
|
||||
/// migration so seeding it does not have to pin a family.
|
||||
int cardTextMaxSize = 0;
|
||||
|
||||
bool operator==(const FontConfig &rhs) const
|
||||
{
|
||||
return fonts == rhs.fonts;
|
||||
return fonts == rhs.fonts && cardTextMinSize == rhs.cardTextMinSize && cardTextMaxSize == rhs.cardTextMaxSize;
|
||||
}
|
||||
bool operator!=(const FontConfig &rhs) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -681,6 +681,7 @@ QFont ThemeManager::cardTextFont() const
|
|||
}
|
||||
QFont serifFont(QStringLiteral("Serif"));
|
||||
serifFont.setStyleHint(QFont::Serif);
|
||||
serifFont.setPointSize(12);
|
||||
return serifFont;
|
||||
}
|
||||
|
||||
|
|
@ -702,6 +703,29 @@ QFont ThemeManager::defaultAppFont(AppFont::Role /*role*/) const
|
|||
return defaultFont;
|
||||
}
|
||||
|
||||
int ThemeManager::cardTextMaxSize() const
|
||||
{
|
||||
if (currentFonts.cardTextMaxSize > 0) {
|
||||
return currentFonts.cardTextMaxSize;
|
||||
}
|
||||
const auto it = currentFonts.fonts.constFind(AppFont::CardText);
|
||||
if (it != currentFonts.fonts.constEnd()) {
|
||||
const int size = it.value().pointSize();
|
||||
if (size > 0) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return 12;
|
||||
}
|
||||
|
||||
int ThemeManager::cardTextMinSize() const
|
||||
{
|
||||
if (currentFonts.cardTextMinSize > 0) {
|
||||
return currentFonts.cardTextMinSize;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ThemeManager::themeChangedSlot()
|
||||
{
|
||||
QString themeName = SettingsCache::instance().getThemeName();
|
||||
|
|
|
|||
|
|
@ -169,6 +169,11 @@ public:
|
|||
* "what this role resolves to" must use this, because appFont()
|
||||
* reflects a live preview while the editor is open. */
|
||||
QFont defaultAppFont(AppFont::Role role) const;
|
||||
/** @brief CardText max size: stored CardTextMaxSize, else the CardText
|
||||
* point size, else 12. */
|
||||
int cardTextMaxSize() const;
|
||||
/** @brief CardText shrink-to-fit floor: stored CardTextMinSize, else 1. */
|
||||
int cardTextMinSize() const;
|
||||
|
||||
QBrush &getBgBrush(Role zone);
|
||||
QBrush getExtraBgBrush(Role zone, int zoneId = 0);
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ void CardInfoPictureWithTextOverlayWidget::setOutlineColor(const QColor &color)
|
|||
void CardInfoPictureWithTextOverlayWidget::setFontSize(const int size)
|
||||
{
|
||||
fontSize = size > 0 ? size : 1;
|
||||
hasExplicitFontSize = true;
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -179,10 +180,12 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
|
|||
}
|
||||
|
||||
// Calculate the optimal font size. The family comes from the CardText
|
||||
// theme role; the fontSize member stays the starting size and the
|
||||
// shrink-to-fit logic below is unchanged.
|
||||
// theme role; the start size is the explicit size when set (e.g. deck
|
||||
// preview 24), else the theme max, shrinking down to the theme min floor.
|
||||
QFont font = themeManager->appFont(AppFont::CardText);
|
||||
int optimalFontSize = fontSize; // Start with the user-defined font size
|
||||
const int themeMin = std::max(1, themeManager->cardTextMinSize());
|
||||
const int themeMax = std::max(themeMin, std::max(1, themeManager->cardTextMaxSize()));
|
||||
int optimalFontSize = hasExplicitFontSize ? std::max(1, fontSize) : themeMax;
|
||||
const QFontMetrics baseMetrics(font);
|
||||
int textWidth = pixmapRect.width();
|
||||
|
||||
|
|
@ -200,7 +203,7 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
|
|||
}
|
||||
|
||||
--optimalFontSize;
|
||||
} while (optimalFontSize > 1);
|
||||
} while (optimalFontSize > themeMin);
|
||||
|
||||
// Apply the calculated font size
|
||||
painter.setFont(font);
|
||||
|
|
@ -227,7 +230,7 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
|
|||
int totalTextHeight = wrappedText.count('\n') * fontMetrics.height() + fontMetrics.height();
|
||||
|
||||
// Adjust font size if the total text height exceeds the pixmap height
|
||||
while (totalTextHeight > pixmapRect.height() && optimalFontSize > 1) {
|
||||
while (totalTextHeight > pixmapRect.height() && optimalFontSize > themeMin) {
|
||||
--optimalFontSize;
|
||||
font.setPointSize(optimalFontSize);
|
||||
painter.setFont(font);
|
||||
|
|
@ -297,7 +300,9 @@ QSize CardInfoPictureWithTextOverlayWidget::minimumSizeHint() const
|
|||
|
||||
// Get the font metrics for the overlay text
|
||||
QFont font = themeManager->appFont(AppFont::CardText);
|
||||
font.setPointSize(fontSize);
|
||||
const int themeMin = std::max(1, themeManager->cardTextMinSize());
|
||||
const int themeMax = std::max(themeMin, std::max(1, themeManager->cardTextMaxSize()));
|
||||
font.setPointSize(hasExplicitFontSize ? std::max(1, fontSize) : themeMax);
|
||||
const QFontMetrics fontMetrics(font);
|
||||
|
||||
// Calculate the height required for the text
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ private:
|
|||
QColor textColor;
|
||||
QColor outlineColor;
|
||||
int fontSize;
|
||||
bool hasExplicitFontSize = false;
|
||||
Qt::Alignment textAlignment;
|
||||
bool highlighted;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -283,20 +283,11 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
|||
connect(&roundCardCornersCheckBox, &QAbstractButton::toggled, &settings.cardsDisplay(),
|
||||
&CardsDisplaySettings::setRoundCardCorners);
|
||||
|
||||
connect(&maxFontSizeForCardsEdit, qOverload<int>(&QSpinBox::valueChanged), &settings.appearance(),
|
||||
&AppearanceSettings::setMaxFontSize);
|
||||
maxFontSizeForCardsEdit.setValue(settings.appearance().getMaxFontSize());
|
||||
maxFontSizeForCardsLabel.setBuddy(&maxFontSizeForCardsEdit);
|
||||
maxFontSizeForCardsEdit.setMinimum(9);
|
||||
maxFontSizeForCardsEdit.setMaximum(100);
|
||||
|
||||
auto *cardsGrid = new QGridLayout;
|
||||
cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2);
|
||||
cardsGrid->addWidget(&autoRotateSidewaysLayoutCardsCheckBox, 1, 0, 1, 2);
|
||||
cardsGrid->addWidget(&cardScalingCheckBox, 2, 0, 1, 2);
|
||||
cardsGrid->addWidget(&roundCardCornersCheckBox, 3, 0, 1, 2);
|
||||
cardsGrid->addWidget(&maxFontSizeForCardsLabel, 4, 0, 1, 1);
|
||||
cardsGrid->addWidget(&maxFontSizeForCardsEdit, 4, 1, 1, 1);
|
||||
|
||||
cardsGroupBox = new QGroupBox;
|
||||
cardsGroupBox->setLayout(cardsGrid);
|
||||
|
|
@ -657,7 +648,6 @@ void AppearanceSettingsPage::retranslateUi()
|
|||
autoRotateSidewaysLayoutCardsCheckBox.setText(tr("Auto-Rotate cards with sideways layout"));
|
||||
cardScalingCheckBox.setText(tr("Scale cards on mouse over"));
|
||||
roundCardCornersCheckBox.setText(tr("Use rounded card corners"));
|
||||
maxFontSizeForCardsLabel.setText(tr("Maximum font size for information displayed on cards:"));
|
||||
|
||||
cardLayoutGroupBox->setTitle(tr("Card layout"));
|
||||
verticalCardOverlapPercentLabel.setText(
|
||||
|
|
|
|||
|
|
@ -71,8 +71,6 @@ private:
|
|||
QCheckBox autoRotateSidewaysLayoutCardsCheckBox;
|
||||
QCheckBox cardScalingCheckBox;
|
||||
QCheckBox roundCardCornersCheckBox;
|
||||
QLabel maxFontSizeForCardsLabel;
|
||||
QSpinBox maxFontSizeForCardsEdit;
|
||||
|
||||
QLabel verticalCardOverlapPercentLabel;
|
||||
QSpinBox verticalCardOverlapPercentBox;
|
||||
|
|
|
|||
|
|
@ -33,16 +33,6 @@ void AppearanceSettings::setStyleUserList(bool _styleUserList)
|
|||
emit styleUserListChanged();
|
||||
}
|
||||
|
||||
int AppearanceSettings::getMaxFontSize() const
|
||||
{
|
||||
return getValue("maxFontSize", QString(), QString(), 12).toInt();
|
||||
}
|
||||
|
||||
void AppearanceSettings::setMaxFontSize(int _max)
|
||||
{
|
||||
setValue(_max, "maxFontSize");
|
||||
}
|
||||
|
||||
QString AppearanceSettings::getHomeTabBackgroundSource() const
|
||||
{
|
||||
return getValue("homeTabBackgroundSource", QString(), QString(), "themed").toString();
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ public:
|
|||
void setThemeName(const QString &_themeName);
|
||||
[[nodiscard]] bool getStyleUserList() const;
|
||||
void setStyleUserList(bool _styleUserList);
|
||||
[[nodiscard]] int getMaxFontSize() const;
|
||||
void setMaxFontSize(int _max);
|
||||
[[nodiscard]] QString getHomeTabBackgroundSource() const;
|
||||
void setHomeTabBackgroundSource(const QString &_backgroundSource);
|
||||
[[nodiscard]] int getHomeTabBackgroundShuffleFrequency() const;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
#include <QStringLiteral>
|
||||
#include <QTextStream>
|
||||
#include <QVariant>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
|
@ -395,6 +396,10 @@ static void migrateAppearanceSettings(const QString &settingsPath, QSettings &gl
|
|||
{
|
||||
const QMap<QString, QString> appearanceKeyMap = {
|
||||
{"theme/name", "appearance/themeName"},
|
||||
// Retired into the theme's CardText size, but still read here so users
|
||||
// who have not migrated past global.ini keep their card text size:
|
||||
// SettingsCache runs migrateSettingsFromGlobalIni() before
|
||||
// migrateAppearanceMaxFontSizeToTheme().
|
||||
{"game/maxfontsize", "appearance/maxFontSize"},
|
||||
{"home/background", "appearance/homeTabBackgroundSource"},
|
||||
{"home/background/shuffleTimer", "appearance/homeTabBackgroundShuffleFrequency"},
|
||||
|
|
@ -424,6 +429,181 @@ static void migrateAppearanceSettings(const QString &settingsPath, QSettings &gl
|
|||
}
|
||||
}
|
||||
|
||||
static QString themesPathForSettings(const QString &settingsPath)
|
||||
{
|
||||
QSettings pathsIni(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
const QString configured = pathsIni.value("paths/themes").toString().trimmed();
|
||||
if (!configured.isEmpty()) {
|
||||
return configured;
|
||||
}
|
||||
// Default layout: <data>/themes/ sibling of <data>/settings/.
|
||||
return QDir::cleanPath(QDir(settingsPath).absoluteFilePath("../themes"));
|
||||
}
|
||||
|
||||
static bool fontsTomlHasExplicitCardTextMax(const QString &filePath)
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString currentSection;
|
||||
QTextStream in(&f);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
if (line.isEmpty() || line.startsWith('#') || line.startsWith(';')) {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
currentSection = line.mid(1, line.length() - 2).trimmed();
|
||||
continue;
|
||||
}
|
||||
if (currentSection.compare("Fonts", Qt::CaseInsensitive) != 0) {
|
||||
continue;
|
||||
}
|
||||
int eq = line.indexOf('=');
|
||||
if (eq < 0) {
|
||||
continue;
|
||||
}
|
||||
QString key = line.left(eq).trimmed();
|
||||
QString value = line.mid(eq + 1).trimmed();
|
||||
for (int i = 1; i < value.size(); ++i) {
|
||||
if ((value[i] == '#' || value[i] == ';') && value[i - 1].isSpace()) {
|
||||
value = value.left(i).trimmed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (value.size() >= 2 &&
|
||||
((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\'')))) {
|
||||
value = value.mid(1, value.size() - 2).trimmed();
|
||||
}
|
||||
if (key.startsWith("AppFont::")) {
|
||||
key = key.mid(QStringLiteral("AppFont::").size());
|
||||
}
|
||||
if (key.compare("CardTextMaxSize", Qt::CaseInsensitive) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (key.compare("CardText", Qt::CaseInsensitive) != 0) {
|
||||
continue;
|
||||
}
|
||||
int comma = value.lastIndexOf(',');
|
||||
if (comma < 0) {
|
||||
continue;
|
||||
}
|
||||
QString family = value.left(comma).trimmed();
|
||||
if (family.size() >= 2 &&
|
||||
((family.startsWith('"') && family.endsWith('"')) || (family.startsWith('\'') && family.endsWith('\'')))) {
|
||||
family = family.mid(1, family.size() - 2).trimmed();
|
||||
}
|
||||
bool ok = false;
|
||||
int pointSize = value.mid(comma + 1).trimmed().toInt(&ok);
|
||||
if (!ok || family.isEmpty() || pointSize <= 0) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool seedCardTextMaxIntoThemeDir(const QString &themeDirPath, int maxSize)
|
||||
{
|
||||
if (themeDirPath.isEmpty() || maxSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
QDir dir(themeDirPath);
|
||||
if (!dir.exists()) {
|
||||
return false;
|
||||
}
|
||||
const QString filePath = dir.absoluteFilePath("fonts.toml");
|
||||
if (fontsTomlHasExplicitCardTextMax(filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString existing;
|
||||
if (QFile::exists(filePath)) {
|
||||
QFile in(filePath);
|
||||
if (!in.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
existing = QString::fromUtf8(in.readAll());
|
||||
}
|
||||
|
||||
// Size-only key: seeding the CardText family here would pin it to Serif
|
||||
// for the whole board (names, player labels, the completer), which is not
|
||||
// what the retired maxFontSize setting meant. Let the family keep its
|
||||
// existing resolution and only constrain the size.
|
||||
const QString entry = QString("%1 = %2\n").arg(QStringLiteral("CardTextMaxSize"), -20).arg(maxSize);
|
||||
|
||||
QString out;
|
||||
if (existing.isEmpty()) {
|
||||
out = "[Fonts]\n" + entry + "\n";
|
||||
} else if (existing.contains("[Fonts]", Qt::CaseInsensitive)) {
|
||||
out = existing;
|
||||
if (!out.endsWith('\n')) {
|
||||
out += '\n';
|
||||
}
|
||||
out += entry;
|
||||
} else {
|
||||
out = existing;
|
||||
if (!out.endsWith('\n')) {
|
||||
out += '\n';
|
||||
}
|
||||
out += "\n[Fonts]\n" + entry + "\n";
|
||||
}
|
||||
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
QTextStream stream(&f);
|
||||
stream << out;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool appearanceMaxFontSizeMigrationPending(const QString &settingsPath)
|
||||
{
|
||||
if (!QFile::exists(settingsPath + "appearance.ini")) {
|
||||
return false;
|
||||
}
|
||||
QSettings appearanceIni(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
return appearanceIni.contains("appearance/maxFontSize");
|
||||
}
|
||||
|
||||
bool SettingsMigration::migrateAppearanceMaxFontSizeToTheme(const QString &settingsPath)
|
||||
{
|
||||
if (!appearanceMaxFontSizeMigrationPending(settingsPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSettings appearanceIni(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
// Retired default was 12 in AppearanceSettings::getMaxFontSize; only
|
||||
// non-default values are seeded, default users rely on the theme fallback.
|
||||
const int legacy = appearanceIni.value("appearance/maxFontSize").toInt();
|
||||
if (legacy != 12 && legacy > 0) {
|
||||
const QString themesPath = themesPathForSettings(settingsPath);
|
||||
QDir themesDir(themesPath);
|
||||
const QStringList themes = themesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
|
||||
for (const QString &theme : themes) {
|
||||
seedCardTextMaxIntoThemeDir(themesDir.absoluteFilePath(theme), legacy);
|
||||
}
|
||||
|
||||
// Shipped themes (Fabric, Leather, ...) resolve to the system theme
|
||||
// directory unless the user has already saved something for them, and
|
||||
// System/Fusion only ever exist in the user directory. Seeding existing
|
||||
// folders alone would therefore drop the retired size for every user on
|
||||
// a stock theme, so create the active theme's user directory the same
|
||||
// way ThemeManager::writableThemeDir() does for palette and font saves.
|
||||
const QString activeTheme = appearanceIni.value("appearance/themeName").toString().trimmed();
|
||||
if (!activeTheme.isEmpty() && !themesDir.exists(activeTheme) && themesDir.mkpath(activeTheme)) {
|
||||
seedCardTextMaxIntoThemeDir(themesDir.absoluteFilePath(activeTheme), legacy);
|
||||
}
|
||||
}
|
||||
|
||||
appearanceIni.remove("appearance/maxFontSize");
|
||||
appearanceIni.sync();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void migrateNetworkSettings(const QString &settingsPath, QSettings &globalIni)
|
||||
{
|
||||
const QMap<QString, QString> networkKeyMap = {
|
||||
|
|
@ -821,6 +1001,10 @@ bool SettingsMigration::isMigrationPending(const QString &settingsPath)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (appearanceMaxFontSizeMigrationPending(settingsPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QSettings personalIni(settingsPath + "personal.ini", QSettings::IniFormat);
|
||||
if (personalIni.value("migration/legacy_complete", false).toBool()) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@ public:
|
|||
*/
|
||||
static bool migrateGameFiltersToCamelCase(const QString &settingsPath);
|
||||
|
||||
/**
|
||||
* Moves a non-default appearance/maxFontSize value into each user-writable theme
|
||||
* directory lacking an explicit CardText max (creating/merging fonts.toml, never
|
||||
* overwriting an explicit max), then deletes the legacy key. Default (12) or absent
|
||||
* values only delete the key and create no files, since the ThemeManager fallback
|
||||
* already covers them. Idempotent, safe to run on every startup.
|
||||
*/
|
||||
static bool migrateAppearanceMaxFontSizeToTheme(const QString &settingsPath);
|
||||
|
||||
/**
|
||||
* Returns true when at least one of the two migrations would still do work, i.e. when a
|
||||
* backup of the settings directory is worth taking before they run.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QTemporaryDir>
|
||||
#include <QTextStream>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -186,6 +187,154 @@ TEST(FontConfigTest, InequalityComparesRoles)
|
|||
EXPECT_NE(left, right);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, CardTextMinRoundTrip)
|
||||
{
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
|
||||
FontConfig cfg;
|
||||
cfg.fonts[AppFont::CardText] = makeFont("Noto Serif", 14);
|
||||
cfg.cardTextMinSize = 3;
|
||||
ASSERT_TRUE(cfg.hasFonts());
|
||||
|
||||
const QString filePath = writeTempFile(tmp, "fonts.toml", cfg.toToml());
|
||||
const FontConfig loaded = FontConfig::fromFile(filePath);
|
||||
|
||||
EXPECT_EQ(loaded, cfg);
|
||||
EXPECT_EQ(loaded.fonts[AppFont::CardText].pointSize(), 14);
|
||||
EXPECT_EQ(loaded.cardTextMinSize, 3);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, CardTextMinDefaultIsOmitted)
|
||||
{
|
||||
FontConfig cfg;
|
||||
cfg.fonts[AppFont::CardText] = makeFont("Noto Serif", 12);
|
||||
EXPECT_EQ(cfg.cardTextMinSize, 1);
|
||||
|
||||
const QString toml = cfg.toToml();
|
||||
EXPECT_TRUE(toml.contains(QStringLiteral("CardText")));
|
||||
EXPECT_FALSE(toml.contains(QStringLiteral("CardTextMinSize")));
|
||||
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
const QString filePath = writeTempFile(tmp, "fonts.toml", toml);
|
||||
EXPECT_EQ(FontConfig::fromFile(filePath).cardTextMinSize, 1);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, MalformedMinIsSkipped)
|
||||
{
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
|
||||
const QString filePath = writeTempFile(tmp, "fonts.toml",
|
||||
"[Fonts]\n"
|
||||
"CardText = \"Noto Serif, 14\"\n"
|
||||
"CardTextMinSize = big\n");
|
||||
|
||||
// Each malformed value parses independently, so rewrite the file per case.
|
||||
const QStringList badValues = {"big", "0", "-3", "", "Inter, 10"};
|
||||
for (const QString &bad : badValues) {
|
||||
const QString badPath = writeTempFile(
|
||||
tmp, "bad.toml", QString("[Fonts]\nCardText = \"Noto Serif, 14\"\nCardTextMinSize = %1\n").arg(bad));
|
||||
const FontConfig loaded = FontConfig::fromFile(badPath);
|
||||
EXPECT_EQ(loaded.cardTextMinSize, 1) << "bad value: " << bad.toStdString();
|
||||
ASSERT_TRUE(loaded.fonts.contains(AppFont::CardText));
|
||||
EXPECT_EQ(loaded.fonts[AppFont::CardText].pointSize(), 14);
|
||||
}
|
||||
|
||||
// The valid file still loads.
|
||||
EXPECT_EQ(FontConfig::fromFile(filePath).cardTextMinSize, 1);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, CardTextMaxRoundTrip)
|
||||
{
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
|
||||
FontConfig cfg;
|
||||
cfg.fonts[AppFont::CardText] = makeFont("Noto Serif", 14);
|
||||
cfg.cardTextMaxSize = 18;
|
||||
ASSERT_TRUE(cfg.hasFonts());
|
||||
|
||||
const QString filePath = writeTempFile(tmp, "fonts.toml", cfg.toToml());
|
||||
const FontConfig loaded = FontConfig::fromFile(filePath);
|
||||
|
||||
EXPECT_EQ(loaded, cfg);
|
||||
EXPECT_EQ(loaded.cardTextMaxSize, 18);
|
||||
EXPECT_EQ(loaded.fonts[AppFont::CardText].pointSize(), 14);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, CardTextMaxSizeWithoutFamily)
|
||||
{
|
||||
// The retired maxFontSize setting migrates to a size-only key, so the
|
||||
// family keeps its existing resolution instead of being pinned to Serif.
|
||||
FontConfig cfg;
|
||||
cfg.cardTextMaxSize = 18;
|
||||
ASSERT_TRUE(cfg.hasFonts());
|
||||
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
const QString filePath = writeTempFile(tmp, "fonts.toml", cfg.toToml());
|
||||
const FontConfig loaded = FontConfig::fromFile(filePath);
|
||||
|
||||
EXPECT_TRUE(loaded.fonts.isEmpty());
|
||||
EXPECT_EQ(loaded.cardTextMaxSize, 18);
|
||||
EXPECT_EQ(loaded, cfg);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, CardTextMaxDefaultIsOmitted)
|
||||
{
|
||||
FontConfig cfg;
|
||||
cfg.fonts[AppFont::CardText] = makeFont("Noto Serif", 12);
|
||||
EXPECT_EQ(cfg.cardTextMaxSize, 0);
|
||||
|
||||
const QString toml = cfg.toToml();
|
||||
EXPECT_FALSE(toml.contains(QStringLiteral("CardTextMaxSize")));
|
||||
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
const QString filePath = writeTempFile(tmp, "fonts.toml", toml);
|
||||
EXPECT_EQ(FontConfig::fromFile(filePath).cardTextMaxSize, 0);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, MalformedMaxIsSkipped)
|
||||
{
|
||||
QTemporaryDir tmp;
|
||||
ASSERT_TRUE(tmp.isValid());
|
||||
|
||||
const QStringList badValues = {"big", "0", "-3", "", "Inter, 18"};
|
||||
for (const QString &bad : badValues) {
|
||||
const QString badPath = writeTempFile(
|
||||
tmp, "bad.toml", QString("[Fonts]\nCardText = \"Noto Serif, 14\"\nCardTextMaxSize = %1\n").arg(bad));
|
||||
const FontConfig loaded = FontConfig::fromFile(badPath);
|
||||
EXPECT_EQ(loaded.cardTextMaxSize, 0) << "bad value: " << bad.toStdString();
|
||||
ASSERT_TRUE(loaded.fonts.contains(AppFont::CardText));
|
||||
EXPECT_EQ(loaded.fonts[AppFont::CardText].pointSize(), 14);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, InequalityComparesMinSize)
|
||||
{
|
||||
FontConfig left;
|
||||
left.fonts[AppFont::CardText] = makeFont("Noto Serif", 12);
|
||||
left.cardTextMinSize = 1;
|
||||
|
||||
FontConfig right = left;
|
||||
right.cardTextMinSize = 2;
|
||||
EXPECT_NE(left, right);
|
||||
}
|
||||
|
||||
TEST(FontConfigTest, InequalityComparesMaxSize)
|
||||
{
|
||||
FontConfig left;
|
||||
left.fonts[AppFont::CardText] = makeFont("Noto Serif", 12);
|
||||
left.cardTextMaxSize = 12;
|
||||
|
||||
FontConfig right = left;
|
||||
right.cardTextMaxSize = 18;
|
||||
EXPECT_NE(left, right);
|
||||
}
|
||||
|
||||
// Every test brings its own main(): GTest is not guaranteed to be found on the build machines, and
|
||||
// the fallback then links the bare gtest library without gtest_main.
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
|||
|
|
@ -461,19 +461,6 @@ TEST_F(SettingsDefaultsTest, Appearance_StyleUserList_Default)
|
|||
ASSERT_EQ(s.getStyleUserList(), true);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Appearance_MaxFontSize_Default)
|
||||
{
|
||||
AppearanceSettings s(settingsPath, nullptr);
|
||||
ASSERT_EQ(s.getMaxFontSize(), 12);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Appearance_MaxFontSize_SetAndGet)
|
||||
{
|
||||
AppearanceSettings s(settingsPath, nullptr);
|
||||
s.setMaxFontSize(14);
|
||||
ASSERT_EQ(s.getMaxFontSize(), 14);
|
||||
}
|
||||
|
||||
TEST_F(SettingsDefaultsTest, Appearance_HomeTabBackgroundSource_Default)
|
||||
{
|
||||
AppearanceSettings s(settingsPath, nullptr);
|
||||
|
|
|
|||
|
|
@ -984,6 +984,198 @@ TEST_F(SettingsMigrationTest, IsNotMigrationPendingWithoutLegacyData)
|
|||
ASSERT_FALSE(SettingsMigration::isMigrationPending(settingsPath));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_NonDefaultSeedsThemeAndDeletesKey)
|
||||
{
|
||||
const QString themesPath = tempDir.path() + "/themes";
|
||||
ASSERT_TRUE(QDir().mkpath(themesPath + "/MyTheme"));
|
||||
{
|
||||
QSettings paths(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
paths.setValue("paths/themes", themesPath);
|
||||
paths.sync();
|
||||
}
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/maxFontSize", 18);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
|
||||
ASSERT_FALSE(readFromIni("appearance.ini", "appearance/maxFontSize").isValid());
|
||||
|
||||
const QString fontsPath = themesPath + "/MyTheme/fonts.toml";
|
||||
ASSERT_TRUE(QFile::exists(fontsPath));
|
||||
QFile f(fontsPath);
|
||||
ASSERT_TRUE(f.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QString content = QString::fromUtf8(f.readAll());
|
||||
EXPECT_TRUE(content.contains("CardTextMaxSize"));
|
||||
EXPECT_TRUE(content.contains("18"));
|
||||
// Size-only: the migration must not pin the family, or every CardText
|
||||
// surface switches to Serif for users who never chose it.
|
||||
EXPECT_FALSE(content.contains("Serif"));
|
||||
|
||||
ASSERT_FALSE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_SeedsActiveStockTheme)
|
||||
{
|
||||
// A stock theme resolves to the system directory, so there is no user theme
|
||||
// folder to seed. The migration has to create the active theme's folder,
|
||||
// otherwise the retired size is deleted without landing anywhere.
|
||||
const QString themesPath = tempDir.path() + "/themes";
|
||||
{
|
||||
QSettings paths(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
paths.setValue("paths/themes", themesPath);
|
||||
paths.sync();
|
||||
}
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/themeName", "Fabric");
|
||||
appearance.setValue("appearance/maxFontSize", 18);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
|
||||
ASSERT_FALSE(readFromIni("appearance.ini", "appearance/maxFontSize").isValid());
|
||||
|
||||
const QString fontsPath = themesPath + "/Fabric/fonts.toml";
|
||||
ASSERT_TRUE(QFile::exists(fontsPath));
|
||||
QFile f(fontsPath);
|
||||
ASSERT_TRUE(f.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QString content = QString::fromUtf8(f.readAll());
|
||||
EXPECT_TRUE(content.contains("CardTextMaxSize"));
|
||||
EXPECT_TRUE(content.contains("18"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_KeepsExistingActiveTheme)
|
||||
{
|
||||
const QString themesPath = tempDir.path() + "/themes";
|
||||
ASSERT_TRUE(QDir().mkpath(themesPath + "/Fabric"));
|
||||
{
|
||||
QFile f(themesPath + "/Fabric/fonts.toml");
|
||||
ASSERT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
QTextStream out(&f);
|
||||
out << "[Fonts]\nCardText = \"Inter, 10\"\n";
|
||||
}
|
||||
{
|
||||
QSettings paths(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
paths.setValue("paths/themes", themesPath);
|
||||
paths.sync();
|
||||
}
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/themeName", "Fabric");
|
||||
appearance.setValue("appearance/maxFontSize", 18);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
|
||||
QFile f(themesPath + "/Fabric/fonts.toml");
|
||||
ASSERT_TRUE(f.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QString content = QString::fromUtf8(f.readAll());
|
||||
EXPECT_TRUE(content.contains("Inter, 10"));
|
||||
EXPECT_FALSE(content.contains("18"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_DefaultDeletesKeyWithoutFiles)
|
||||
{
|
||||
const QString themesPath = tempDir.path() + "/themes";
|
||||
ASSERT_TRUE(QDir().mkpath(themesPath + "/MyTheme"));
|
||||
{
|
||||
QSettings paths(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
paths.setValue("paths/themes", themesPath);
|
||||
paths.sync();
|
||||
}
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/maxFontSize", 12);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
ASSERT_FALSE(readFromIni("appearance.ini", "appearance/maxFontSize").isValid());
|
||||
ASSERT_FALSE(QFile::exists(themesPath + "/MyTheme/fonts.toml"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_DoesNotOverwriteExplicitMax)
|
||||
{
|
||||
const QString themesPath = tempDir.path() + "/themes";
|
||||
ASSERT_TRUE(QDir().mkpath(themesPath + "/MyTheme"));
|
||||
{
|
||||
QFile f(themesPath + "/MyTheme/fonts.toml");
|
||||
ASSERT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
QTextStream out(&f);
|
||||
out << "[Fonts]\nCardText = \"Inter, 10\"\n";
|
||||
}
|
||||
{
|
||||
QSettings paths(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
paths.setValue("paths/themes", themesPath);
|
||||
paths.sync();
|
||||
}
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/maxFontSize", 18);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
|
||||
QFile f(themesPath + "/MyTheme/fonts.toml");
|
||||
ASSERT_TRUE(f.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QString content = QString::fromUtf8(f.readAll());
|
||||
EXPECT_TRUE(content.contains("10"));
|
||||
EXPECT_FALSE(content.contains("18"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_MergesWithOtherRoles)
|
||||
{
|
||||
const QString themesPath = tempDir.path() + "/themes";
|
||||
ASSERT_TRUE(QDir().mkpath(themesPath + "/MyTheme"));
|
||||
{
|
||||
QFile f(themesPath + "/MyTheme/fonts.toml");
|
||||
ASSERT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
QTextStream out(&f);
|
||||
out << "[Fonts]\nInterfaceDefault = \"Inter, 10\"\n";
|
||||
}
|
||||
{
|
||||
QSettings paths(settingsPath + "paths.ini", QSettings::IniFormat);
|
||||
paths.setValue("paths/themes", themesPath);
|
||||
paths.sync();
|
||||
}
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/maxFontSize", 20);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
|
||||
QFile f(themesPath + "/MyTheme/fonts.toml");
|
||||
ASSERT_TRUE(f.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QString content = QString::fromUtf8(f.readAll());
|
||||
EXPECT_TRUE(content.contains("InterfaceDefault"));
|
||||
EXPECT_TRUE(content.contains("CardTextMaxSize"));
|
||||
EXPECT_TRUE(content.contains("20"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, AppearanceMaxFontSize_AbsentKeyDoesNothing)
|
||||
{
|
||||
ASSERT_FALSE(SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath));
|
||||
}
|
||||
|
||||
TEST_F(SettingsMigrationTest, IsMigrationPendingWhenMaxFontSizePresent)
|
||||
{
|
||||
{
|
||||
QSettings appearance(settingsPath + "appearance.ini", QSettings::IniFormat);
|
||||
appearance.setValue("appearance/maxFontSize", 18);
|
||||
appearance.sync();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(SettingsMigration::isMigrationPending(settingsPath));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue