quickshell: unify media player selection in a Media singleton

The pill and popout each picked an active MPRIS player independently, so
with multiple players open they disagreed and flipped. Add a Media
singleton that selects one active player with stickiness and bind both
components to it.
This commit is contained in:
2026-06-01 14:05:35 +02:00
parent 119789cc53
commit a74fef4bc5
3 changed files with 55 additions and 16 deletions
+1 -8
View File
@@ -1,5 +1,4 @@
import Quickshell
import Quickshell.Services.Mpris
import QtQuick
import QtQuick.Layouts
import "../shared" as Shared
@@ -7,13 +6,7 @@ import "../shared" as Shared
BarPill {
id: root
readonly property var player: {
let players = Mpris.players.values;
for (let i = 0; i < players.length; i++) {
if (players[i].isPlaying) return players[i];
}
return players.length > 0 ? players[0] : null;
}
readonly property var player: Shared.Media.activePlayer
visible: player !== null
groupName: "media"
@@ -1,5 +1,4 @@
import Quickshell
import Quickshell.Services.Mpris
import QtQuick
import QtQuick.Layouts
import "../../shared" as Shared
@@ -13,13 +12,7 @@ Item {
PopoutBackground { anchors.fill: parent }
MouseArea { anchors.fill: parent }
readonly property var player: {
let players = Mpris.players.values;
for (let i = 0; i < players.length; i++) {
if (players[i].isPlaying) return players[i];
}
return players.length > 0 ? players[0] : null;
}
readonly property var player: Shared.Media.activePlayer
readonly property bool isPlaying: player?.isPlaying ?? false
readonly property string trackTitle: player?.trackTitle ?? ""
+53
View File
@@ -0,0 +1,53 @@
pragma Singleton
import Quickshell
import Quickshell.Services.Mpris
import QtQuick
// Single source of truth for "which MPRIS player is active", so the bar pill and
// the popout never disagree when several players are open.
Singleton {
id: root
property var activePlayer: null
readonly property bool hasPlayer: activePlayer !== null
readonly property bool isPlaying: activePlayer?.isPlaying ?? false
// Choose the active player with stickiness: keep the current one while it
// still exists and is playing; otherwise prefer a playing player (scanning
// newest-first), else keep a valid current, else fall back to the first.
function reevaluate() {
let players = Mpris.players.values;
if (players.length === 0) { root.activePlayer = null; return; }
let cur = root.activePlayer;
let curValid = cur && players.indexOf(cur) >= 0;
if (curValid && cur.isPlaying) return;
for (let i = players.length - 1; i >= 0; i--) {
if (players[i].isPlaying) { root.activePlayer = players[i]; return; }
}
if (curValid) return;
root.activePlayer = players[0];
}
// Re-evaluate when the set of players changes…
Connections {
target: Mpris.players
function onValuesChanged() { root.reevaluate(); }
}
// …and when any individual player's playback state changes.
Instantiator {
model: Mpris.players
delegate: Connections {
required property var modelData
target: modelData
function onPlaybackStateChanged() { root.reevaluate(); }
}
}
Component.onCompleted: reevaluate()
}