[Fonts] Fix font preview leaks and reuse default-font loader

This commit is contained in:
Lukas Brübach 2026-10-16 09:57:01 +02:00
parent e2e12a3743
commit 2e11418a3d
4 changed files with 62 additions and 29 deletions

View file

@ -71,6 +71,9 @@ FontGridWidget::FontGridWidget(QWidget *parent) : QWidget(parent)
void FontGridWidget::loadFonts(const FontConfig &cfg) void FontGridWidget::loadFonts(const FontConfig &cfg)
{ {
// Suppress fontsChanged while materializing fallback rows so the initial
// load never reads as a user edit (mirrors paletteGrid blocking).
blockSignals(true);
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) {
auto role = static_cast<AppFont::Role>(fontEnum.value(i)); auto role = static_cast<AppFont::Role>(fontEnum.value(i));
@ -90,6 +93,7 @@ void FontGridWidget::loadFonts(const FontConfig &cfg)
} }
row.sizeSpin->setValue(qBound(MIN_FONT_SIZE, size, MAX_FONT_SIZE)); row.sizeSpin->setValue(qBound(MIN_FONT_SIZE, size, MAX_FONT_SIZE));
} }
blockSignals(false);
} }
FontConfig FontGridWidget::currentFontConfig() const FontConfig FontGridWidget::currentFontConfig() const

View file

