pragma ComponentBehavior: Bound import Quickshell.Services.Mpris import Quickshell.Widgets import QtQuick import Qt5Compat.GraphicalEffects import QtQuick.Layouts import QtQuick.Controls import qs import qs.settings import qs.widgets Rectangle { id: root color: Colors.base00 radius: Settings.config.rounding implicitWidth: 600 implicitHeight: 200 visible: getSpotify() != null layer { enabled: true effect: DropShadow { color: "#111111" horizontalOffset: 7 verticalOffset: 8 radius: 12 samples: 14 } } MouseArea { id: hoverDetect hoverEnabled: true anchors.fill: parent onExited: title.x = 0 } function getSpotify() { for (var i = 0; i < Mpris.players.values.length; i++) { if (Mpris.players.values[i].identity == "Spotify" || Mpris.players.values[i] == "spotify") { return Mpris.players.values[i]; } else { return null; } } return null; } property var spotify: getSpotify() != null ? getSpotify() : null property var title: getSpotify() != null ? getSpotify().trackTitle : "" property var album: getSpotify() != null ? getSpotify().trackAlbum : "" property var art: getSpotify() != null ? getSpotify().trackArtUrl : "" property var artist: getSpotify() != null ? getSpotify().trackArtist : "" ClippingWrapperRectangle { id: songWrapper radius: Settings.config.rounding / 1.5 anchors.margins: 8 margin: 0 anchors.fill: parent color: Colors.base00 RowLayout { id: songLayout spacing: 10 ClippingWrapperRectangle { id: coverRounder Layout.fillWidth: true Layout.fillHeight: true Layout.maximumWidth: songCover.sourceSize.width radius: Settings.config.rounding / 1.5 Image { id: songCover source: root.art sourceSize { width: 180 height: 180 } } } WrapperRectangle { color: Colors.base01 Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignTop radius: Settings.config.rounding / 1.5 margin: 20 child: ColumnLayout { id: songInfo Layout.alignment: Qt.AlignTop Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: 20 Layout.topMargin: 2 Item { id: titleContainer Layout.fillWidth: true x: 0 implicitHeight: title.implicitHeight clip: true // Keeps the text inside this "window" CText { id: title Layout.maximumWidth: 300 text: root.title font.pixelSize: 30 SequentialAnimation on x { id: scrollAnimation running: hoverDetect.containsMouse && title.width > titleContainer.width loops: Animation.Infinite // Scroll to the end NumberAnimation { to: titleContainer.width - title.width duration: Math.max(2000, (title.width - titleContainer.width) * 30) easing.type: Easing.InOutQuad } // Scroll back to the start NumberAnimation { to: 0 duration: Math.max(2000, (title.width - titleContainer.width) * 30) easing.type: Easing.InOutQuad } } } } CText { id: album text: root.album + " - " + root.artist opacity: 0.6 Layout.maximumWidth: 250 Layout.alignment: Qt.AlignTop elide: Text.ElideRight } ProgressBar { id: songProgress FrameAnimation { // only emit the signal when the position is actually changing. running: root.spotify ? root.spotify.playbackState == MprisPlaybackState.Playing || root.visible : false // emit the positionChanged signal every frame. onTriggered: root.spotify.positionChanged() } implicitWidth: 200 implicitHeight: 10 from: 0 to: root.spotify != null ? root.spotify.length : 0 value: root.spotify != null ? root.spotify.position : 0 background: Rectangle { implicitWidth: 200 implicitHeight: 6 color: Colors.base02 radius: Settings.config.rounding } contentItem: Item { implicitWidth: 200 implicitHeight: 4 // Progress indicator for determinate state. Rectangle { width: songProgress.visualPosition * parent.width height: parent.height radius: Settings.config.rounding color: Colors.base07 visible: !songProgress.indeterminate } } } RowLayout { id: playerControls Layout.maximumWidth: 200 CIcon { id: previous text: "\ue045" Layout.alignment: Qt.AlignLeft MouseArea { id: prevHandler anchors.fill: parent acceptedButtons: Qt.LeftButton cursorShape: Qt.PointingHandCursor onClicked: { root.spotify.previous(); title.x = 0; } } } CIcon { id: pause text: root.spotify ? root.spotify.isPlaying ? "\ue034" : "\ue037" : "" Layout.alignment: Qt.AlignHCenter MouseArea { id: pauseHandler anchors.fill: parent acceptedButtons: Qt.LeftButton cursorShape: Qt.PointingHandCursor onClicked: { root.spotify.togglePlaying(); title.x = 0; } } } CIcon { id: next Layout.alignment: Qt.AlignRight text: "\ue044" MouseArea { id: nextHandler anchors.fill: parent acceptedButtons: Qt.LeftButton cursorShape: Qt.PointingHandCursor onClicked: { root.spotify.next(); title.x = 0; } } } } } } } } }