feat(settings): an app-wide typeface, picked from the fonts already shipped

Settings > Appearance > Typography & Language gains a Font row. The
catalogue is the SAME five the lyrics panel offers and the same slugs,
because they are the same files: everything is bundled in
qml/assets/fonts/, so a choice cannot fail on a machine that lacks the
face and looks identical everywhere. "System" resolves to the window
default, which is Inter.

It applies LIVE, with no restart: the family sits on the
ApplicationWindow, which every Text that does not name its own family
inherits.

The index rides its own qproperty rather than the settings document,
because the consumer is the root window and making it parse the whole
settingsJson on every republish to read one integer is the wrong trade.
Same shape as theme_filter beside it.

The loaders are lazy, exactly like the lyrics picker: a family nobody
selected is never loaded, and one that has not finished loading falls
back to Inter rather than rendering the app in a wrong face for a frame.

Only the first label is translated — a typeface name is a proper noun.

cc
This commit is contained in:
vicrodh committed 2026-08-22 00:00:05 -06:00
1 parent 58fa77a390
commit 3c4c2dae20
3 files changed
+97 -1

No files matched your search

+32 -1
View File
@@ -178,7 +178,38 @@ ApplicationWindow {
// the family app-wide, so QualityBadge can name it without carrying its
// own loader into every row it renders in.
FontLoader { id: lineSeed; source: "assets/fonts/LINESeedJP-Regular.ttf" }
font.family: interRegular.status === FontLoader.Ready ? interRegular.name : "Sans Serif"
// ── Settings > Appearance > Typography & Language > Font ───────────────
// The app-wide typeface. Every Text in the tree that does not name a
// family of its own inherits THIS one, which is why the setting lives on
// the ApplicationWindow and applies live with no restart.
//
// The catalog is settings_qt::APP_FONT_VALUES and the index rides
// `QbzShell.appFontIndex` — a property of its own rather than a read out
// of settingsJson, so the root window does not parse the whole settings
// document to find one integer.
//
// LAZY, exactly like the lyrics panel's own picker
// (shell/LyricsLinesView.qml:66-80): a family nobody selected is never
// loaded, and a family that has not FINISHED loading falls back to Inter
// rather than rendering the app in a wrong face for a frame.
FontLoader { id: appMontserrat; source: QbzShell.appFontIndex === 2 ? "assets/fonts/Montserrat-VariableFont_wght.ttf" : "" }
FontLoader { id: appNoto; source: QbzShell.appFontIndex === 3 ? "assets/fonts/NotoSans-VariableFont_wdth,wght.ttf" : "" }
FontLoader { id: appSource; source: QbzShell.appFontIndex === 4 ? "assets/fonts/SourceSans3-VariableFont_wght.ttf" : "" }
readonly property string appFontFamily: {
if (QbzShell.appFontIndex === 1)
return lineSeed.status === FontLoader.Ready ? lineSeed.name : ""
if (QbzShell.appFontIndex === 2)
return appMontserrat.status === FontLoader.Ready ? appMontserrat.name : ""
if (QbzShell.appFontIndex === 3)
return appNoto.status === FontLoader.Ready ? appNoto.name : ""
if (QbzShell.appFontIndex === 4)
return appSource.status === FontLoader.Ready ? appSource.name : ""
return ""
}
font.family: window.appFontFamily !== ""
? window.appFontFamily
: (interRegular.status === FontLoader.Ready ? interRegular.name : "Sans Serif")
// Phase 23: every domain singleton boots (registers its Qt-thread
// hop; QbzSession.boot additionally fires the app boot sequence).