152 lines
4.3 KiB
QML
152 lines
4.3 KiB
QML
import Quickshell.Services.Mpris
|
||
import QtQuick
|
||
import QtQuick.Layouts
|
||
import "../components"
|
||
|
||
// Plain Item – lives inside the expanding center section border in Bar.qml.
|
||
Item {
|
||
id: root
|
||
required property var player
|
||
|
||
implicitWidth: 280
|
||
implicitHeight: layout.implicitHeight + 28
|
||
|
||
// Keep the progress binding live while playing, pause while seeking.
|
||
FrameAnimation {
|
||
running: (root.player?.isPlaying ?? false) && !progressArea.pressed
|
||
onTriggered: root.player.positionChanged()
|
||
}
|
||
|
||
ColumnLayout {
|
||
id: layout
|
||
anchors { fill: parent; margins: 14 }
|
||
spacing: 8
|
||
|
||
// Track info
|
||
Text {
|
||
text: root.player?.trackTitle ?? ""
|
||
color: Theme.text
|
||
font.pixelSize: 13
|
||
font.bold: true
|
||
Layout.fillWidth: true
|
||
elide: Text.ElideRight
|
||
}
|
||
|
||
Text {
|
||
text: root.player?.trackArtist ?? ""
|
||
color: Theme.textDim
|
||
font.pixelSize: 11
|
||
Layout.fillWidth: true
|
||
elide: Text.ElideRight
|
||
visible: (root.player?.trackArtist ?? "") !== ""
|
||
Layout.topMargin: -4
|
||
}
|
||
|
||
// Progress bar
|
||
Item {
|
||
visible: root.player?.positionSupported && root.player?.lengthSupported
|
||
&& (root.player?.length ?? 0) > 0
|
||
Layout.fillWidth: true
|
||
height: 12
|
||
|
||
Rectangle {
|
||
anchors.verticalCenter: parent.verticalCenter
|
||
width: parent.width
|
||
height: 3
|
||
radius: 2
|
||
color: Theme.progressTrack
|
||
|
||
Rectangle {
|
||
width: root.player?.length > 0
|
||
? parent.width * (root.player.position / root.player.length)
|
||
: 0
|
||
height: parent.height
|
||
radius: parent.radius
|
||
color: Theme.accent
|
||
}
|
||
}
|
||
|
||
MouseArea {
|
||
id: progressArea
|
||
anchors.fill: parent
|
||
hoverEnabled: true
|
||
cursorShape: Qt.PointingHandCursor
|
||
|
||
function seek(mx) {
|
||
if (!(root.player?.canSeek)) return
|
||
const ratio = Math.max(0, Math.min(1, mx / width))
|
||
root.player.position = ratio * (root.player?.length ?? 0)
|
||
}
|
||
|
||
onClicked: seek(mouseX)
|
||
onPositionChanged: if (pressed) seek(mouseX)
|
||
}
|
||
}
|
||
|
||
// Media controls
|
||
RowLayout {
|
||
Layout.fillWidth: true
|
||
spacing: 0
|
||
|
||
Item { Layout.fillWidth: true }
|
||
|
||
ControlButton {
|
||
icon: "⏮"
|
||
iconSize: 18
|
||
enabled: root.player?.canGoPrevious ?? false
|
||
onActivated: root.player.previous()
|
||
}
|
||
|
||
Item { width: 8 }
|
||
|
||
ControlButton {
|
||
icon: root.player?.isPlaying ? "⏸" : "▶"
|
||
iconSize: 22
|
||
enabled: true
|
||
onActivated: root.player?.togglePlaying()
|
||
}
|
||
|
||
Item { width: 8 }
|
||
|
||
ControlButton {
|
||
icon: "⏭"
|
||
iconSize: 18
|
||
enabled: root.player?.canGoNext ?? false
|
||
onActivated: root.player.next()
|
||
}
|
||
|
||
Item { Layout.fillWidth: true }
|
||
}
|
||
}
|
||
|
||
component ControlButton: Rectangle {
|
||
id: btn
|
||
required property string icon
|
||
required property int iconSize
|
||
required property bool enabled
|
||
signal activated
|
||
|
||
implicitWidth: label.implicitWidth + 16
|
||
implicitHeight: label.implicitHeight + 10
|
||
radius: Theme.radius
|
||
color: btnArea.containsMouse ? Qt.rgba(1, 1, 1, 0.08) : 'transparent'
|
||
Behavior on color { ColorAnimation { duration: 80 } }
|
||
|
||
Text {
|
||
id: label
|
||
anchors.centerIn: parent
|
||
text: btn.icon
|
||
font.pixelSize: btn.iconSize
|
||
color: btn.enabled ? Theme.text : Theme.textDisabled
|
||
}
|
||
|
||
MouseArea {
|
||
id: btnArea
|
||
anchors.fill: parent
|
||
hoverEnabled: true
|
||
cursorShape: Qt.PointingHandCursor
|
||
onClicked: if (btn.enabled) btn.activated()
|
||
}
|
||
}
|
||
}
|