[Theme] Carry Default theme directory customizations over to System (#7385)

* [Theme] Carry Default theme directory customizations over to System

The Default -> System rename rewrote the stored theme name but left the
user profile's themes/Default directory behind, so anyone who had
customized zone graphics, stylesheets or palettes there silently lost them.

Migrate the directory alongside the name. Rename it when System does not
exist yet, otherwise merge file by file without overwriting what System
already has, then drop the legacy directory. Users who already ran the
rename get their customizations back on the next start, and a hand-made
Default folder of a user on another theme is left untouched.

* [Tests] Give theme_migration_test an explicit main and gate it on WITH_CLIENT

The build machines that cannot find GTest fall back to the downloaded
googletest, which links the bare gtest library without gtest_main. Every
other test defines its own main() for that reason; this one did not, so
Debian 13, Fedora 44 and Ubuntu 26.04 failed to link it.

The test compiles client sources, so -DTEST=1 -DWITH_CLIENT=0 has no way
to build it. Gate the subdirectory on WITH_CLIENT like the other
client-source test.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-10-03 13:43:25 +02:00 • committed by GitHub
parent add65caae5
commit 014983b5bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 299 additions and 1 deletions

View file

@ -23,6 +23,7 @@
#include <libcockatrice/settings/paths_settings.h>
#define SYSTEM_THEME_NAME "System"
#define LEGACY_SYSTEM_THEME_NAME "Default"
#define FUSION_THEME_NAME "Fusion"
#define STYLE_CSS_NAME "style.css"
#define HANDZONE_BG_NAME "handzone"
@ -122,12 +123,84 @@ ThemeManager::ThemeManager(QObject *parent) : QObject(parent)
themeChangedSlot();
}
// Copy every file of sourceDir into targetDir without ever overwriting an
// existing file, recursing into subdirectories (zones/, backgrounds/, ...).
// Returns false as soon as an entry could not be copied, leaving the source
// untouched so the caller can retry later.
static bool mergeDirWithoutOverwrite(const QString &sourceDir, const QString &targetDir)
{
const QDir target(targetDir);
bool merged = true;
const QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden;
for (const QFileInfo &entry : QDir(sourceDir).entryInfoList(filters)) {
const QString targetEntry = target.absoluteFilePath(entry.fileName());
if (entry.isDir()) {
if (!QDir().mkpath(targetEntry) || !mergeDirWithoutOverwrite(entry.absoluteFilePath(), targetEntry)) {
merged = false;
}
} else if (!target.exists(entry.fileName()) && !QFile::copy(entry.absoluteFilePath(), targetEntry)) {
qCWarning(ThemeManagerLog) << "Could not migrate theme file:" << entry.absoluteFilePath();
merged = false;
}
}
return merged;
}
void ThemeManager::migrateLegacyThemeDir(const QString &themesPath, const QString &currentThemeName)
{
const QString legacyPath = QDir(themesPath).absoluteFilePath(LEGACY_SYSTEM_THEME_NAME);
if (!QDir(legacyPath).exists()) {
return;
}
// Only the renamed theme's own directory is migrated. A user sitting on a
// different theme may well have hand-made a "Default" folder of their own,
// and silently folding it into "System" would be rude. The check accepts
// both the pre-rename name and the post-rename one, since a user who
// already ran the rename keeps their settings on "System" while their
// customised directory is still called "Default".
if (currentThemeName != LEGACY_SYSTEM_THEME_NAME && currentThemeName != SYSTEM_THEME_NAME) {
return;
}
const QString targetPath = QDir(themesPath).absoluteFilePath(SYSTEM_THEME_NAME);
if (!QDir(targetPath).exists() && QDir().rename(legacyPath, targetPath)) {
qCInfo(ThemeManagerLog) << "Migrated customised theme directory" << LEGACY_SYSTEM_THEME_NAME << "to"
<< SYSTEM_THEME_NAME;
return;
}
// The rename is only possible when the target does not exist yet. When it
// does (a partial earlier upgrade, or a user who re-customised the theme
// after the rename), fall back to merging so nothing is lost: existing
// "System" files win, legacy files only fill gaps. Because every legacy
// file either already exists at the target or is copied there, removing
// the legacy directory afterwards cannot drop user data.
if (!mergeDirWithoutOverwrite(legacyPath, targetPath)) {
qCWarning(ThemeManagerLog) << "Could not fully migrate theme directory" << legacyPath << "; keeping it for a"
<< "later retry";
return;
}
if (!QDir(legacyPath).removeRecursively()) {
qCWarning(ThemeManagerLog) << "Migrated theme directory but could not remove the legacy one:" << legacyPath;
return;
}
qCInfo(ThemeManagerLog) << "Merged customised theme directory" << LEGACY_SYSTEM_THEME_NAME << "into"
<< SYSTEM_THEME_NAME;
}
void ThemeManager::ensureThemeDirectoryExists()
{
auto &settings = SettingsCache::instance();
// Carry the user's theme customisations (zone graphics, stylesheets,
// palettes, theme.cfg) across before the stored name is rewritten.
migrateLegacyThemeDir(settings.paths().getThemesPath(), settings.getThemeName());
// Migrate the old "Default" theme name to "System"
if (settings.getThemeName() == "Default") {
if (settings.getThemeName() == LEGACY_SYSTEM_THEME_NAME) {
settings.setThemeName(SYSTEM_THEME_NAME);
}