The Large dock was a bare cover overlay: the sidebar knew nothing about it, so the playlist rows kept running to the sidebar's bottom edge and sat underneath the album art. Sidebar.slint reserves the in-sidebar portion of the dock; the port had dropped that. Reserve it the same way, from a single source of truth. QbzShell gains largeDockHeight (shell_bridge.rs owns the arithmetic: 9 top + band + gap + 208 art + 4 bottom), the sidebar subtracts largeDockHeight - npbLargeHeight from the playlist list, and AppShell pins the overlay at height - largeDockHeight. The 112px bar height becomes a theme token so the shell and the reservation cannot drift apart. The band the cover was missing needs the FFT tap, so wire it: the runtime is built with_visualizer, viz_qt.rs runs the frontend-agnostic producer from qbz-audio and drains it at 30fps into a new QbzViz domain bridge as typed QList<f32>. Bars (28 mirrored columns over the 14 active bins), Waveform (48 downsampled L columns) and Energy (5 bands) render as Slint does, the cover's eye button toggles the band, clicking it cycles the mode, and both persist to ui_prefs. Cost is zero when unused: the tap starts disabled, the eye toggle gates capture, the drain thread parks while off, and the transport is mirrored so a paused player parks the producer instead of re-analyzing a stale buffer. The QML binds bar HEIGHTS against static-model Repeaters — the lists are replaced per frame, so a model binding would rebuild every delegate 30 times a second. Adds the eye/eye-off icon pair to the seven baked tint variants. 18/18 tests; offscreen smoke clean (0 QML errors, home published). cc
143 lines
6.8 KiB
QML
143 lines
6.8 KiB
QML
// Shared theme tokens (phase 19: LIVE) — the color tokens now bind to
|
|
// QbzShell.themeJson, the serialized qbz-theme ThemeColors for the
|
|
// persisted ui_prefs `theme` slug (theme_qt.rs — the Qt equivalent of the
|
|
// Slint theme::push_colors: switching the theme republishes the document
|
|
// and every consumer repaints, no restart). Until the first publish lands
|
|
// the baked dark fallback below applies (the bridge seeds themeJson at
|
|
// construction, so the fallback is purely defensive).
|
|
//
|
|
// The non-color tokens (spacing / radius / typography / layout from
|
|
// foundation/*.slint) stay static regardless of theme.
|
|
//
|
|
// Kept as a plain instantiable QtObject (NOT a pragma Singleton — the
|
|
// cxx-qt-build generated qmldir cannot mark singletons).
|
|
|
|
import QtQuick
|
|
import com.blitzfc.qbz
|
|
|
|
QtObject {
|
|
// Baked dark fallback (pre-publish only) — the old static POC tokens.
|
|
readonly property var _fallback: ({
|
|
"surfaceMain": "#0f0f0f", "surfaceCard": "#1a1a1a",
|
|
"surfaceElevated": "#2a2a2a", "surfaceHover": "#10ffffff",
|
|
"bgHover": "#2a2a2a",
|
|
"textPrimary": "#ffffff", "textSecondary": "#cccccc",
|
|
"textMuted": "#888888", "textDisabled": "#555555",
|
|
"accent": "#4285f4", "accentHover": "#5a9bf4",
|
|
"accentPressed": "#3275e4", "accentText": "#ffffff",
|
|
"danger": "#e0564f", "dangerBg": "#26e0564f",
|
|
"dangerBorder": "#59e0564f", "dangerHover": "#e87670",
|
|
"warning": "#fbbf24", "warningBg": "#26fbbf24",
|
|
"warningBorder": "#59fbbf24", "warningHover": "#fdcb47",
|
|
"success": "#3fae6a", "successBg": "#263fae6a",
|
|
"successBorder": "#593fae6a", "successHover": "#55bd7e",
|
|
"borderSubtle": "#14ffffff", "borderMuted": "#38ffffff",
|
|
"borderStrong": "#59ffffff", "focusRing": "#4285f4",
|
|
"favorite": "#ef4444", "cardShadow": "#66000000",
|
|
"surfaceCardA50": "#801a1a1a", "surfaceMainA22": "#380f0f0f",
|
|
"surfaceMainA30": "#4d0f0f0f", "frostBorder": "#1affffff",
|
|
"isDark": true
|
|
})
|
|
|
|
readonly property var _doc: {
|
|
if (QbzShell.themeJson === "") return _fallback
|
|
try {
|
|
return JSON.parse(QbzShell.themeJson)
|
|
} catch (e) {
|
|
return _fallback
|
|
}
|
|
}
|
|
function _c(key) {
|
|
const v = _doc[key]
|
|
return v === undefined ? _fallback[key] : v
|
|
}
|
|
|
|
// --- Theme colors (live; ThemeColors contract) ----------------------
|
|
readonly property bool isDark: _doc.isDark === undefined ? true : _doc.isDark
|
|
readonly property color surfaceMain: _c("surfaceMain")
|
|
readonly property color surfaceCard: _c("surfaceCard")
|
|
readonly property color surfaceElevated: _c("surfaceElevated")
|
|
readonly property color surfaceHover: _c("surfaceHover")
|
|
readonly property color bgHover: _c("bgHover")
|
|
readonly property color textPrimary: _c("textPrimary")
|
|
readonly property color textSecondary: _c("textSecondary")
|
|
readonly property color textMuted: _c("textMuted")
|
|
readonly property color textDisabled: _c("textDisabled")
|
|
readonly property color accent: _c("accent")
|
|
readonly property color accentHover: _c("accentHover")
|
|
readonly property color accentPressed: _c("accentPressed")
|
|
readonly property color accentText: _c("accentText")
|
|
readonly property color danger: _c("danger")
|
|
readonly property color dangerBg: _c("dangerBg")
|
|
readonly property color dangerBorder: _c("dangerBorder")
|
|
readonly property color dangerHover: _c("dangerHover")
|
|
readonly property color warning: _c("warning")
|
|
readonly property color warningBg: _c("warningBg")
|
|
readonly property color warningBorder: _c("warningBorder")
|
|
readonly property color warningHover: _c("warningHover")
|
|
readonly property color success: _c("success")
|
|
readonly property color successBg: _c("successBg")
|
|
readonly property color successBorder: _c("successBorder")
|
|
readonly property color successHover: _c("successHover")
|
|
readonly property color borderSubtle: _c("borderSubtle")
|
|
readonly property color borderMuted: _c("borderMuted")
|
|
readonly property color borderStrong: _c("borderStrong")
|
|
readonly property color focusRing: _c("focusRing")
|
|
readonly property color favorite: _c("favorite")
|
|
readonly property color cardShadow: _c("cardShadow")
|
|
// 24 alpha tiers ([4..95]%, white-based dark / black-based light).
|
|
readonly property var alphaTiers: _doc.alpha === undefined ? [] : _doc.alpha
|
|
// Ambient-background layering (phase 14; theme-derived since phase 19):
|
|
// chrome surface-card @50%, frosted content panel surface-main @22%,
|
|
// thin bars surface-main @30%, hairline = alpha tier 10%. The live
|
|
// values for the QBZ_BG_* env knobs still ride QbzBridge
|
|
// .ambientSurfaceAlpha / .ambientBarAlpha.
|
|
readonly property color surfaceCardA50: _c("surfaceCardA50")
|
|
readonly property color surfaceMainA22: _c("surfaceMainA22")
|
|
readonly property color surfaceMainA30: _c("surfaceMainA30")
|
|
readonly property color frostBorder: _c("frostBorder")
|
|
|
|
// --- Spacing (spacing.slint) --------------------------------------
|
|
readonly property int spacingXs: 4
|
|
readonly property int spacingSm: 8
|
|
readonly property int spacingMd: 16
|
|
readonly property int spacingLg: 20
|
|
readonly property int spacingXl: 32
|
|
readonly property int cardPadding: 52
|
|
|
|
// --- Radius (radius.slint) ----------------------------------------
|
|
readonly property int radiusSm: 8
|
|
readonly property int radiusMd: 12
|
|
readonly property int radiusLg: 16
|
|
|
|
// --- Typography (typography.slint, boost = 1.0) -------------------
|
|
readonly property int fontLegal: 13
|
|
readonly property int fontLink: 14
|
|
readonly property int fontBody: 15
|
|
readonly property int fontSubtitle: 15
|
|
readonly property int fontButton: 17
|
|
readonly property int fontSection: 18
|
|
readonly property int fontHeading: 19
|
|
readonly property int fontTitle: 25
|
|
readonly property int fontWordmark: 29
|
|
readonly property int weightRegular: Font.Normal
|
|
readonly property int weightMedium: Font.Medium
|
|
readonly property int weightSemibold: Font.DemiBold
|
|
readonly property int weightBold: Font.Bold
|
|
|
|
// --- Shell layout (layout.slint + state.slint) --------------------
|
|
readonly property int headerHeight: 42
|
|
// Three-state sidebar rendered widths (ShellState.sidebar-rendered-width).
|
|
readonly property int sidebarOpenWidth: 240
|
|
readonly property int sidebarMiniWidth: 64
|
|
// Right queue/lyrics column width (AppShell).
|
|
readonly property int queuePanelWidth: 300
|
|
// Small now-playing bar total height (ShellState.npb-small-height,
|
|
// npb-small-extra is 0 on Linux).
|
|
readonly property int npbSmallHeight: 42
|
|
// Every other bar mode (New / Classic / Large) — AppShell.slint:396.
|
|
// The Large dock's sidebar reservation subtracts this, so it must be the
|
|
// SAME value the shell pins the bar to.
|
|
readonly property int npbLargeHeight: 112
|
|
}
|