MultiEffect used to do a hard cutoff and not work with soft masks, but since Qt 6.10 (or 6.11) it works correctly, so we don't need the custom mask anymore
142 lines
3.8 KiB
QML
142 lines
3.8 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import Caelestia.Components
|
|
import Caelestia.Config
|
|
import qs.components
|
|
import qs.components.controls
|
|
import qs.components.effects
|
|
import qs.services
|
|
|
|
Item {
|
|
id: root
|
|
|
|
required property var lock
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
implicitHeight: layout.implicitHeight
|
|
|
|
Image {
|
|
anchors.fill: parent
|
|
source: Players.getArtUrl(Players.active)
|
|
|
|
asynchronous: true
|
|
fillMode: Image.PreserveAspectCrop
|
|
sourceSize: {
|
|
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
|
|
return Qt.size(width * dpr, height * dpr);
|
|
}
|
|
|
|
layer.enabled: true
|
|
layer.effect: Mask {
|
|
maskSource: mask
|
|
}
|
|
|
|
opacity: status === Image.Ready ? 1 : 0
|
|
|
|
Behavior on opacity {
|
|
Anim {
|
|
type: Anim.StandardExtraLarge
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: mask
|
|
|
|
anchors.fill: parent
|
|
layer.enabled: true
|
|
visible: false
|
|
|
|
gradient: Gradient {
|
|
orientation: Gradient.Horizontal
|
|
|
|
GradientStop {
|
|
position: 0
|
|
color: Qt.rgba(0, 0, 0, 0.5)
|
|
}
|
|
GradientStop {
|
|
position: 0.4
|
|
color: Qt.rgba(0, 0, 0, 0.2)
|
|
}
|
|
GradientStop {
|
|
position: 0.8
|
|
color: Qt.rgba(0, 0, 0, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
id: layout
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.margins: Tokens.padding.medium
|
|
|
|
StyledText {
|
|
Layout.topMargin: Tokens.padding.medium
|
|
Layout.bottomMargin: Tokens.spacing.large
|
|
text: qsTr("Now playing")
|
|
color: Colours.palette.m3onSurfaceVariant
|
|
font: Tokens.font.label.builders.medium.weight(Font.DemiBold).vaxis("slnt", -4).build()
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
animate: true
|
|
text: Players.active?.trackArtist ?? qsTr("No media")
|
|
color: Colours.palette.m3primary
|
|
horizontalAlignment: Text.AlignHCenter
|
|
font: Tokens.font.body.builders.large.weight(Font.DemiBold).build()
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
animate: true
|
|
text: Players.active?.trackTitle ?? qsTr("No media")
|
|
horizontalAlignment: Text.AlignHCenter
|
|
font: Tokens.font.title.medium
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
ButtonRow {
|
|
Layout.alignment: Qt.AlignHCenter
|
|
Layout.topMargin: Tokens.spacing.medium
|
|
Layout.bottomMargin: Tokens.padding.extraLarge
|
|
|
|
spacing: Tokens.spacing.extraSmall
|
|
|
|
IconButton {
|
|
type: IconButton.Tonal
|
|
icon: "skip_previous"
|
|
isRound: true
|
|
shapeMorph: true
|
|
disabled: !Players.active?.canGoPrevious
|
|
onClicked: Players.active?.previous()
|
|
}
|
|
|
|
IconButton {
|
|
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
|
|
isRound: true
|
|
shapeMorph: true
|
|
checked: Players.active?.isPlaying ?? false
|
|
disabled: !Players.active?.canTogglePlaying
|
|
onClicked: Players.active?.togglePlaying()
|
|
implicitWidth: implicitHeight + Tokens.padding.large * 2
|
|
}
|
|
|
|
IconButton {
|
|
type: IconButton.Tonal
|
|
icon: "skip_next"
|
|
isRound: true
|
|
shapeMorph: true
|
|
disabled: !Players.active?.canGoNext
|
|
onClicked: Players.active?.next()
|
|
}
|
|
}
|
|
}
|
|
}
|