mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -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
5a5671e047
commit
f47813dfe4
19 changed files with 563 additions and 49 deletions
|
|
@ -137,6 +137,9 @@ SettingsCache::SettingsCache()
|
||||||
// before that rename (idempotent, safe to run on every startup, portable builds included).
|
// before that rename (idempotent, safe to run on every startup, portable builds included).
|
||||||
SettingsMigration::migrateGameFiltersToCamelCase(settingsPath);
|
SettingsMigration::migrateGameFiltersToCamelCase(settingsPath);
|
||||||
|
|
||||||
|
// Retire appearance/maxFontSize into per-theme CardText max (idempotent, safe every startup).
|
||||||
|
SettingsMigration::migrateAppearanceMaxFontSizeToTheme(settingsPath);
|
||||||
|
|
||||||
shortcutsSettings = new ShortcutsSettings(settingsPath, this);
|
shortcutsSettings = new ShortcutsSettings(settingsPath, this);
|
||||||
cardDatabaseSettings = new CardDatabaseSettings(settingsPath, this);
|
cardDatabaseSettings = new CardDatabaseSettings(settingsPath, this);
|
||||||
serversSettings = new ServersSettings(settingsPath, this);
|
serversSettings = new ServersSettings(settingsPath, this);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
#include <libcockatrice/settings/appearance_settings.h>
|
|
||||||
#include <libcockatrice/settings/cards_display_settings.h>
|
#include <libcockatrice/settings/cards_display_settings.h>
|
||||||
#include <libcockatrice/settings/debug_settings.h>
|
#include <libcockatrice/settings/debug_settings.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
|
|
@ -136,8 +135,8 @@ QSizeF AbstractCardItem::getTranslatedSize(QPainter *painter) const
|
||||||
|
|
||||||
void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle)
|
void AbstractCardItem::transformPainter(QPainter *painter, const QSizeF &translatedSize, int angle)
|
||||||
{
|
{
|
||||||
const int MAX_FONT_SIZE = SettingsCache::instance().appearance().getMaxFontSize();
|
const int minSize = std::max(1, themeManager->cardTextMinSize());
|
||||||
const int fontSize = std::max(9, MAX_FONT_SIZE);
|
const int fontSize = std::max(minSize, std::max(1, themeManager->cardTextMaxSize()));
|
||||||
|
|
||||||
QRectF totalBoundingRect = painter->combinedTransform().mapRect(boundingRect());
|
QRectF totalBoundingRect = painter->combinedTransform().mapRect(boundingRect());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
static constexpr int MIN_FONT_SIZE = 6;
|
static constexpr int MIN_FONT_SIZE = 6;
|
||||||
static constexpr int MAX_FONT_SIZE = 72;
|
static constexpr int MAX_FONT_SIZE = 72;
|
||||||
|
static constexpr int CARD_TEXT_MIN_SIZE = 1;
|
||||||
static constexpr int FALLBACK_FONT_SIZE = 10;
|
static constexpr int FALLBACK_FONT_SIZE = 10;
|
||||||
|
|
||||||
static const QMap<AppFont::Role, const char *> APP_FONT_DESCRIPTIONS = {
|
static const QMap<AppFont::Role, const char *> APP_FONT_DESCRIPTIONS = {
|
||||||
|
|
@ -46,6 +47,7 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
|
||||||
grid->setColumnStretch(0, 0);
|
grid->setColumnStretch(0, 0);
|
||||||
grid->setColumnStretch(1, 1);
|
grid->setColumnStretch(1, 1);
|
||||||
grid->setColumnStretch(2, 0);
|
grid->setColumnStretch(2, 0);
|
||||||
|
grid->setColumnStretch(3, 0);
|
||||||
|
|
||||||
QMetaEnum fontEnum = QMetaEnum::fromType<AppFont::Role>();
|
QMetaEnum fontEnum = QMetaEnum::fromType<AppFont::Role>();
|
||||||
for (int i = 0; i < fontEnum.keyCount(); ++i) {
|
for (int i = 0; i < fontEnum.keyCount(); ++i) {
|
||||||
|
|
@ -58,6 +60,30 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
|
||||||
grid->addWidget(familyCombo, i, 1);
|
grid->addWidget(familyCombo, i, 1);
|
||||||
connect(familyCombo, &QFontComboBox::currentFontChanged, this, [this] { emit fontsChanged(); });
|
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);
|
auto *sizeSpin = new QSpinBox(this);
|
||||||
sizeSpin->setRange(MIN_FONT_SIZE, MAX_FONT_SIZE);
|
sizeSpin->setRange(MIN_FONT_SIZE, MAX_FONT_SIZE);
|
||||||
grid->addWidget(sizeSpin, i, 2);
|
grid->addWidget(sizeSpin, i, 2);
|
||||||
|
|
@ -65,7 +91,7 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
|
||||||
|
|
||||||
label->setBuddy(familyCombo);
|
label->setBuddy(familyCombo);
|
||||||
|
|
||||||
fontRows[role] = {label, familyCombo, sizeSpin};
|
fontRows[role] = {label, familyCombo, sizeSpin, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
retranslateUi();
|
retranslateUi();
|
||||||
|
|
@ -96,6 +122,11 @@ void FontGridWidget::loadFonts(const FontConfig &cfg)
|
||||||
size = FALLBACK_FONT_SIZE;
|
size = FALLBACK_FONT_SIZE;
|
||||||
}
|
}
|
||||||
row.sizeSpin->setValue(qBound(MIN_FONT_SIZE, size, MAX_FONT_SIZE));
|
row.sizeSpin->setValue(qBound(MIN_FONT_SIZE, size, MAX_FONT_SIZE));
|
||||||
|
|
||||||
|
if (role == AppFont::CardText && row.minSpin != nullptr) {
|
||||||
|
int minSize = cfg.cardTextMinSize > 0 ? cfg.cardTextMinSize : themeManager->cardTextMinSize();
|
||||||
|
row.minSpin->setValue(qBound(CARD_TEXT_MIN_SIZE, minSize, MAX_FONT_SIZE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
blockSignals(false);
|
blockSignals(false);
|
||||||
}
|
}
|
||||||
|
|
@ -113,6 +144,10 @@ FontConfig FontGridWidget::currentFontConfig() const
|
||||||
QFont font(row.familyCombo->currentFont().family());
|
QFont font(row.familyCombo->currentFont().family());
|
||||||
font.setPointSize(row.sizeSpin->value());
|
font.setPointSize(row.sizeSpin->value());
|
||||||
cfg.fonts[role] = font;
|
cfg.fonts[role] = font;
|
||||||
|
|
||||||
|
if (role == AppFont::CardText && row.minSpin != nullptr) {
|
||||||
|
cfg.cardTextMinSize = row.minSpin->value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
@ -128,6 +163,15 @@ void FontGridWidget::retranslateUi()
|
||||||
row.label->setToolTip(tr(APP_FONT_DESCRIPTIONS.value(role, "")));
|
row.label->setToolTip(tr(APP_FONT_DESCRIPTIONS.value(role, "")));
|
||||||
row.familyCombo->setToolTip(tr(APP_FONT_DESCRIPTIONS.value(role, "")));
|
row.familyCombo->setToolTip(tr(APP_FONT_DESCRIPTIONS.value(role, "")));
|
||||||
row.familyCombo->setAccessibleName(roleDisplayName(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->setToolTip(tr("Point size for %1").arg(roleDisplayName(role)));
|
||||||
row.sizeSpin->setAccessibleName(tr("Point size for %1").arg(roleDisplayName(role)));
|
row.sizeSpin->setAccessibleName(tr("Point size for %1").arg(roleDisplayName(role)));
|
||||||
row.sizeSpin->setSuffix(tr(" pt"));
|
row.sizeSpin->setSuffix(tr(" pt"));
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ private:
|
||||||
QLabel *label = nullptr;
|
QLabel *label = nullptr;
|
||||||
QFontComboBox *familyCombo = nullptr;
|
QFontComboBox *familyCombo = nullptr;
|
||||||
QSpinBox *sizeSpin = nullptr;
|
QSpinBox *sizeSpin = nullptr;
|
||||||
|
QSpinBox *minSpin = nullptr; // CardText only: shrink-to-fit floor
|
||||||
};
|
};
|
||||||
|
|
||||||
QMap<AppFont::Role, FontRow> fontRows;
|
QMap<AppFont::Role, FontRow> fontRows;
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ QPalette PaletteConfig::apply(QPalette base) const
|
||||||
|
|
||||||
bool FontConfig::hasFonts() const
|
bool FontConfig::hasFonts() const
|
||||||
{
|
{
|
||||||
return !fonts.isEmpty();
|
return !fonts.isEmpty() || cardTextMinSize != 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FontConfig::toToml() const
|
QString FontConfig::toToml() const
|
||||||
|
|
@ -334,6 +334,10 @@ QString FontConfig::toToml() const
|
||||||
entries += QString("%1 = \"%2, %3\"\n").arg(QString(roleName), -20).arg(family).arg(pointSize);
|
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 (entries.isEmpty()) {
|
if (entries.isEmpty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -401,6 +405,22 @@ FontConfig FontConfig::fromFile(const QString &filePath)
|
||||||
value = value.mid(1, value.size() - 2).trimmed();
|
value = value.mid(1, value.size() - 2).trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CardText floor: bare integer, e.g. CardTextMinSize = 2. Tolerate
|
||||||
|
// quoting like the family entries; skip invalid lines.
|
||||||
|
QString minKey = key;
|
||||||
|
if (minKey.startsWith("AppFont::")) {
|
||||||
|
minKey = minKey.mid(QStringLiteral("AppFont::").size());
|
||||||
|
}
|
||||||
|
if (minKey.compare("CardTextMinSize", Qt::CaseInsensitive) == 0) {
|
||||||
|
bool ok = false;
|
||||||
|
int minSize = value.trimmed().toInt(&ok);
|
||||||
|
if (!ok || minSize <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cfg.cardTextMinSize = minSize;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int comma = value.lastIndexOf(',');
|
int comma = value.lastIndexOf(',');
|
||||||
|
|
||||||
if (comma < 0) {
|
if (comma < 0) {
|
||||||
|
|
|
||||||
|
|
@ -84,15 +84,20 @@ struct PaletteConfig
|
||||||
* @code
|
* @code
|
||||||
* [Fonts]
|
* [Fonts]
|
||||||
* InterfaceDefault = "Inter, 10"
|
* InterfaceDefault = "Inter, 10"
|
||||||
|
* CardText = "Noto Serif, 12"
|
||||||
|
* CardTextMinSize = 1
|
||||||
* @endcode
|
* @endcode
|
||||||
|
* CardText stores family + MAX size; cardTextMinSize stores the overlay
|
||||||
|
* shrink-to-fit floor (omitted from TOML when 1).
|
||||||
*/
|
*/
|
||||||
struct FontConfig
|
struct FontConfig
|
||||||
{
|
{
|
||||||
QMap<AppFont::Role, QFont> fonts;
|
QMap<AppFont::Role, QFont> fonts;
|
||||||
|
int cardTextMinSize = 1;
|
||||||
|
|
||||||
bool operator==(const FontConfig &rhs) const
|
bool operator==(const FontConfig &rhs) const
|
||||||
{
|
{
|
||||||
return fonts == rhs.fonts;
|
return fonts == rhs.fonts && cardTextMinSize == rhs.cardTextMinSize;
|
||||||
}
|
}
|
||||||
bool operator!=(const FontConfig &rhs) const
|
bool operator!=(const FontConfig &rhs) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -689,6 +689,7 @@ QFont ThemeManager::cardTextFont() const
|
||||||
}
|
}
|
||||||
QFont serifFont(QStringLiteral("Serif"));
|
QFont serifFont(QStringLiteral("Serif"));
|
||||||
serifFont.setStyleHint(QFont::Serif);
|
serifFont.setStyleHint(QFont::Serif);
|
||||||
|
serifFont.setPointSize(12);
|
||||||
return serifFont;
|
return serifFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -710,6 +711,26 @@ QFont ThemeManager::defaultAppFont(AppFont::Role /*role*/) const
|
||||||
return defaultFont;
|
return defaultFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ThemeManager::cardTextMaxSize() const
|
||||||
|
{
|
||||||
|
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()
|
void ThemeManager::themeChangedSlot()
|
||||||
{
|
{
|
||||||
QString themeName = SettingsCache::instance().getThemeName();
|
QString themeName = SettingsCache::instance().getThemeName();
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,10 @@ public:
|
||||||
* "what this role resolves to" must use this, because appFont()
|
* "what this role resolves to" must use this, because appFont()
|
||||||
* reflects a live preview while the editor is open. */
|
* reflects a live preview while the editor is open. */
|
||||||
QFont defaultAppFont(AppFont::Role role) const;
|
QFont defaultAppFont(AppFont::Role role) const;
|
||||||
|
/** @brief CardText max size: stored 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 &getBgBrush(Role zone);
|
||||||
QBrush getExtraBgBrush(Role zone, int zoneId = 0);
|
QBrush getExtraBgBrush(Role zone, int zoneId = 0);
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ void CardInfoPictureWithTextOverlayWidget::setOutlineColor(const QColor &color)
|
||||||
void CardInfoPictureWithTextOverlayWidget::setFontSize(const int size)
|
void CardInfoPictureWithTextOverlayWidget::setFontSize(const int size)
|
||||||
{
|
{
|
||||||
fontSize = size > 0 ? size : 1;
|
fontSize = size > 0 ? size : 1;
|
||||||
|
hasExplicitFontSize = true;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,10 +180,12 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the optimal font size. The family comes from the CardText
|
// Calculate the optimal font size. The family comes from the CardText
|
||||||
// theme role; the fontSize member stays the starting size and the
|
// theme role; the start size is the explicit size when set (e.g. deck
|
||||||
// shrink-to-fit logic below is unchanged.
|
// preview 24), else the theme max, shrinking down to the theme min floor.
|
||||||
QFont font = themeManager->appFont(AppFont::CardText);
|
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);
|
const QFontMetrics baseMetrics(font);
|
||||||
int textWidth = pixmapRect.width();
|
int textWidth = pixmapRect.width();
|
||||||
|
|
||||||
|
|
@ -200,7 +203,7 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
|
||||||
}
|
}
|
||||||
|
|
||||||
--optimalFontSize;
|
--optimalFontSize;
|
||||||
} while (optimalFontSize > 1);
|
} while (optimalFontSize > themeMin);
|
||||||
|
|
||||||
// Apply the calculated font size
|
// Apply the calculated font size
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
|
|
@ -227,7 +230,7 @@ void CardInfoPictureWithTextOverlayWidget::paintEvent(QPaintEvent *event)
|
||||||
int totalTextHeight = wrappedText.count('\n') * fontMetrics.height() + fontMetrics.height();
|
int totalTextHeight = wrappedText.count('\n') * fontMetrics.height() + fontMetrics.height();
|
||||||
|
|
||||||
// Adjust font size if the total text height exceeds the pixmap 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;
|
--optimalFontSize;
|
||||||
font.setPointSize(optimalFontSize);
|
font.setPointSize(optimalFontSize);
|
||||||
painter.setFont(font);
|
painter.setFont(font);
|
||||||
|
|
@ -297,7 +300,9 @@ QSize CardInfoPictureWithTextOverlayWidget::minimumSizeHint() const
|
||||||
|
|
||||||
// Get the font metrics for the overlay text
|
// Get the font metrics for the overlay text
|
||||||
QFont font = themeManager->appFont(AppFont::CardText);
|
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);
|
const QFontMetrics fontMetrics(font);
|
||||||
|
|
||||||
// Calculate the height required for the text
|
// Calculate the height required for the text
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ private:
|
||||||
QColor textColor;
|
QColor textColor;
|
||||||
QColor outlineColor;
|
QColor outlineColor;
|
||||||
int fontSize;
|
int fontSize;
|
||||||
|
bool hasExplicitFontSize = false;
|
||||||
Qt::Alignment textAlignment;
|
Qt::Alignment textAlignment;
|
||||||
bool highlighted;
|
bool highlighted;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -284,20 +284,11 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
||||||
connect(&roundCardCornersCheckBox, &QAbstractButton::toggled, &settings.cardsDisplay(),
|
connect(&roundCardCornersCheckBox, &QAbstractButton::toggled, &settings.cardsDisplay(),
|
||||||
&CardsDisplaySettings::setRoundCardCorners);
|
&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;
|
auto *cardsGrid = new QGridLayout;
|
||||||
cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2);
|
cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2);
|
||||||
cardsGrid->addWidget(&autoRotateSidewaysLayoutCardsCheckBox, 1, 0, 1, 2);
|
cardsGrid->addWidget(&autoRotateSidewaysLayoutCardsCheckBox, 1, 0, 1, 2);
|
||||||
cardsGrid->addWidget(&cardScalingCheckBox, 2, 0, 1, 2);
|
cardsGrid->addWidget(&cardScalingCheckBox, 2, 0, 1, 2);
|
||||||
cardsGrid->addWidget(&roundCardCornersCheckBox, 3, 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 = new QGroupBox;
|
||||||
cardsGroupBox->setLayout(cardsGrid);
|
cardsGroupBox->setLayout(cardsGrid);
|
||||||
|
|
@ -658,7 +649,6 @@ void AppearanceSettingsPage::retranslateUi()
|
||||||
autoRotateSidewaysLayoutCardsCheckBox.setText(tr("Auto-Rotate cards with sideways layout"));
|
autoRotateSidewaysLayoutCardsCheckBox.setText(tr("Auto-Rotate cards with sideways layout"));
|
||||||
cardScalingCheckBox.setText(tr("Scale cards on mouse over"));
|
cardScalingCheckBox.setText(tr("Scale cards on mouse over"));
|
||||||
roundCardCornersCheckBox.setText(tr("Use rounded card corners"));
|
roundCardCornersCheckBox.setText(tr("Use rounded card corners"));
|
||||||
maxFontSizeForCardsLabel.setText(tr("Maximum font size for information displayed on cards:"));
|
|
||||||
|
|
||||||
cardLayoutGroupBox->setTitle(tr("Card layout"));
|
cardLayoutGroupBox->setTitle(tr("Card layout"));
|
||||||
verticalCardOverlapPercentLabel.setText(
|
verticalCardOverlapPercentLabel.setText(
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,6 @@ private:
|
||||||
QCheckBox autoRotateSidewaysLayoutCardsCheckBox;
|
QCheckBox autoRotateSidewaysLayoutCardsCheckBox;
|
||||||
QCheckBox cardScalingCheckBox;
|
QCheckBox cardScalingCheckBox;
|
||||||
QCheckBox roundCardCornersCheckBox;
|
QCheckBox roundCardCornersCheckBox;
|
||||||
QLabel maxFontSizeForCardsLabel;
|
|
||||||
QSpinBox maxFontSizeForCardsEdit;
|
|
||||||
|
|
||||||
QLabel verticalCardOverlapPercentLabel;
|
QLabel verticalCardOverlapPercentLabel;
|
||||||
QSpinBox verticalCardOverlapPercentBox;
|
QSpinBox verticalCardOverlapPercentBox;
|
||||||
|
|
|
||||||
|
|
@ -33,16 +33,6 @@ void AppearanceSettings::setStyleUserList(bool _styleUserList)
|
||||||
emit styleUserListChanged();
|
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
|
QString AppearanceSettings::getHomeTabBackgroundSource() const
|
||||||
{
|
{
|
||||||
return getValue("homeTabBackgroundSource", QString(), QString(), "themed").toString();
|
return getValue("homeTabBackgroundSource", QString(), QString(), "themed").toString();
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,6 @@ public:
|
||||||
void setThemeName(const QString &_themeName);
|
void setThemeName(const QString &_themeName);
|
||||||
[[nodiscard]] bool getStyleUserList() const;
|
[[nodiscard]] bool getStyleUserList() const;
|
||||||
void setStyleUserList(bool _styleUserList);
|
void setStyleUserList(bool _styleUserList);
|
||||||
[[nodiscard]] int getMaxFontSize() const;
|
|
||||||
void setMaxFontSize(int _max);
|
|
||||||
[[nodiscard]] QString getHomeTabBackgroundSource() const;
|
[[nodiscard]] QString getHomeTabBackgroundSource() const;
|
||||||
void setHomeTabBackgroundSource(const QString &_backgroundSource);
|
void setHomeTabBackgroundSource(const QString &_backgroundSource);
|
||||||
[[nodiscard]] int getHomeTabBackgroundShuffleFrequency() const;
|
[[nodiscard]] int getHomeTabBackgroundShuffleFrequency() const;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QStringLiteral>
|
#include <QStringLiteral>
|
||||||
|
#include <QTextStream>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
@ -395,6 +396,10 @@ static void migrateAppearanceSettings(const QString &settingsPath, QSettings &gl
|
||||||
{
|
{
|
||||||
const QMap<QString, QString> appearanceKeyMap = {
|
const QMap<QString, QString> appearanceKeyMap = {
|
||||||
{"theme/name", "appearance/themeName"},
|
{"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"},
|
{"game/maxfontsize", "appearance/maxFontSize"},
|
||||||
{"home/background", "appearance/homeTabBackgroundSource"},
|
{"home/background", "appearance/homeTabBackgroundSource"},
|
||||||
{"home/background/shuffleTimer", "appearance/homeTabBackgroundShuffleFrequency"},
|
{"home/background/shuffleTimer", "appearance/homeTabBackgroundShuffleFrequency"},
|
||||||
|
|
@ -424,6 +429,175 @@ 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("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());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString entry =
|
||||||
|
QString("%1 = \"%2, %3\"\n").arg(QStringLiteral("CardText"), -20).arg(QStringLiteral("Serif")).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)
|
static void migrateNetworkSettings(const QString &settingsPath, QSettings &globalIni)
|
||||||
{
|
{
|
||||||
const QMap<QString, QString> networkKeyMap = {
|
const QMap<QString, QString> networkKeyMap = {
|
||||||
|
|
@ -821,6 +995,10 @@ bool SettingsMigration::isMigrationPending(const QString &settingsPath)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appearanceMaxFontSizeMigrationPending(settingsPath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QSettings personalIni(settingsPath + "personal.ini", QSettings::IniFormat);
|
QSettings personalIni(settingsPath + "personal.ini", QSettings::IniFormat);
|
||||||
if (personalIni.value("migration/legacy_complete", false).toBool()) {
|
if (personalIni.value("migration/legacy_complete", false).toBool()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,15 @@ public:
|
||||||
*/
|
*/
|
||||||
static bool migrateGameFiltersToCamelCase(const QString &settingsPath);
|
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
|
* 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.
|
* backup of the settings directory is worth taking before they run.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
@ -186,6 +187,76 @@ TEST(FontConfigTest, InequalityComparesRoles)
|
||||||
EXPECT_NE(left, right);
|
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, InequalityComparesMinSize)
|
||||||
|
{
|
||||||
|
FontConfig left;
|
||||||
|
left.fonts[AppFont::CardText] = makeFont("Noto Serif", 12);
|
||||||
|
left.cardTextMinSize = 1;
|
||||||
|
|
||||||
|
FontConfig right = left;
|
||||||
|
right.cardTextMinSize = 2;
|
||||||
|
EXPECT_NE(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
// Every test brings its own main(): GTest is not guaranteed to be found on the build machines, and
|
// 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.
|
// the fallback then links the bare gtest library without gtest_main.
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
|
|
||||||
|
|
@ -408,19 +408,6 @@ TEST_F(SettingsDefaultsTest, Appearance_StyleUserList_Default)
|
||||||
ASSERT_EQ(s.getStyleUserList(), true);
|
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)
|
TEST_F(SettingsDefaultsTest, Appearance_HomeTabBackgroundSource_Default)
|
||||||
{
|
{
|
||||||
AppearanceSettings s(settingsPath, nullptr);
|
AppearanceSettings s(settingsPath, nullptr);
|
||||||
|
|
|
||||||
|
|
@ -984,6 +984,196 @@ TEST_F(SettingsMigrationTest, IsNotMigrationPendingWithoutLegacyData)
|
||||||
ASSERT_FALSE(SettingsMigration::isMigrationPending(settingsPath));
|
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("CardText"));
|
||||||
|
EXPECT_TRUE(content.contains("18"));
|
||||||
|
EXPECT_TRUE(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("CardText"));
|
||||||
|
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("CardText"));
|
||||||
|
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
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue