fix(qt): pane corners stay round under the dynamic background

AppShell hides its BezelCorner nubs while the field is meant to show
through the pane's corners, and Qt's clip is a rectangular scissor that
ignores radius — so any full-bleed child at y=0 squares the pane. The
doctrine was already written in AppShell.qml ("a full-bleed pane child
must round itself; there is no mask that can do it for it here"); these
eight surfaces predated it or, in the two thin bars added with the
ambient port, ignored it.

- The seven fixed 56px headers (Home, Search, DiscoverBrowse,
  ArtistReleases, PlaylistBrowse, PlaylistManager, PlayHistory) take
  topLeftRadius/topRightRadius. Invisible with the background off, where
  the view root paints the same colour beneath. PlaylistBrowse's SECOND
  bar is the category strip at mid-page and keeps square corners;
  PlayHistory keeps its unconditional opaque fill, which is reference
  parity (:43/:34) and a separate question from the corner.
- LocalAlbumView's neutral header band is now gated on the background,
  1:1 with the Qobuz page's atmosphere (AlbumView headerAtmoOn,
  AlbumPageView.slint:168): the two dark backdrops clash and the field
  already provides one. That band had been square since it was written;
  the nubs had been hiding it, and they only stand down when the field
  is showing. Header text there is theme.textPrimary, not the Qobuz
  page's light-on-dark hdrStrong, so it does not depend on the band to
  stay readable — checked before removing the backdrop from under it.

Gate: cargo build -p qbz-qt, both QML audits, offscreen smoke 0
complaints.

cc
This commit is contained in:
vicrodh committed 2026-08-13 14:33:35 -06:00
1 parent 4e4decb1e2
commit 3b03682e09
8 files changed
+92

No files matched your search

