186 lines
5.3 KiB
QML
186 lines
5.3 KiB
QML
import QtQuick
|
|
import QtQuick.Shapes
|
|
import Quickshell
|
|
import "root:/config"
|
|
import "root:/components"
|
|
import "root:/services"
|
|
import "root:/popouts"
|
|
|
|
// Now-playing module. Collapses to nothing when no player is around.
|
|
BarPill {
|
|
id: root
|
|
|
|
required property var screenRef
|
|
|
|
accent: Theme.primary
|
|
padding: Theme.padS
|
|
visible: Media.hasPlayer || width > 1
|
|
|
|
implicitWidth: Media.hasPlayer
|
|
? layout.implicitWidth + contentPad * 2
|
|
: 0
|
|
|
|
Behavior on implicitWidth {
|
|
NumberAnimation { duration: Theme.durSlow; easing.type: Easing.OutExpo }
|
|
}
|
|
|
|
clip: true
|
|
|
|
onClicked: Media.playPause()
|
|
onRightClicked: Media.next()
|
|
onMiddleClicked: Media.raise()
|
|
onScrolled: delta => delta > 0 ? Media.next() : Media.previous()
|
|
|
|
Row {
|
|
id: layout
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
spacing: Theme.padS
|
|
|
|
// Album art, chopped into the house shape.
|
|
Item {
|
|
id: artFrame
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 24
|
|
height: 24
|
|
|
|
Shape {
|
|
id: artMask
|
|
anchors.fill: parent
|
|
visible: false
|
|
layer.enabled: true
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
fillColor: "white"
|
|
strokeColor: "transparent"
|
|
PathPolyline {
|
|
path: Geom.chamfer(Geom.parallelogram(24, 24, 0.18), 5, [1, 3])
|
|
}
|
|
}
|
|
}
|
|
|
|
Image {
|
|
id: art
|
|
anchors.fill: parent
|
|
source: Media.artUrl
|
|
fillMode: Image.PreserveAspectCrop
|
|
cache: true
|
|
asynchronous: true
|
|
visible: status === Image.Ready
|
|
layer.enabled: true
|
|
layer.effect: MaskedArt { maskItem: artMask }
|
|
}
|
|
|
|
// Fallback: the source app's glyph.
|
|
Icon {
|
|
anchors.centerIn: parent
|
|
visible: art.status !== Image.Ready
|
|
text: Media.sourceIcon
|
|
font.pixelSize: Theme.fsLarge
|
|
color: Theme.primary
|
|
pulsing: Media.playing
|
|
pulseScale: 1.12
|
|
}
|
|
}
|
|
|
|
Column {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 0
|
|
|
|
Marquee {
|
|
width: 130
|
|
height: 14
|
|
text: Media.title || Media.identity
|
|
color: Theme.text
|
|
pixelSize: Theme.fsSmall
|
|
family: Theme.fontDisplay
|
|
weight: Font.DemiBold
|
|
italic: true
|
|
}
|
|
|
|
Marquee {
|
|
width: 130
|
|
height: 12
|
|
text: Media.artist || Media.album || Media.identity
|
|
color: Theme.muted
|
|
pixelSize: Theme.fsMicro
|
|
family: Theme.fontMono
|
|
}
|
|
}
|
|
|
|
// Play state, as a two-bar equaliser that freezes when paused.
|
|
Row {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 2
|
|
|
|
Repeater {
|
|
model: 3
|
|
|
|
Rectangle {
|
|
required property int index
|
|
width: 3
|
|
height: 6
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
color: Media.playing ? Theme.primary : Theme.muted
|
|
|
|
SequentialAnimation on height {
|
|
running: Media.playing
|
|
loops: Animation.Infinite
|
|
NumberAnimation {
|
|
to: 4 + (index * 5) % 13
|
|
duration: 320 + index * 90
|
|
easing.type: Easing.InOutSine
|
|
}
|
|
NumberAnimation {
|
|
to: 14 - (index * 4) % 9
|
|
duration: 280 + index * 70
|
|
easing.type: Easing.InOutSine
|
|
}
|
|
}
|
|
|
|
Behavior on color {
|
|
ColorAnimation { duration: Theme.durBase }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Track progress, hairline along the bottom of the pill.
|
|
Rectangle {
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 3
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: root.leanInset
|
|
width: Math.max(0, (root.width - root.leanInset * 2) * Media.progress)
|
|
height: 2
|
|
color: Theme.accent
|
|
visible: Media.canSeek
|
|
|
|
Behavior on width {
|
|
NumberAnimation { duration: 480; easing.type: Easing.OutQuad }
|
|
}
|
|
}
|
|
|
|
onVisibleChanged: if (visible) titleGlitch.restart()
|
|
|
|
Connections {
|
|
target: Media
|
|
function onTrackChanged() { titleGlitch.restart(); }
|
|
}
|
|
|
|
SequentialAnimation {
|
|
id: titleGlitch
|
|
NumberAnimation {
|
|
target: artFrame; property: "rotation"
|
|
from: -8; to: 0; duration: Theme.durSlow; easing.type: Easing.OutBack
|
|
}
|
|
}
|
|
|
|
MediaPopout {
|
|
id: popout
|
|
anchorItem: root
|
|
triggerHovered: root.hovered && Media.hasPlayer
|
|
}
|
|
}
|