@ -8,6 +8,7 @@
#include "quick_setup_panel.h" #include "quick_setup_panel.h"
#include <QApplication> #include <QApplication>
#include <QCloseEvent>
#include <QComboBox> #include <QComboBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDir> #include <QDir>
@ -186,7 +187,10 @@ void PaletteEditorDialog::setupUi()
connect(autoApplyTimer, &QTimer::timeout, this, &PaletteEditorDialog::onApply); connect(autoApplyTimer, &QTimer::timeout, this, &PaletteEditorDialog::onApply);
connect(quickSetupPanel, &QuickSetupPanel::valueChanged, this, &PaletteEditorDialog::onGenerateFromAccent); connect(quickSetupPanel, &QuickSetupPanel::valueChanged, this, &PaletteEditorDialog::onGenerateFromAccent);
connect(paletteGrid, &PaletteGridWidget::paletteChanged, this, [this] { autoApplyTimer->start(); }); connect(paletteGrid, &PaletteGridWidget::paletteChanged, this, [this] { autoApplyTimer->start(); });
connect(fontGrid, &FontGridWidget::fontsChanged, this, [this] { autoApplyTimer->start(); }); connect(fontGrid, &FontGridWidget::fontsChanged, this, [this] {
fontsDirty = true;
autoApplyTimer->start();
});
connect(revertButton, &QPushButton::clicked, this, &PaletteEditorDialog::onRevertToDefault); connect(revertButton, &QPushButton::clicked, this, &PaletteEditorDialog::onRevertToDefault);
connect(resetBtn, &QPushButton::clicked, this, &PaletteEditorDialog::onReset); connect(resetBtn, &QPushButton::clicked, this, &PaletteEditorDialog::onReset);
connect(saveBtn, &QPushButton::clicked, this, &PaletteEditorDialog::onSave); connect(saveBtn, &QPushButton::clicked, this, &PaletteEditorDialog::onSave);
@ -281,15 +285,11 @@ void PaletteEditorDialog::loadSchemes()
void PaletteEditorDialog::loadFonts() void PaletteEditorDialog::loadFonts()
{ {
// Same user-dir > system-dir fallback ThemeManager uses: the shipped savedFontConfig = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName);
// fonts.toml may live in the system theme directory rather than the fontGrid->blockSignals(true);
// resolved (user) theme directory. fontGrid->loadFonts(savedFontConfig);
FontConfig cfg = ThemeManager::loadFontConfig(themeDirPath); fontGrid->blockSignals(false);
if (!cfg.hasFonts()) { fontsDirty = false;
cfg = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName);
}
savedFontConfig = cfg;
fontGrid->loadFonts(cfg);
} }
void PaletteEditorDialog::seedAccentFromScheme(const QString &scheme) void PaletteEditorDialog::seedAccentFromScheme(const QString &scheme)
@ -322,7 +322,9 @@ void PaletteEditorDialog::onGenerateFromAccent(const QColor &accent, int intensi
void PaletteEditorDialog::onApply() void PaletteEditorDialog::onApply()
{ {
themeManager->previewPalette(paletteGrid->currentPaletteConfig(), loadedScheme); themeManager->previewPalette(paletteGrid->currentPaletteConfig(), loadedScheme);
themeManager->previewFonts(fontGrid->currentFontConfig()); if (fontsDirty) {
themeManager->previewFonts(fontGrid->currentFontConfig());
}
} }
void PaletteEditorDialog::onSave() void PaletteEditorDialog::onSave()
@ -374,14 +376,17 @@ void PaletteEditorDialog::onSave()
} }
// Persist the shared per-theme fonts when they changed. // Persist the shared per-theme fonts when they changed.
const FontConfig fontCfg = fontGrid->currentFontConfig(); if (fontsDirty) {
if (fontCfg != savedFontConfig) { const FontConfig fontCfg = fontGrid->currentFontConfig();
if (!ThemeManager::saveFontConfig(saveDir, fontCfg)) { if (fontCfg != savedFontConfig) {
QMessageBox::warning(this, tr("Save failed"), if (!ThemeManager::saveFontConfig(saveDir, fontCfg)) {
tr("Could not write %1 to:\n%2").arg(FontConfig::fileName(), saveDir)); QMessageBox::warning(this, tr("Save failed"),
return; tr("Could not write %1 to:\n%2").arg(FontConfig::fileName(), saveDir));
return;
}
savedFontConfig = fontCfg;
} }
savedFontConfig = fontCfg; fontsDirty = false;
} }
themeManager->reloadCurrentTheme(); themeManager->reloadCurrentTheme();
@ -390,9 +395,16 @@ void PaletteEditorDialog::onSave()
void PaletteEditorDialog::onReset() void PaletteEditorDialog::onReset()
{ {
const bool hadFontPreview = fontsDirty;
workingConfig[loadedScheme] = savedConfig[loadedScheme]; workingConfig[loadedScheme] = savedConfig[loadedScheme];
paletteGrid->loadPalette(savedConfig[loadedScheme]); paletteGrid->loadPalette(savedConfig[loadedScheme]);
fontGrid->blockSignals(true);
fontGrid->loadFonts(savedFontConfig); fontGrid->loadFonts(savedFontConfig);
fontGrid->blockSignals(false);
fontsDirty = false;
if (hadFontPreview) {
themeManager->previewFonts(savedFontConfig);
}
seedAccentFromScheme(loadedScheme); seedAccentFromScheme(loadedScheme);
} }
@ -438,7 +450,18 @@ void PaletteEditorDialog::onRevertToDefault()
const FontConfig fontDef = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName); const FontConfig fontDef = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName);
savedFontConfig = fontDef; savedFontConfig = fontDef;
fontGrid->blockSignals(true);
fontGrid->loadFonts(fontDef); fontGrid->loadFonts(fontDef);
fontGrid->blockSignals(false);
fontsDirty = false;
}
void PaletteEditorDialog::reject()
{
// Discard any unsaved live preview (palette and fonts) so closing without
// Save restores the active theme instead of leaking the preview globally.
themeManager->reloadCurrentTheme();
QDialog::reject();
} }
void PaletteEditorDialog::changeEvent(QEvent *e) void PaletteEditorDialog::changeEvent(QEvent *e)
@ -450,6 +473,14 @@ void PaletteEditorDialog::changeEvent(QEvent *e)
QDialog::changeEvent(e); QDialog::changeEvent(e);
} }
void PaletteEditorDialog::closeEvent(QCloseEvent *e)
{
// Window-system close bypasses the Close button: discard the preview here
// too. The extra reload when closeEvent forwards to reject() is harmless.
themeManager->reloadCurrentTheme();
QDialog::closeEvent(e);
}
void PaletteEditorDialog::refreshChromePalettes() void PaletteEditorDialog::refreshChromePalettes()
{ {
const QPalette base = qApp->palette(); const QPalette base = qApp->palette();

View file

@ -19,6 +19,7 @@ class FontGridWidget;
class QuickSetupPanel; class QuickSetupPanel;
class QFrame; class QFrame;
class QWidget; class QWidget;
class QCloseEvent;
class PaletteEditorDialog : public QDialog class PaletteEditorDialog : public QDialog
{ {
@ -26,6 +27,7 @@ class PaletteEditorDialog : public QDialog
public: public:
explicit PaletteEditorDialog(const QString &themeDirPath, const QString &themeName, QWidget *parent = nullptr); explicit PaletteEditorDialog(const QString &themeDirPath, const QString &themeName, QWidget *parent = nullptr);
void loadSchemes(); void loadSchemes();
void reject() override;
private slots: private slots:
void onSave(); void onSave();
@ -79,11 +81,16 @@ private:
// Fonts are shared per theme (fonts.toml, not split by light/dark), so a // Fonts are shared per theme (fonts.toml, not split by light/dark), so a
// single saved config suffices; the live edits are read from fontGrid. // single saved config suffices; the live edits are read from fontGrid.
FontConfig savedFontConfig; FontConfig savedFontConfig;
// Set only by user-driven fontsChanged after the initial load completes.
// Gates both previewFonts and the fonts.toml write so a palette-only Save
// never materializes fallback fonts to disk.
bool fontsDirty = false;
QTimer *autoApplyTimer = nullptr; QTimer *autoApplyTimer = nullptr;
protected: protected:
void changeEvent(QEvent *e) override; void changeEvent(QEvent *e) override;
void closeEvent(QCloseEvent *e) override;
}; };
#endif // COCKATRICE_PALETTE_EDITOR_DIALOG_H #endif // COCKATRICE_PALETTE_EDITOR_DIALOG_H

View file

@ -732,16 +732,7 @@ void ThemeManager::themeChangedSlot()
applyStyleAndPalette(themeName, themeCfg, palette, activeScheme); applyStyleAndPalette(themeName, themeCfg, palette, activeScheme);
// ── Load fonts: shared per theme, no light/dark split ─────────────────── // ── Load fonts: shared per theme, no light/dark split ───────────────────
FontConfig fonts = loadFontConfig(dirPath); FontConfig fonts = loadDefaultFontConfig(dirPath, themeName);
if (!fonts.hasFonts()) {
// The shipped fonts.toml may live in the system theme directory rather
// than the resolved (user) theme directory, so built-in themes still
// get their curated fonts.
const QString sysFontPath = QDir(systemThemesBasePath()).absoluteFilePath(themeName);
if (sysFontPath != dirPath) {
fonts = loadFontConfig(sysFontPath);
}
}
currentFonts = fonts; currentFonts = fonts;
const auto fontIt = currentFonts.fonts.constFind(AppFont::InterfaceDefault); const auto fontIt = currentFonts.fonts.constFind(AppFont::InterfaceDefault);
if (fontIt != currentFonts.fonts.constEnd()) { if (fontIt != currentFonts.fonts.constEnd()) {