feat(qbz-slint): port the local bit-perfect volume lock + force-100 (QConnect parity)

Port the rest of Tauri's volume logic to the Slint bar.

PlayerBar gains a derived vol-locked reproducing Tauri's handleVolumeChange
formula exactly: (alsaDirectHw && !isRemote) || volumeLocked. The local
ALSA-Direct (bit-perfect) lock is lifted while controlling a peer (the
!is-remote term) so the user can adjust the remote renderer; the sink-pushed
NowPlayingState.volume-locked keeps meaning only the peer-disallow half. All
five volume-interaction gates and the slider opacity now read vol-locked.

Force the local player to unity gain (set_volume(1.0)) in bit-perfect, mirroring
Tauri's playerSetVolume(100): when the backend is ALSA + the hw plugin AND a
peer is not the active renderer, set core volume to 1.0 and push
NowPlayingState.volume = 1.0. Applied (a) at the startup audio-settings load and
(b) on backend / alsa-plugin change. Gated on a new is_peer_active() so the
force-100 is skipped while controlling a peer. core().set_volume is the safe
seam; the protected device-init is untouched.

cc
This commit is contained in:
vicrodh committed 2026-06-03 10:47:38 -06:00
1 parent a1b054c387
commit 9bb5e6db26
4 files changed
+95 -7

No files matched your search

+13 -5
View File
@@ -138,6 +138,14 @@ export component PlayerBar inherits Rectangle {
property <float> col-side: ShellState.npb-mode == 1 ? 0.324 : root.side-frac;
property <float> col-centre: 1.0 - 2.0 * root.col-side;
// Volume lock, mirroring Tauri's handleVolumeChange formula:
// (isAlsaDirectHw && !peerActive) || (peerActive && !remoteVolumeAllowed)
// The local ALSA-Direct lock (bit-perfect) is LIFTED when controlling a peer
// (the `!is-remote` term) so the user CAN adjust the remote renderer; the
// peer half is the sink-pushed NowPlayingState.volume-locked.
property <bool> vol-locked: (SettingsState.alsa-plugin-is-hw && !NowPlayingState.is-remote)
|| NowPlayingState.volume-locked;
VerticalLayout {
Rectangle { height: 6px; }
@@ -746,7 +754,7 @@ export component PlayerBar inherits Rectangle {
tooltip: NowPlayingState.muted ? @tr("Unmute") : @tr("Mute");
active: NowPlayingState.muted;
clicked => {
if (!NowPlayingState.volume-locked) {
if (!root.vol-locked) {
NowPlayingState.toggle-mute();
}
}
@@ -754,14 +762,14 @@ export component PlayerBar inherits Rectangle {
}
VerticalLayout {
alignment: center;
opacity: NowPlayingState.volume-locked ? 0.4 : 1.0;
opacity: root.vol-locked ? 0.4 : 1.0;
QbzSlider {
width: 90px;
minimum: 0;
maximum: 100;
value: NowPlayingState.volume * 100;
changed(v) => {
if (!NowPlayingState.volume-locked) {
if (!root.vol-locked) {
NowPlayingState.set-volume(v / 100.0);
}
}
@@ -775,7 +783,7 @@ export component PlayerBar inherits Rectangle {
btn-width: 28px;
tooltip: @tr("Volume down");
clicked => {
if (!NowPlayingState.volume-locked) {
if (!root.vol-locked) {
NowPlayingState.set-volume(Math.max(0.0, NowPlayingState.volume - 0.05));
}
}
@@ -789,7 +797,7 @@ export component PlayerBar inherits Rectangle {
btn-width: 28px;
tooltip: @tr("Volume up");
clicked => {
if (!NowPlayingState.volume-locked) {
if (!root.vol-locked) {
NowPlayingState.set-volume(Math.min(1.0, NowPlayingState.volume + 0.05));
}
}