diff --git a/quickshell/Common/SessionData.qml b/quickshell/Common/SessionData.qml index 36390d94a..88a40ca60 100644 --- a/quickshell/Common/SessionData.qml +++ b/quickshell/Common/SessionData.qml @@ -219,6 +219,7 @@ Singleton { property string bluetoothAdapterOverride: "" property string lastPlayerIdentity: "" + property string pinnedPlayerIdentity: "" property var deviceMaxVolumes: ({}) property var hiddenOutputDeviceNames: [] diff --git a/quickshell/Common/settings/SessionSpec.js b/quickshell/Common/settings/SessionSpec.js index fab64c909..20f6fe3db 100644 --- a/quickshell/Common/settings/SessionSpec.js +++ b/quickshell/Common/settings/SessionSpec.js @@ -87,6 +87,7 @@ var SPEC = { bluetoothAdapterOverride: { def: "" }, lastPlayerIdentity: { def: "" }, + pinnedPlayerIdentity: { def: "" }, deviceMaxVolumes: { def: {} }, hiddenOutputDeviceNames: { def: [] }, diff --git a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml index c3842c7f8..d9e2568b3 100644 --- a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml +++ b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml @@ -578,7 +578,7 @@ Item { anchors.leftMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingM - width: parent.width - Theme.spacingM * 2 + width: parent.width - Theme.spacingM * 2 - pinIndicator.width - Theme.spacingS DankIcon { name: "music_note" @@ -618,12 +618,36 @@ Item { } } + Rectangle { + id: pinIndicator + readonly property bool pinned: !!modelData?.identity && MprisController.pinnedIdentity === modelData.identity + anchors.right: parent.right + anchors.rightMargin: Theme.spacingM + anchors.verticalCenter: parent.verticalCenter + width: 12 + height: 12 + radius: 6 + color: pinned ? Theme.primary : "transparent" + border.color: pinned ? Theme.primary : Theme.outlineHeavy + border.width: 2 + } + MouseArea { id: playerMouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: { + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: mouse => { + if (mouse.button === Qt.RightButton) { + if (modelData?.identity) { + if (MprisController.pinnedIdentity === modelData.identity) + MprisController.setPinned(""); + else + MprisController.setPinned(modelData.identity); + } + return; + } if (modelData?.identity) { root.playerSelected(modelData); } diff --git a/quickshell/Services/MprisController.qml b/quickshell/Services/MprisController.qml index 4518b16a3..dae45870e 100644 --- a/quickshell/Services/MprisController.qml +++ b/quickshell/Services/MprisController.qml @@ -43,6 +43,7 @@ Singleton { }); } property MprisPlayer activePlayer: null + property string pinnedIdentity: SessionData.pinnedPlayerIdentity || "" property real activePlayerStableLength: 0 // Chromium can report blank metadata between tracks property string stableTitle: "" @@ -247,6 +248,15 @@ Singleton { if (playing.length === 0) return null; + if (pinnedIdentity !== "") { + const pinLower = pinnedIdentity.toLowerCase(); + const exact = playing.filter(player => player.identity === pinnedIdentity); + const matches = exact.length > 0 ? exact : playing.filter(player => (player.identity || "").toLowerCase().includes(pinLower)); + const pinned = matches.find(player => player.canControl) ?? matches[0]; + if (pinned) + return pinned; + } + const controllable = playing.filter(player => player.canControl); if (activePlayer?.isPlaying) { if (activePlayer.canControl || controllable.length === 0) @@ -301,6 +311,12 @@ Singleton { _persistIdentity(player.identity); } + function setPinned(identity: string): void { + if (SessionData.pinnedPlayerIdentity !== identity) + SessionData.set("pinnedPlayerIdentity", identity); + _resolveActivePlayer(); + } + function _persistIdentity(identity: string): void { if (identity && SessionData.lastPlayerIdentity !== identity) SessionData.set("lastPlayerIdentity", identity);