Files
qbz/crates/qbz-qt/qml/shell/AudioSettingsMenu.qml
T
vicrodh 01f3819da1 fix(qt): the now-playing audio button opens the Normalization + Gapless flyout, and reads the real state
It was not a flyout and it did not read the persisted value, which together
made normalization impossible to turn off.

- `shell/AudioSettingsMenu.qml` (new) ports the `audio-menu` PopupWindow both
  bars mount (PlayerBar.slint:686-705, PlayerBarSmall.slint:635-712): 252 wide,
  two 44px ToggleRows with 12px side padding and a 12px gap to the switch,
  label text-primary 13px over hint text-muted 10px at spacing 1 — the numbers
  are PlayerBar.slint's file-private ToggleRow (:80-115).
- `PlayerBar.qml` had `normTouched` / `normLocal` and toggled normalization on
  click. `NowPlayingBarSmall.qml` was worse: `property bool normOn: false`,
  local state that started at false no matter what was persisted, so the FIRST
  click on that button always sent `normalization = true`. Both now just open
  the flyout. `active` mirrors normalization on the full bar and is neutral on
  the Small one, which is the reference's own asymmetry
  (PlayerBarSmall.slint:628-630 states it out loud).
- Gapless had no now-playing control at all.

The local shadow state existed because the document never carried the truth:
`publish_settings()` ran ONLY on `navigate_to("settings")` and on a language
change, and no handler in `settings_qt` ever republished. So until the user
opened Settings the shell read an empty document, the control drew OFF while
the backend had it ON, and the first interaction wrote the wrong value back.
Slint has no equivalent hole — `SettingsState` is a global seeded at startup.
So: seed the document at shell entry, and republish after every audio apply.
Verified by effect, not by diff — the boot log now reports the snapshot as
5006 bytes carrying the persisted `normalization` and `gapless`, before any
view is opened.

Seeding at shell entry also seeds the now-playing stamp's two output LEDs,
which `publish_snapshot` derives from the same audio settings.

cc
2026-07-30 00:43:04 -06:00

105 lines
4.1 KiB
QML

// Audio settings flyout — the port of the "Audio settings" PopupWindow that
// BOTH now-playing bars mount (`shell/PlayerBar.slint:686-705` and
// `shell/PlayerBarSmall.slint:635-712`). Two rows, Normalization and Gapless,
// and nothing else: the Slint popup is 252x118 with exactly these two
// ToggleRows (`PlayerBar.slint:80-115` is the file-private ToggleRow this
// reproduces — 44px tall, 12px side padding, 12px gap to the switch, label
// text-primary 13px over hint text-muted 10px, spacing 1).
//
// It replaces a direct-toggle button that both bars had shipped with LOCAL
// state (`normLocal` / `property bool normOn: false`), which is what made
// normalization impossible to turn off: the bars never learned the persisted
// value, so the button drew OFF on a fresh launch and the first click SET IT
// ON. The state now comes from the published document, which is seeded at
// shell entry and republished after every apply.
//
// Mounted as a QbzContextMenu (the port's popup primitive, same as
// ViewModeMenu next to it) rather than a raw Popup, so the panel chrome,
// placement clamping and close policy are the ones every other flyout in the
// bar already uses.
import QtQuick
import QtQuick.Controls
import com.blitzfc.qbz
import "../controls"
import "../theme"
QbzContextMenu {
id: root
// PlayerBar.slint:689 — `width: 252px`.
menuWidth: 252
/// The live document both rows read. The host passes its already-parsed
/// `settingsDoc` so this file never parses the JSON a second time.
property var doc: ({})
QbzTheme { id: theme }
Repeater {
model: [
{
"key": "normalization",
"label": QbzSession.tr("Normalization", QbzSession.trRev),
"hint": QbzSession.tr("Level loudness across tracks", QbzSession.trRev),
"on": root.doc.normalization === true,
},
{
"key": "gapless",
"label": QbzSession.tr("Gapless", QbzSession.trRev),
"hint": QbzSession.tr("Seamless track transitions", QbzSession.trRev),
"on": root.doc.gapless === true,
},
]
delegate: Item {
required property var modelData
width: parent ? parent.width : 0
// ToggleRow.slint:86 — `height: 44px`.
height: 44
Row {
anchors.fill: parent
// ToggleRow.slint:88-90 — padding 12/12, spacing 12.
anchors.leftMargin: 12
anchors.rightMargin: 12
spacing: 12
Column {
width: parent.width - 12 - rowToggle.width
height: parent.height
// The label/hint pair is vertically centred as a block
// (ToggleRow's inner VerticalLayout `alignment: center`),
// so the Column is centred rather than filling.
anchors.verticalCenter: parent.verticalCenter
// ToggleRow.slint:93 — `spacing: 1px`.
spacing: 1
Text {
width: parent.width
text: modelData.label
color: theme.textPrimary
// ToggleRow.slint:98 — literal 13px, not a token.
font.pixelSize: 13
elide: Text.ElideRight
}
Text {
width: parent.width
text: modelData.hint
color: theme.textMuted
// ToggleRow.slint:103 — literal 10px.
font.pixelSize: 10
elide: Text.ElideRight
}
}
QbzToggle {
id: rowToggle
anchors.verticalCenter: parent.verticalCenter
checked: modelData.on
onToggled: function (v) {
QbzBridge.settingsBool(modelData.key, v)
}
}
}
}
}
}