adds overview, removes old stuff

This commit is contained in:
2026-08-19 20:47:29 +02:00
parent 6a9ae98c26
commit 67d50fb504
75 changed files with 3558 additions and 1691 deletions
@@ -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() }
}
}
}