diff --git a/crates/qbz-qt/qml/shell/AppShell.qml b/crates/qbz-qt/qml/shell/AppShell.qml index 5ee7048cc..a59d3c32d 100644 --- a/crates/qbz-qt/qml/shell/AppShell.qml +++ b/crates/qbz-qt/qml/shell/AppShell.qml @@ -252,12 +252,14 @@ Rectangle { hostName: QbzOrbit.hostName } - // The gutter the player bar keeps to the window edges (below). Nothing - // is painted behind it: the bar floats over the shell's own ground — - // the ambient field or the root colour — exactly like the content pane's - // margins show what is under them (a backdrop was tried and rejected). - readonly property int npbGutter: Qt.platform.os === "osx" ? theme.npbGutterMac : theme.npbGutter - + // The bottom chrome band (NowPlayingBar.qml's root): full width, flush + // on the window's bottom edge, the same tier and paint as the HeaderBar + // above. The bar layout inside it keeps the content pane's gutter to the + // window's left, right and bottom edges — wider on macOS, where the + // Liquid Glass corner curve ate the cover's corner (QbzTheme.npbGutterMac) + // — and the band sizes itself: the mode-aware layout height (Small + // collapses to one header-tall row; New/Classic/Large keep the full + // 112px, AppShell.slint:396) plus that gutter. NowPlayingBar { id: npb // The small seek thumb/hit area extends above the bar by a few pixels. @@ -267,17 +269,6 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - // Inset by the content pane's gutter on the three window edges - // (2026-09-13, every mode): the bar's ends line up with the pane's - // and nothing in it touches the window edge — wider on macOS, where - // the Liquid Glass corner curve ate the cover's corner - // (QbzTheme.npbGutterMac). - anchors.leftMargin: root.npbGutter - anchors.rightMargin: root.npbGutter - anchors.bottomMargin: root.npbGutter - // Mode-aware height (AppShell.slint:396): Small collapses to one - // header-tall row; New/Classic/Large keep the full 112px. - height: QbzShell.npbMode === 2 ? theme.npbSmallHeight : theme.npbLargeHeight // The shared hover-tooltip overlay (declared further down — id // references resolve at completion). All four modes consume it for // Shuffle/Repeat state; the full bar also uses it for Qobuz Connect. @@ -666,9 +657,9 @@ Rectangle { // so this width also sets the art size. x: 16 width: 208 - // Flush with the bar's bottom edge, which now sits a gutter above - // the window's (npbGutter) — the dock lifts with it. - y: npb.y + npb.height - QbzShell.largeDockHeight + // Flush with the bar LAYOUT's bottom edge, which sits a gutter above + // the window's (the band's inset) — the dock lifts with it. + y: npb.y + npb.height - npb.gutter - QbzShell.largeDockHeight ambientOn: root.ambientOn } diff --git a/crates/qbz-qt/qml/shell/NowPlayingBar.qml b/crates/qbz-qt/qml/shell/NowPlayingBar.qml index 69fc685ec..9b2781db4 100644 --- a/crates/qbz-qt/qml/shell/NowPlayingBar.qml +++ b/crates/qbz-qt/qml/shell/NowPlayingBar.qml @@ -1,13 +1,39 @@ // NowPlayingBar SHELL — the mode seam (NowPlayingBar.slint, phase 18): // mounts NowPlayingBarSmall for mode 2 (Small) and the full PlayerBar for -// modes 0 (New) / 1 (Classic) / 3 (Large). AppShell pins the height -// mode-aware (42px Small / 112px otherwise). +// modes 0 (New) / 1 (Classic) / 3 (Large), and sizes itself mode-aware +// (42px Small / 112px otherwise, plus the gutter below). +// +// THE BAND (2026-09-13). This root is the bottom chrome band, the exact +// counterpart of HeaderBar at the top: one full-width piece of chrome, flush +// on the window's bottom edge, painted surface-card @ 0.5 over the field +// while the ambient background is active and opaque surface-card otherwise. +// The bar LAYOUT mounted below is inset by `gutter` on its left, right and +// bottom, so the bar's ends line up with the content pane's and nothing in +// it touches the window edge — while the band stays continuous around it. +// Both alternatives were tried and rejected the same day: insetting the +// whole bar left the raw field showing on three sides (the only zone of the +// shell without chrome), and a frame slab behind an inset plate read as a +// bar glued onto a slab. A layout knows nothing about the band or the +// gutter: it paints no background of its own and fills the rect it is given, +// which is what keeps a future layout from needing either. import QtQuick import com.blitzfc.qbz +import "../theme" -Item { +Rectangle { id: root + color: ambientOn ? theme.surfaceCardA50 : theme.surfaceCard + readonly property bool ambientOn: theme.ambientOn + + QbzTheme { id: theme } + + /// The gutter the bar layout keeps to the window's left, right and + /// bottom edges: the pane's 8px, wider on macOS (QbzTheme.npbGutterMac). + readonly property int gutter: Qt.platform.os === "osx" ? theme.npbGutterMac : theme.npbGutter + /// The layout's own height (PlayerBar.slint / PlayerBarSmall.slint). + readonly property int layoutHeight: QbzShell.npbMode === 2 ? theme.npbSmallHeight : theme.npbLargeHeight + implicitHeight: layoutHeight + gutter // The shell's shared hover-tooltip overlay (controls/QbzTooltip.qml), // fed in by AppShell (same pattern as Sidebar.tooltip). Forwarded into @@ -18,6 +44,10 @@ Item { Loader { id: barLoader anchors.fill: parent + // The inset. Top stays flush: the band's top edge IS the layout's. + anchors.leftMargin: root.gutter + anchors.rightMargin: root.gutter + anchors.bottomMargin: root.gutter source: QbzShell.npbMode === 2 ? "NowPlayingBarSmall.qml" : "PlayerBar.qml" } diff --git a/crates/qbz-qt/qml/shell/NowPlayingBarSmall.qml b/crates/qbz-qt/qml/shell/NowPlayingBarSmall.qml index 5f0f64f50..3ee433505 100644 --- a/crates/qbz-qt/qml/shell/NowPlayingBarSmall.qml +++ b/crates/qbz-qt/qml/shell/NowPlayingBarSmall.qml @@ -40,9 +40,10 @@ import "../theme" Rectangle { id: root - // surface-card @ 0.5 while the ambient background is active (phase 14, - // PlayerBarSmall.slint). - color: ambientOn ? theme.surfaceCardA50 : theme.surfaceCard + // No background of its own: the chrome band is NowPlayingBar.qml's root + // (surface-card @ 0.5 under the ambient background, opaque surface-card + // otherwise), full width behind this inset layout — see THE BAND there. + color: "transparent" readonly property bool ambientOn: theme.ambientOn /// AppShell's one shared hover-tooltip overlay. property Item tooltip: null @@ -65,7 +66,7 @@ Rectangle { // 1366 / 1920) are the SHARED theme token, so this bar and PlayerBar can // never drift apart — see QbzTheme.npbSideFrac. // `root.width` is the window width minus the two side gutters - // (AppShell `npbGutter`): the bar is anchored left-to-right on the shell + // (NowPlayingBar.qml `gutter`): the bar is anchored left-to-right on the shell // root, exactly like PlayerBarSmall.slint reads its own root. property real sideFrac: theme.npbSideFrac(root.width) property real colCentre: 1.0 - 2.0 * root.sideFrac diff --git a/crates/qbz-qt/qml/shell/PlayerBar.qml b/crates/qbz-qt/qml/shell/PlayerBar.qml index 6a1618c37..105bd5afa 100644 --- a/crates/qbz-qt/qml/shell/PlayerBar.qml +++ b/crates/qbz-qt/qml/shell/PlayerBar.qml @@ -69,7 +69,10 @@ import "../theme" Rectangle { id: root - color: ambientOn ? theme.surfaceCardA50 : theme.surfaceCard + // No background of its own: the chrome band is NowPlayingBar.qml's root + // (surface-card @ 0.5 under the ambient background, opaque surface-card + // otherwise), full width behind this inset layout — see THE BAND there. + color: "transparent" readonly property bool ambientOn: theme.ambientOn readonly property bool largeActive: QbzShell.npbMode === 3 && QbzShell.sidebarState === 0 readonly property bool isClassic: QbzShell.npbMode === 1 @@ -103,7 +106,7 @@ Rectangle { // are the SHARED theme token (QbzTheme.npbSideFrac) — the Small bar reads // the same one, so the two bars cannot drift apart. // `root.width` is the window width minus the two side gutters - // (AppShell `npbGutter`): the bar is anchored left-to-right on the shell + // (NowPlayingBar.qml `gutter`): the bar is anchored left-to-right on the shell // root, exactly like PlayerBar.slint reads its own root. property real sideFrac: theme.npbSideFrac(root.width) property real colSide: isClassic ? 0.324 : sideFrac