import QtQml import QtQuick import Quickshell import QtQuick.Layouts import QtQuick.Controls import Quickshell.Widgets import Quickshell.Services.Mpris PopupWindow { property PlayerControllerV2 player: PlayerControllerV2; readonly property string elementBackgroundColor : "#323232" readonly property string elementButtonBaseColor : "#515151" readonly property string elementButtonHoverColor : "#737373" id: audioPopup anchor { item: root } anchor.rect.x: root.width / 2 - width / 2 anchor.rect.y: root.height * 1.2 height: columnContainer.height + 5 width: 425 Rectangle { anchors.fill: parent color: "#919191" ColumnLayout { id: columnContainer anchors.centerIn: parent Repeater { model: player.mpris.players Item { id: root width: 375 height: 110 required property MprisPlayer modelData property string trackPosition: PlayerControllerV2.trackPositionFormatter(root.modelData.position) property string trackLength: "0:00" Rectangle { id: background anchors.fill: parent color: audioPopup.elementBackgroundColor radius: 7 } MouseArea { anchors.fill: parent onClicked: event => { audioPopup.player.setActivePlayer(root.modelData); audioPopup.player.update(); } } ColumnLayout { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter Layout.alignment: Qt.AlignHCenter width:root.width - 15 RowLayout { implicitWidth: root.width - 15 ClippingWrapperRectangle { implicitWidth: 75 implicitHeight: 75 radius: 7 Image { anchors.fill: parent mipmap: true source: root.modelData.trackArtUrl } } ColumnLayout { spacing: 2 ScrollView { implicitWidth: 180 height: parent.height Text { text: root.modelData.trackTitle color: "white" font.pointSize: 11 antialiasing: true } } Text { text: root.modelData.trackArtist color: "white" font.pointSize: 8 antialiasing: true } } Rectangle { // Spacer color: "transparent" Layout.fillWidth: true } RowLayout { Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter // Layout. spacing: 0 // buttons Item { implicitWidth: 30 implicitHeight: 25 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Item { Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Rectangle { id: gobackButton width: 30 height: 25 topLeftRadius: 7 bottomLeftRadius: 7 color: audioPopup.elementButtonBaseColor } IconImage { Layout.alignment: Qt.AlignCenter implicitSize: 25 source: "root:/assets/media_player/skip_previous.svg" } } MouseArea { anchors.fill: parent hoverEnabled: true onEntered: event => { gobackButton.color = audioPopup.elementButtonHoverColor } onExited: event => { gobackButton.color = audioPopup.elementButtonBaseColor } onClicked: event => { root.modelData.previous(); } } } Item { width: 30 height: 25 Item { Layout.alignment: Qt.AlignCenter width: 30 height: 25 Rectangle { id: pauseButton width: 30 height: 25 // topLeftRadius: 7 // bottomLeftRadius: 7 color: audioPopup.elementButtonBaseColor } IconImage { Layout.alignment: Qt.AlignCenter anchors.centerIn: parent implicitSize: 25 source: root.modelData.isPlaying ? "root:/assets/media_player/pausebutton.svg" : "root:/assets/media_player/playbutton.svg" } } MouseArea { // id: pauseButton anchors.fill: parent hoverEnabled: true onEntered: event => { pauseButton.color = audioPopup.elementButtonHoverColor } onExited: event => { pauseButton.color = audioPopup.elementButtonBaseColor } onClicked: event => { root.modelData.togglePlaying(); } } } Item { width: 30 height: 25 Item { Layout.alignment: Qt.AlignCenter Rectangle { id: gonextButton width: 30 height: 25 topRightRadius: 7 bottomRightRadius: 7 color: audioPopup.elementButtonBaseColor } IconImage { Layout.alignment: Qt.AlignCenter implicitSize: 25 source: "root:/assets/media_player/skip_next.svg" } } MouseArea { anchors.fill: parent hoverEnabled: true onEntered: event => { gonextButton.color = audioPopup.elementButtonHoverColor } onExited: event => { gonextButton.color = audioPopup.elementButtonBaseColor } onClicked: event => { root.modelData.next(); } } } } } RowLayout { Slider { id: slider Layout.fillWidth: true from: 0 value: root.modelData.position to: root.modelData.length property double lastPosition; property bool debounceValue: false; onMoved: event => { debounceValue = true; // there is insane lag if we put the move logic here, so its better to trigger a time with the logic instead } Timer { running: slider.debounceValue interval: 250 repeat: true onTriggered: { root.modelData.position = slider.value audioPopup.player.update(); // force values to update // causing ui to update with it slider.debounceValue = false; } } } Text { id: positionText text: trackPosition + "/" + trackLength // returns float of seconds of length color: "white" font.pointSize: 10 } } } Timer { id: updateTimer property bool tempOneshot: false; running: root.modelData.isPlaying || (root.modelData.position != slider.lastPosition) interval: 750 repeat: true onTriggered: { trackPosition = PlayerControllerV2.trackPositionFormatter(root.modelData.position) trackLength = PlayerControllerV2.trackPositionFormatter(root.modelData.length) updateTimer.tempOneshot = false; slider.lastPosition = slider.value slider.value = root.modelData.position } } } } } } }