mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-09 11:33:25 -08:00
[Fonts] Retire max font size setting into theme CardText min/max range (#7544)
* [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. * [Fonts] Resolve CardText sizes against the shipped theme Seeding themes/<name>/fonts.toml made getAvailableThemes() resolve the user dir, which shadowed the shipped theme.cfg and stylesheet: styleName came back empty and QSS was cleared. Fall back to the shipped theme dir for theme.cfg and QSS when the user dir exists, and resolve CardText sizes through FontConfig's 6..72 range so a migrated 80..100 renders at the size the editor shows. Insert CardTextMaxSize inside [Fonts] when another section follows. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
16455c9fd5
commit
3d24597134
21 changed files with 1058 additions and 74 deletions
|
|
@ -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,204 @@ 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").arg(QStringLiteral("CardTextMaxSize"), -20).arg(maxSize);
|
||||
|
||||
QString out;
|
||||
if (existing.isEmpty()) {
|
||||
out = "[Fonts]\n" + entry + "\n";
|
||||
} else {
|
||||
out = existing;
|
||||
if (!out.endsWith('\n')) {
|
||||
out += '\n';
|
||||
}
|
||||
|
||||
// Insert the key inside an existing [Fonts] section. Appending at EOF
|
||||
// would land it in whatever section happens to follow, where the font
|
||||
// parser never reads it. Tracking the last non-blank line of the
|
||||
// section keeps the file's own comments, ordering and blank lines.
|
||||
QStringList lines = out.split('\n');
|
||||
int insertAt = -1;
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
const QString trimmed = lines[i].trimmed();
|
||||
if (insertAt < 0) {
|
||||
if (trimmed.startsWith('[') && trimmed.endsWith(']') &&
|
||||
trimmed.mid(1, trimmed.length() - 2).trimmed().compare("Fonts", Qt::CaseInsensitive) == 0) {
|
||||
insertAt = i + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
||||
break;
|
||||
}
|
||||
if (!trimmed.isEmpty()) {
|
||||
insertAt = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (insertAt < 0) {
|
||||
out += "\n[Fonts]\n" + entry + "\n";
|
||||
} else {
|
||||
lines.insert(insertAt, entry);
|
||||
out = lines.join('\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 +1024,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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue