adds overview, removes old stuff
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
// MediaCavaWidget.qml - cava visualiser + media info + volume in one pill
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
import ".."
|
||||
|
||||
Popup {
|
||||
id: root
|
||||
popupName: "media"
|
||||
|
||||
property MprisPlayer player: {
|
||||
var players = Mpris.players.values
|
||||
for (var i = 0; i < players.length; i++)
|
||||
if (players[i].identity.toLowerCase() === "spotify") return players[i]
|
||||
return players.length > 0 ? players[0] : null
|
||||
}
|
||||
|
||||
property bool isPlaying: player?.playbackState === MprisPlaybackState.Playing ?? false
|
||||
property real displayPosition: 0
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: root.visible && root.player !== null
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.displayPosition = root.player?.position ?? 0
|
||||
}
|
||||
|
||||
function formatTime(secs) {
|
||||
secs = Math.max(0, Math.floor(secs))
|
||||
return Math.floor(secs / 60) + ":" + String(secs % 60).padStart(2, "0")
|
||||
}
|
||||
|
||||
component CtrlBtn: Text {
|
||||
property string icon: ""
|
||||
property color baseColor: Theme.pillText
|
||||
property color hoverColor: Theme.pillHover
|
||||
signal activate()
|
||||
text: icon
|
||||
font { family: Theme.fontMono; pixelSize: 20 }
|
||||
color: ma.containsMouse ? hoverColor : baseColor
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
MouseArea {
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: parent.activate()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: 200
|
||||
spacing: 8
|
||||
|
||||
// Album art - hidden when no art is available
|
||||
Rectangle {
|
||||
visible: artImage.status === Image.Ready
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: 180
|
||||
Layout.preferredHeight: artImage.status === Image.Ready ? 180 : 0
|
||||
Layout.topMargin: artImage.status === Image.Ready ? 10 : 0
|
||||
radius: Theme.radius
|
||||
clip: true
|
||||
color: Theme.pill
|
||||
|
||||
Image {
|
||||
id: artImage
|
||||
anchors.fill: parent
|
||||
source: root.player?.trackArtUrl ?? ""
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: true
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.player)
|
||||
Exec.run(["hyprctl", "dispatch", "hl.dsp.focus({ window = 'class:" + root.player.identity + "' })"])
|
||||
GlobalStates.popups.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track title with marquee scroll
|
||||
Item {
|
||||
id: titleClip
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
Layout.topMargin: 4
|
||||
implicitHeight: titleText.implicitHeight
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
id: titleText
|
||||
text: root.player?.trackTitle ?? "Nothing playing"
|
||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize + 1; bold: true }
|
||||
color: titleMa.containsMouse && root.player ? Qt.lighter(Theme.pillText, 1.3) : Theme.pillText
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
MouseArea {
|
||||
id: titleMa
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: root.player ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: if (root.player) {
|
||||
Exec.run(["hyprctl", "dispatch", "hl.dsp.focus({ window = 'class:" + root.player.identity + "' })"])
|
||||
GlobalStates.popups.close()
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
marquee.stop()
|
||||
x = 0
|
||||
Qt.callLater(() => {
|
||||
if (implicitWidth > titleClip.width) marquee.start()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: Qt.callLater(() => {
|
||||
if (titleText.implicitWidth > width) marquee.start()
|
||||
})
|
||||
|
||||
SequentialAnimation {
|
||||
id: marquee
|
||||
loops: Animation.Infinite
|
||||
|
||||
PauseAnimation { duration: 1500 }
|
||||
NumberAnimation {
|
||||
target: titleText; property: "x"
|
||||
from: 0
|
||||
to: -(titleText.implicitWidth - titleClip.width + 10)
|
||||
duration: Math.max(1, titleText.implicitWidth - titleClip.width) * 18
|
||||
easing.type: Easing.Linear
|
||||
}
|
||||
PauseAnimation { duration: 800 }
|
||||
NumberAnimation {
|
||||
target: titleText; property: "x"
|
||||
to: 0; duration: 400; easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Artist
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.player?.trackArtists ? Array.from(root.player.trackArtists).join(", ") : ""
|
||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||
color: Theme.pillText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// Progress bar
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
height: 3
|
||||
radius: 2
|
||||
color: Theme.pill
|
||||
|
||||
Rectangle {
|
||||
width: root.player && root.player.length > 0
|
||||
? Math.min(1, root.displayPosition / root.player.length) * parent.width
|
||||
: 0
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
color: Theme.color6
|
||||
}
|
||||
}
|
||||
|
||||
// Time labels
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
Text {
|
||||
text: root.formatTime(root.displayPosition)
|
||||
font { family: Theme.fontMono; pixelSize: Theme.fontSize - 1 }
|
||||
color: Theme.pillText
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Text {
|
||||
text: root.formatTime(root.player?.length ?? 0)
|
||||
font { family: Theme.fontMono; pixelSize: Theme.fontSize - 1 }
|
||||
color: Theme.pillText
|
||||
}
|
||||
}
|
||||
|
||||
// Controls
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: 10
|
||||
spacing: 20
|
||||
CtrlBtn { text: ""; onActivate: root.player?.previous() }
|
||||
CtrlBtn { text: root.isPlaying ? "" : ""; onActivate: root.player?.togglePlaying() }
|
||||
CtrlBtn { text: ""; onActivate: root.player?.stop() }
|
||||
CtrlBtn { text: ""; onActivate: root.player?.next() }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user