feat(qt): HiFi Wizard — guided DAC setup ported from Slint

Six-step modal (Welcome / Check / DACs / Configure / Test / Done) opened
from Settings > Audio. It probes the audio stack and renders per-distro
remediations, enumerates DACs with a manual node.name escape hatch,
generates the PipeWire / WirePlumber snippets to copy, and plays four
tracks spanning 16/44.1 to 24/192 with a live requested-vs-negotiated
read-back. Read-only: it never writes a system file or runs a command.

The logic was unreachable from Qt — every public function in
qbz-dac-wizard takes a &AppWindow and writes straight into Slint globals.
It is lifted into a new frontend-agnostic crate, qbz-dac-wizard-core,
which returns plain data. qbz-dac-wizard itself is left untouched.

Rust owns everything it computes plus `open`, so the reset and the probe
are one act; QML owns the step navigation and the four progress
checkboxes, which is how the reference splits them too.

Two deliberate divergences from the reference: the row sits at the end of
OUTPUT rather than BIT-PERFECT, and it is offered on every backend
instead of PipeWire only — someone on ALSA direct is exactly who needs
the walkthrough.

The green glyphs use the accent tint, not success: success has no qrc bake
directory, so a degraded tint set would render nothing at all. Every
success-toned text keeps theme.success (#3fae6a verbatim).

Verified by 309 tests, both QML audits and the offscreen smoke, plus a
forced-route probe that mounts Settings and walks the wizard's steps —
the standard smoke only mounts Home, so it is no evidence for a modal.
The modal was also driven and screenshotted over VNC.

cc
This commit is contained in:
vicrodh committed 2026-08-11 11:26:50 -06:00
1 parent 79e3cdc46b
commit 8ba9483462
28 files changed
+3983 -3

No files matched your search

+28 -2
View File
@@ -9,8 +9,12 @@
// - "Detected device limit" + its fallback disclosure (#638): the device-cap
// probe (`device_cap.rs`) is Slint glue that was never ported, so there is
// no measured value to show.
// - "HiFi Wizard": the guided PipeWire setup (DacWizardActions) is a whole
// modal flow, not a settings row.
//
// The HiFi Wizard IS shipped (2026-08-11): the last row of OUTPUT opens
// settings/DacWizardModal.qml, mounted at the SettingsView root. Its logic
// lives in `qbz-dac-wizard-core`, the frontend-agnostic crate extracted for
// this port. Placement and backend gating diverge from the reference — see
// the row itself.
import QtQuick
import com.blitzfc.qbz
@@ -125,6 +129,28 @@ Column {
onSelected: function (i) { QbzBridge.settingsSelect("dsd-mode", i) }
}
}
// HiFi Wizard — guided bit-perfect DAC setup. The LAST row of OUTPUT,
// under "Output device", and shown for EVERY backend.
//
// TWO DIVERGENCES FROM THE REFERENCE, both owner rulings (2026-08-11):
// it lives in OUTPUT, not at the end of BIT-PERFECT; and it is NOT gated
// on `backend-is-pipewire` the way `AudioSettings.slint:282` gates it.
// The reference hides it off PipeWire because the config it generates is
// PipeWire/WirePlumber — but that is exactly who needs it: someone on ALSA
// direct who wants to set their DAC up cannot reach the wizard that would
// walk them through it. Do not "restore parity" by putting the gate back.
//
// Opening it resets the wizard and kicks the audio-stack probe off the UI
// thread, so there is nothing to arm here.
SettingRow {
label: QbzSession.tr("HiFi Wizard", QbzSession.trRev)
description: QbzSession.tr("Auto-detect your DACs and set up bit-perfect playback, step by step.", QbzSession.trRev)
SettingsButton {
iconName: "gandalf"
text: QbzSession.tr("Open Wizard", QbzSession.trRev)
onClicked: QbzDacWizard.open()
}
}
SettingsSpacer { }
SettingsDivider { }