[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 e9defaecf6
commit 64150a32d4
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)
{
// 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>();
for (int i = 0; i < fontEnum.keyCount(); ++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));
}
blockSignals(false);
}
FontConfig FontGridWidget::currentFontConfig() const

View file

@ -8,6 +8,7 @@
#include "quick_setup_panel.h"
#include <QApplication>
#include <QCloseEvent>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QDir>
@ -186,7 +187,10 @@ void PaletteEditorDialog::setupUi()
connect(autoApplyTimer, &QTimer::timeout, this, &PaletteEditorDialog::onApply);
connect(quickSetupPanel, &QuickSetupPanel::valueChanged, this, &PaletteEditorDialog::onGenerateFromAccent);
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(resetBtn, &QPushButton::clicked, this, &PaletteEditorDialog::onReset);
connect(saveBtn, &QPushButton::clicked, this, &PaletteEditorDialog::onSave);
@ -281,15 +285,11 @@ void PaletteEditorDialog::loadSchemes()
void PaletteEditorDialog::loadFonts()
{
// Same user-dir > system-dir fallback ThemeManager uses: the shipped
// fonts.toml may live in the system theme directory rather than the
// resolved (user) theme directory.
FontConfig cfg = ThemeManager::loadFontConfig(themeDirPath);
if (!cfg.hasFonts()) {
cfg = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName);
}
savedFontConfig = cfg;
fontGrid->loadFonts(cfg);
savedFontConfig = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName);
fontGrid->blockSignals(true);
fontGrid->loadFonts(savedFontConfig);
fontGrid->blockSignals(false);
fontsDirty = false;
}
void PaletteEditorDialog::seedAccentFromScheme(const QString &scheme)
@ -322,7 +322,9 @@ void PaletteEditorDialog::onGenerateFromAccent(const QColor &accent, int intensi
void PaletteEditorDialog::onApply()
{
themeManager->previewPalette(paletteGrid->currentPaletteConfig(), loadedScheme);
themeManager->previewFonts(fontGrid->currentFontConfig());
if (fontsDirty) {
themeManager->previewFonts(fontGrid->currentFontConfig());
}
}
void PaletteEditorDialog::onSave()
@ -374,14 +376,17 @@ void PaletteEditorDialog::onSave()
}
// Persist the shared per-theme fonts when they changed.
const FontConfig fontCfg = fontGrid->currentFontConfig();
if (fontCfg != savedFontConfig) {
if (!ThemeManager::saveFontConfig(saveDir, fontCfg)) {
QMessageBox::warning(this, tr("Save failed"),
tr("Could not write %1 to:\n%2").arg(FontConfig::fileName(), saveDir));
return;
if (fontsDirty) {
const FontConfig fontCfg = fontGrid->currentFontConfig();
if (fontCfg != savedFontConfig) {
if (!ThemeManager::saveFontConfig(saveDir, fontCfg)) {
QMessageBox::warning(this, tr("Save failed"),
tr("Could not write %1 to:\n%2").arg(FontConfig::fileName(), saveDir));
return;
}
savedFontConfig = fontCfg;
}
savedFontConfig = fontCfg;
fontsDirty = false;
}
themeManager->reloadCurrentTheme();
@ -390,9 +395,16 @@ void PaletteEditorDialog::onSave()
void PaletteEditorDialog::onReset()
{
const bool hadFontPreview = fontsDirty;
workingConfig[loadedScheme] = savedConfig[loadedScheme];
paletteGrid->loadPalette(savedConfig[loadedScheme]);
fontGrid->blockSignals(true);
fontGrid->loadFonts(savedFontConfig);
fontGrid->blockSignals(false);
fontsDirty = false;
if (hadFontPreview) {
themeManager->previewFonts(savedFontConfig);
}
seedAccentFromScheme(loadedScheme);
}
@ -438,7 +450,18 @@ void PaletteEditorDialog::onRevertToDefault()
const FontConfig fontDef = ThemeManager::loadDefaultFontConfig(themeDirPath, themeName);
savedFontConfig = fontDef;
fontGrid->blockSignals(true);
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)
@ -450,6 +473,14 @@ void PaletteEditorDialog::changeEvent(QEvent *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()
{
const QPalette base = qApp->palette();

View file

@ -19,6 +19,7 @@ class FontGridWidget;
class QuickSetupPanel;
class QFrame;
class QWidget;
class QCloseEvent;
class PaletteEditorDialog : public QDialog
{
@ -26,6 +27,7 @@ class PaletteEditorDialog : public QDialog
public:
explicit PaletteEditorDialog(const QString &themeDirPath, const QString &themeName, QWidget *parent = nullptr);
void loadSchemes();
void reject() override;
private slots:
void onSave();
@ -79,11 +81,16 @@ private:
// 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.
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;
protected:
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);
// ── Load fonts: shared per theme, no light/dark split ───────────────────
FontConfig fonts = loadFontConfig(dirPath);
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);
}
}
FontConfig fonts = loadDefaultFontConfig(dirPath, themeName);
currentFonts = fonts;
const auto fontIt = currentFonts.fonts.constFind(AppFont::InterfaceDefault);
if (fontIt != currentFonts.fonts.constEnd()) {