307 lines
10 KiB
QML
307 lines
10 KiB
QML
import qs.components
|
|
import qs.services
|
|
import qs.config
|
|
import Quickshell.Widgets
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls
|
|
|
|
RowLayout {
|
|
id: root
|
|
|
|
required property bool isLarge
|
|
|
|
spacing: Appearance.spacing.large * (isLarge ? 2 : 1.5)
|
|
width: isLarge ? Config.lock.sizes.mediaWidth : Config.lock.sizes.mediaWidthSmall
|
|
|
|
property real playerProgress: {
|
|
const active = Players.active;
|
|
return active?.length ? active.position / active.length : 0;
|
|
}
|
|
|
|
Behavior on playerProgress {
|
|
NumberAnimation {
|
|
duration: Appearance.anim.durations.large
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: Appearance.anim.curves.standard
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
running: Players.active?.isPlaying ?? false
|
|
interval: Config.dashboard.mediaUpdateInterval
|
|
triggeredOnStart: true
|
|
repeat: true
|
|
onTriggered: Players.active?.positionChanged()
|
|
}
|
|
|
|
Item {
|
|
Layout.alignment: Qt.AlignVCenter
|
|
Layout.topMargin: root.isLarge ? 0 : Config.lock.sizes.border / 2
|
|
Layout.bottomMargin: root.isLarge ? Config.lock.sizes.border / 2 : 0
|
|
Layout.leftMargin: root.isLarge ? 0 : Config.lock.sizes.border / 2
|
|
|
|
implicitWidth: root.isLarge ? Config.lock.sizes.mediaCoverSize : Config.lock.sizes.mediaCoverSizeSmall
|
|
implicitHeight: root.isLarge ? Config.lock.sizes.mediaCoverSize : Config.lock.sizes.mediaCoverSizeSmall
|
|
|
|
ClippingWrapperRectangle {
|
|
anchors.fill: parent
|
|
|
|
color: Colours.palette.m3surfaceContainerHigh
|
|
radius: Appearance.rounding.small
|
|
rotation: 9
|
|
|
|
Image {
|
|
anchors.fill: parent
|
|
|
|
source: Players.active?.trackArtUrl ?? ""
|
|
asynchronous: true
|
|
fillMode: Image.PreserveAspectCrop
|
|
sourceSize.width: width
|
|
sourceSize.height: height
|
|
}
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: Appearance.anim.durations.normal
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: Appearance.anim.curves.standard
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
anchors.margins: -1
|
|
border.width: Config.lock.sizes.mediaCoverBorder
|
|
border.color: Colours.palette.m3primary
|
|
color: "transparent"
|
|
radius: Appearance.rounding.small
|
|
rotation: 9
|
|
|
|
Behavior on border.color {
|
|
ColorAnimation {
|
|
duration: Appearance.anim.durations.normal
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: Appearance.anim.curves.standard
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledClippingRect {
|
|
anchors.fill: parent
|
|
|
|
color: Colours.palette.m3surfaceContainerHigh
|
|
radius: Appearance.rounding.small
|
|
|
|
border.width: Config.lock.sizes.mediaCoverBorder
|
|
border.color: Colours.palette.m3primary
|
|
|
|
MaterialIcon {
|
|
anchors.centerIn: parent
|
|
|
|
grade: 200
|
|
text: "art_track"
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
font.pointSize: (root.isLarge ? Config.lock.sizes.mediaCoverSize : Config.lock.sizes.mediaCoverSizeSmall) * 0.4
|
|
}
|
|
|
|
Image {
|
|
anchors.fill: parent
|
|
|
|
source: Players.active?.trackArtUrl ?? ""
|
|
asynchronous: true
|
|
fillMode: Image.PreserveAspectCrop
|
|
sourceSize.width: width
|
|
sourceSize.height: height
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.alignment: Qt.AlignVCenter
|
|
Layout.topMargin: root.isLarge ? 0 : Config.lock.sizes.border / 2
|
|
Layout.bottomMargin: root.isLarge ? Config.lock.sizes.border / 2 : 0
|
|
Layout.rightMargin: root.isLarge ? Config.lock.sizes.border / 2 : 0
|
|
Layout.fillWidth: true
|
|
|
|
spacing: root.isLarge ? Appearance.spacing.small : Appearance.spacing.small / 2
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
|
|
animate: true
|
|
text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
|
|
color: Colours.palette.m3primary
|
|
font.pointSize: root.isLarge ? Appearance.font.size.large : Appearance.font.size.larger
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
|
|
animate: true
|
|
text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album")
|
|
color: Colours.palette.m3outline
|
|
font.pointSize: root.isLarge ? Appearance.font.size.larger : Appearance.font.size.normal
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
|
|
animate: true
|
|
text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist")
|
|
color: Colours.palette.m3secondary
|
|
font.pointSize: root.isLarge ? Appearance.font.size.larger : Appearance.font.size.normal
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
RowLayout {
|
|
id: controls
|
|
|
|
Layout.fillWidth: true
|
|
|
|
spacing: Appearance.spacing.small
|
|
|
|
Slider {
|
|
id: slider
|
|
|
|
Layout.rightMargin: root.isLarge ? Appearance.spacing.small : 0
|
|
Layout.fillWidth: true
|
|
implicitHeight: Appearance.padding.normal * 3
|
|
|
|
value: root.playerProgress
|
|
onMoved: {
|
|
const active = Players.active;
|
|
if (active?.canSeek && active?.positionSupported)
|
|
active.position = value * active.length;
|
|
}
|
|
|
|
background: Item {
|
|
StyledRect {
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: parent.left
|
|
anchors.topMargin: slider.implicitHeight / 3
|
|
anchors.bottomMargin: slider.implicitHeight / 3
|
|
|
|
implicitWidth: slider.handle.x - slider.implicitHeight / 6
|
|
|
|
color: Colours.palette.m3primary
|
|
radius: Appearance.rounding.full
|
|
topRightRadius: slider.implicitHeight / 15
|
|
bottomRightRadius: slider.implicitHeight / 15
|
|
}
|
|
|
|
StyledRect {
|
|
anchors.top: parent.top
|
|
anchors.bottom: parent.bottom
|
|
anchors.right: parent.right
|
|
anchors.topMargin: slider.implicitHeight / 3
|
|
anchors.bottomMargin: slider.implicitHeight / 3
|
|
|
|
implicitWidth: parent.width - slider.handle.x - slider.handle.implicitWidth - slider.implicitHeight / 6
|
|
|
|
color: Colours.palette.m3surfaceContainer
|
|
radius: Appearance.rounding.full
|
|
topLeftRadius: slider.implicitHeight / 15
|
|
bottomLeftRadius: slider.implicitHeight / 15
|
|
}
|
|
}
|
|
|
|
handle: StyledRect {
|
|
id: rect
|
|
|
|
x: slider.visualPosition * slider.availableWidth
|
|
|
|
implicitWidth: slider.implicitHeight / 4.5
|
|
implicitHeight: slider.implicitHeight
|
|
|
|
color: Colours.palette.m3primary
|
|
radius: Appearance.rounding.full
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onPressed: event => event.accepted = false
|
|
}
|
|
}
|
|
}
|
|
|
|
Control {
|
|
icon: "skip_previous"
|
|
canUse: Players.active?.canGoPrevious ?? false
|
|
fontSize: root.isLarge ? Appearance.font.size.extraLarge : Appearance.font.size.large * 1.2
|
|
|
|
function onClicked(): void {
|
|
Players.active?.previous();
|
|
}
|
|
}
|
|
|
|
Control {
|
|
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
|
|
canUse: Players.active?.canTogglePlaying ?? false
|
|
fontSize: root.isLarge ? Appearance.font.size.extraLarge : Appearance.font.size.large * 1.2
|
|
primary: true
|
|
|
|
function onClicked(): void {
|
|
Players.active?.togglePlaying();
|
|
}
|
|
}
|
|
|
|
Control {
|
|
icon: "skip_next"
|
|
canUse: Players.active?.canGoNext ?? false
|
|
fontSize: root.isLarge ? Appearance.font.size.extraLarge : Appearance.font.size.large * 1.2
|
|
|
|
function onClicked(): void {
|
|
Players.active?.next();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
component Control: StyledRect {
|
|
id: control
|
|
|
|
required property string icon
|
|
required property bool canUse
|
|
required property int fontSize
|
|
property int padding
|
|
property bool fill: true
|
|
property bool primary
|
|
function onClicked(): void {
|
|
}
|
|
|
|
implicitWidth: Math.max(icon.implicitWidth, icon.implicitHeight) + padding * 2
|
|
implicitHeight: implicitWidth
|
|
|
|
radius: Appearance.rounding.full
|
|
color: primary && canUse ? Colours.palette.m3primary : "transparent"
|
|
|
|
StateLayer {
|
|
disabled: !control.canUse
|
|
radius: parent.radius
|
|
color: control.primary ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
|
|
|
|
function onClicked(): void {
|
|
control.onClicked();
|
|
}
|
|
}
|
|
|
|
MaterialIcon {
|
|
id: icon
|
|
|
|
anchors.centerIn: parent
|
|
anchors.horizontalCenterOffset: -font.pointSize * 0.02
|
|
anchors.verticalCenterOffset: font.pointSize * 0.02
|
|
|
|
animate: true
|
|
fill: control.fill ? 1 : 0
|
|
text: control.icon
|
|
color: control.canUse ? control.primary ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface : Colours.palette.m3outline
|
|
font.pointSize: control.fontSize
|
|
}
|
|
}
|
|
}
|