@@ -118,6 +118,18 @@ Rectangle {
Rectangle {
anchors.fill: parent
// Rounded at the TOP because this bar is full-bleed at y=0 of
// the content pane, and under the dynamic background the pane's
// own rounding cannot reach it: Qt's `clip` is a rectangular
// scissor that ignores `radius`, and AppShell hides its bezel
// nubs while the field is meant to show through the corners.
// So a full-bleed pane child rounds ITSELF — AppShell.qml says
// exactly this ("there is no mask that can do it for it here")
// and this bar was the counterexample the owner spotted in
// Discover. Invisible with the background off: the view root
// paints the same colour underneath.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: root.ambientOn ? theme.surfaceMainA30 : theme.surfaceMain
}
@@ -90,6 +90,18 @@ Rectangle {
Rectangle {
anchors.fill: parent
// Rounded at the TOP because this bar is full-bleed at y=0 of
// the content pane, and under the dynamic background the pane's
// own rounding cannot reach it: Qt's `clip` is a rectangular
// scissor that ignores `radius`, and AppShell hides its bezel
// nubs while the field is meant to show through the corners.
// So a full-bleed pane child rounds ITSELF — AppShell.qml says
// exactly this ("there is no mask that can do it for it here")
// and this bar was the counterexample the owner spotted in
// Discover. Invisible with the background off: the view root
// paints the same colour underneath.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: root.ambientOn ? theme.surfaceMainA30 : theme.surfaceMain
}
+12
View File
@@ -1267,6 +1267,18 @@ Rectangle {
// lost its backing exactly when it needed one.
Rectangle {
anchors.fill: parent
// Rounded at the TOP because this bar is full-bleed at y=0 of
// the content pane, and under the dynamic background the pane's
// own rounding cannot reach it: Qt's `clip` is a rectangular
// scissor that ignores `radius`, and AppShell hides its bezel
// nubs while the field is meant to show through the corners.
// So a full-bleed pane child rounds ITSELF — AppShell.qml says
// exactly this ("there is no mask that can do it for it here")
// and this bar was the counterexample the owner spotted in
// Discover. Invisible with the background off: the view root
// paints the same colour underneath.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: root.ambientOn ? theme.surfaceMainA30 : theme.surfaceMain
}
@@ -203,7 +203,25 @@ Rectangle {
// ============================ page ===================================
// Neutral header band (local albums have no artwork-derived tint yet).
//
// SUPPRESSED under the app-wide dynamic background, exactly like the Qobuz
// page's artwork atmosphere (AlbumView.qml `headerAtmoOn = pref &&
// !ambientOn`, AlbumPageView.slint:168): the two backdrops clash, and the
// dynamic background is already providing the dark one. This band never got
// that gate, which is why the defect showed HERE and not on the Qobuz album
// page — the owner spotted it as a lost bezel, and the bezel was the
// symptom: the band is full-bleed at y=0 with no radius, so it painted over
// the content pane's rounded top corners. With the background OFF nothing
// changes, and nothing has to: AppShell's BezelCorner nubs are visible in
// that state and mask exactly these corners. They are hidden while the
// field is meant to show through them, which is what uncovered a band that
// had been square all along.
//
// Header text is `theme.textPrimary` here (not the Qobuz page's
// light-on-dark `hdrStrong`), so it does not depend on this band to stay
// readable — checked before removing the backdrop from under it.
Rectangle {
visible: !root.ambientOn
x: 0
y: 0
width: parent.width
@@ -66,6 +66,15 @@ Rectangle {
// two never take the ambient bar alpha the browse pages do.
Rectangle {
anchors.fill: parent
// Top-rounded: full-bleed at y=0 of the content pane. The fill
// stays UNCONDITIONAL per the note above (that is reference
// parity), but the corner is not a colour question — in Slint
// the pane's `clip` follows its radius and cuts this band;
// Qt's clip is a rectangular scissor, so the band squares the
// pane unless it rounds itself. Invisible with the background
// off, where the view root paints the same colour beneath.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: theme.surfaceMain
}
@@ -136,6 +136,18 @@ Rectangle {
Rectangle {
anchors.fill: parent
// Rounded at the TOP because this bar is full-bleed at y=0 of
// the content pane, and under the dynamic background the pane's
// own rounding cannot reach it: Qt's `clip` is a rectangular
// scissor that ignores `radius`, and AppShell hides its bezel
// nubs while the field is meant to show through the corners.
// So a full-bleed pane child rounds ITSELF — AppShell.qml says
// exactly this ("there is no mask that can do it for it here")
// and this bar was the counterexample the owner spotted in
// Discover. Invisible with the background off: the view root
// paints the same colour underneath.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: root.ambientOn ? theme.surfaceMainA30 : theme.surfaceMain
}
@@ -136,6 +136,11 @@ Rectangle {
// view's fixed header takes the A30 treatment instead.
Rectangle {
anchors.fill: parent
// Top-rounded: full-bleed at y=0 of the content pane, whose own
// rounding cannot reach a child (Qt's clip is a rectangular
// scissor). See the longer note in HomeView.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: root.ambientOn ? theme.surfaceMainA30 : theme.surfaceMain
}
+12
View File
@@ -407,6 +407,18 @@ Rectangle {
// lost its backing exactly when it needed one.
Rectangle {
anchors.fill: parent
// Rounded at the TOP because this bar is full-bleed at y=0 of
// the content pane, and under the dynamic background the pane's
// own rounding cannot reach it: Qt's `clip` is a rectangular
// scissor that ignores `radius`, and AppShell hides its bezel
// nubs while the field is meant to show through the corners.
// So a full-bleed pane child rounds ITSELF — AppShell.qml says
// exactly this ("there is no mask that can do it for it here")
// and this bar was the counterexample the owner spotted in
// Discover. Invisible with the background off: the view root
// paints the same colour underneath.
topLeftRadius: theme.radiusMd
topRightRadius: theme.radiusMd
color: root.ambientOn ? theme.surfaceMainA30 : theme.surfaceMain
}