diff --git a/dot_config/quickshell/bar/MediaPill.qml b/dot_config/quickshell/bar/MediaPill.qml index 20a0265..bf44192 100644 --- a/dot_config/quickshell/bar/MediaPill.qml +++ b/dot_config/quickshell/bar/MediaPill.qml @@ -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" diff --git a/dot_config/quickshell/bar/popouts/MediaPopout.qml b/dot_config/quickshell/bar/popouts/MediaPopout.qml index 0c572f4..c942e7e 100644 --- a/dot_config/quickshell/bar/popouts/MediaPopout.qml +++ b/dot_config/quickshell/bar/popouts/MediaPopout.qml @@ -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 ?? "" diff --git a/dot_config/quickshell/shared/Media.qml b/dot_config/quickshell/shared/Media.qml new file mode 100644 index 0000000..7c04164 --- /dev/null +++ b/dot_config/quickshell/shared/Media.qml @@ -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() +}