import QtQuick import QtQuick.Shapes import Quickshell import Quickshell.Widgets import Quickshell.Services.Mpris import Quickshell.Services.Notifications import "../Widgets" as Widgets Item { anchors { top: parent.top horizontalCenter: parent.horizontalCenter } width: childrenRect.width + 12 height: childrenRect.height + 12 Row { spacing: 6 x: 6 y: 6 Rectangle { width: 400 height: 300 color: "#181818" radius: 10 Column { anchors.centerIn: parent spacing: 2 opacity: !notifServer.trackedNotifications.values.length Behavior on opacity {NumberAnimation {duration: 150}} Image { anchors.horizontalCenter: parent.horizontalCenter source: Quickshell.shellPath("assets/notif.svg") width: 24 height: 24 sourceSize {width: width; height: height} opacity: 0.5 } Text { anchors.horizontalCenter: parent.horizontalCenter color: "#888" text: "No notifications" } } ListView { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: anchors.leftMargin clip: true spacing: 4 interactive: background.index === 3 add: Transition {NumberAnimation { property: "opacity" from: 0 to: 1 duration: 150 }} remove: Transition {NumberAnimation { property: "opacity" from: 1 to: 0 duration: 150 }} displaced: Transition {NumberAnimation { property: "y" duration: 300 }} header: Item {height: 4; width: 1} model: Array.from(notifServer.trackedNotifications.values) delegate: Widgets.Notification { color: "#222" } } } Item { id: player property var modelData: Mpris.players.values[0] height: 300 width: 250 Column { anchors.centerIn: parent spacing: 2 opacity: !Mpris.players.values.length Behavior on opacity {NumberAnimation {duration: 150}} Image { anchors.horizontalCenter: parent.horizontalCenter source: Quickshell.shellPath("assets/player-symbolic.svg") width: 24 height: 24 sourceSize {width: width; height: height} opacity: 0.5 } Text { anchors.horizontalCenter: parent.horizontalCenter color: "#888" text: "Nothing playing" } } Column { anchors.centerIn: parent opacity: Mpris.players.values.length Behavior on opacity {NumberAnimation {duration: 150}} Shape { anchors.horizontalCenter: parent.horizontalCenter width: 180 height: 180 preferredRendererType: Shape.CurveRenderer ShapePath { capStyle: ShapePath.RoundCap strokeColor: "#444" fillColor: "transparent" strokeWidth: 4 PathAngleArc { moveToStart: true radiusX: 90 - 6 radiusY: 90 - 6 centerX: 90 centerY: 90 startAngle: 90 + 20 sweepAngle: 360 - 40 } } ShapePath { capStyle: ShapePath.RoundCap strokeColor: "white" fillColor: "transparent" strokeWidth: 4 PathAngleArc { moveToStart: true radiusX: 90 - 6 radiusY: 90 - 6 centerX: 90 centerY: 90 startAngle: 90 + 20 sweepAngle: (player.modelData?.position/player.modelData.length) * (360 - 40) } } ClippingRectangle { color: "transparent" radius: height/2 anchors.fill: parent anchors.margins: 12 Image { anchors.fill: parent source: player.modelData?.trackArtUrl ?? source } NumberAnimation on rotation { id: spin paused: !player.modelData?.isPlaying || !root.up || background.index !== 3 duration: 60000 loops: Animation.Infinite from: 0 to: 360 } layer.enabled: true layer.smooth: true } } Item {width: 1; height: 10} Text { color: "white" text: player.modelData?.trackTitle ?? "" anchors.horizontalCenter: parent.horizontalCenter font.pixelSize: 16 } Text { color: "white" text: player.modelData?.trackArtist ?? "" anchors.horizontalCenter: parent.horizontalCenter font.pixelSize: 12 } Item {width: 1; height: 8} Row { height: childrenRect.height anchors.horizontalCenter: parent.horizontalCenter component Button: Rectangle { id: button property var action property var clicked radius: 12 color: hover.hovered ? "#11ffffff" : "#00ffffff" property var hover: HoverHandler {} width: 40 height: width Image { anchors.fill: parent anchors.margins: 8 sourceSize {width: width; height: height} source: Quickshell.shellPath(`assets/lazer-${action}-symbolic.svg`) } TapHandler {onTapped: button.clicked()} } Button { action: "previous" clicked: player.modelData?.previous } Button { action: player.modelData?.isPlaying ? "pause" : "play" clicked: player.modelData?.togglePlaying } Button { action: "next" clicked: player.modelData?.next } } FrameAnimation { running: background.index === 3 onTriggered: player.modelData.positionChanged() } } } } }