mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
420 lines
16 KiB
QML
420 lines
16 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import Qt5Compat.GraphicalEffects
|
|
import Quickshell
|
|
import Quickshell.Widgets
|
|
import Quickshell.Wayland
|
|
import Quickshell.Io
|
|
import Quickshell.Services.Mpris
|
|
import "Services"
|
|
|
|
PanelWindow {
|
|
id: mediaPlayer
|
|
|
|
property var theme
|
|
property bool isVisible: false
|
|
property MprisPlayer activePlayer: MprisController.activePlayer
|
|
property bool hasActiveMedia: MprisController.isPlaying && (activePlayer?.trackTitle || activePlayer?.trackArtist)
|
|
|
|
property var defaultTheme: QtObject {
|
|
property color primary: "#D0BCFF"
|
|
property color background: "#10121E"
|
|
property color surfaceContainer: "#1D1B20"
|
|
property color surfaceText: "#E6E0E9"
|
|
property color surfaceVariant: "#49454F"
|
|
property color surfaceVariantText: "#CAC4D0"
|
|
property color outline: "#938F99"
|
|
property color error: "#F2B8B5"
|
|
property real cornerRadius: 12
|
|
property real cornerRadiusLarge: 16
|
|
property real cornerRadiusXLarge: 24
|
|
property real cornerRadiusSmall: 8
|
|
property real spacingXS: 4
|
|
property real spacingS: 8
|
|
property real spacingM: 12
|
|
property real spacingL: 16
|
|
property real spacingXL: 24
|
|
property real fontSizeLarge: 16
|
|
property real fontSizeMedium: 14
|
|
property real fontSizeSmall: 12
|
|
property real iconSize: 24
|
|
property real iconSizeLarge: 32
|
|
property string iconFont: "Material Symbols Rounded"
|
|
property int iconFontWeight: Font.Normal
|
|
property int shortDuration: 150
|
|
property int mediumDuration: 300
|
|
property int standardEasing: Easing.OutCubic
|
|
property int emphasizedEasing: Easing.OutQuart
|
|
}
|
|
|
|
property var activeTheme: theme || defaultTheme
|
|
|
|
onHasActiveMediaChanged: {
|
|
if (!hasActiveMedia && isVisible) {
|
|
hide()
|
|
}
|
|
}
|
|
|
|
anchors {
|
|
top: true
|
|
left: true
|
|
right: true
|
|
bottom: true
|
|
}
|
|
|
|
WlrLayershell.layer: WlrLayershell.Overlay
|
|
WlrLayershell.exclusiveZone: -1
|
|
WlrLayershell.keyboardFocus: isVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
|
WlrLayershell.namespace: "quickshell-media-player"
|
|
|
|
visible: isVisible
|
|
color: "transparent"
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Qt.rgba(0, 0, 0, 0.3)
|
|
opacity: mediaPlayer.isVisible ? 1.0 : 0.0
|
|
visible: mediaPlayer.isVisible
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: activeTheme.shortDuration
|
|
easing.type: activeTheme.standardEasing
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
enabled: mediaPlayer.isVisible
|
|
onClicked: mediaPlayer.hide()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: mediaPanel
|
|
|
|
width: 480
|
|
height: 320
|
|
|
|
anchors.centerIn: parent
|
|
|
|
color: Qt.rgba(activeTheme.surfaceContainer.r, activeTheme.surfaceContainer.g, activeTheme.surfaceContainer.b, 0.98)
|
|
radius: activeTheme.cornerRadiusXLarge
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
anchors.margins: -2
|
|
color: "transparent"
|
|
radius: parent.radius + 2
|
|
border.color: Qt.rgba(0, 0, 0, 0.08)
|
|
border.width: 1
|
|
z: -2
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: "transparent"
|
|
border.color: Qt.rgba(activeTheme.outline.r, activeTheme.outline.g, activeTheme.outline.b, 0.12)
|
|
border.width: 1
|
|
radius: parent.radius
|
|
z: -1
|
|
}
|
|
|
|
transform: [
|
|
Scale {
|
|
origin.x: mediaPanel.width / 2
|
|
origin.y: mediaPanel.height / 2
|
|
xScale: mediaPlayer.isVisible ? 1.0 : 0.9
|
|
yScale: mediaPlayer.isVisible ? 1.0 : 0.9
|
|
|
|
Behavior on xScale {
|
|
NumberAnimation {
|
|
duration: activeTheme.mediumDuration
|
|
easing.type: activeTheme.emphasizedEasing
|
|
}
|
|
}
|
|
|
|
Behavior on yScale {
|
|
NumberAnimation {
|
|
duration: activeTheme.mediumDuration
|
|
easing.type: activeTheme.emphasizedEasing
|
|
}
|
|
}
|
|
}
|
|
]
|
|
|
|
opacity: mediaPlayer.isVisible ? 1.0 : 0.0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: activeTheme.mediumDuration
|
|
easing.type: activeTheme.emphasizedEasing
|
|
}
|
|
}
|
|
|
|
Column {
|
|
anchors.fill: parent
|
|
anchors.margins: activeTheme.spacingXL
|
|
spacing: activeTheme.spacingL
|
|
|
|
Row {
|
|
width: parent.width
|
|
height: 32
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Now Playing"
|
|
font.pixelSize: activeTheme.fontSizeLarge + 4
|
|
font.weight: Font.Bold
|
|
color: activeTheme.surfaceText
|
|
}
|
|
|
|
Item { width: parent.width - 200; height: 1 }
|
|
|
|
Rectangle {
|
|
width: 32
|
|
height: 32
|
|
radius: activeTheme.cornerRadius
|
|
color: closeArea.containsMouse ? Qt.rgba(activeTheme.error.r, activeTheme.error.g, activeTheme.error.b, 0.12) : "transparent"
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: "close"
|
|
font.family: activeTheme.iconFont
|
|
font.pixelSize: activeTheme.iconSize
|
|
color: closeArea.containsMouse ? activeTheme.error : activeTheme.surfaceText
|
|
}
|
|
|
|
MouseArea {
|
|
id: closeArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: mediaPlayer.hide()
|
|
}
|
|
}
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
height: parent.height - 80
|
|
spacing: activeTheme.spacingXL
|
|
|
|
Rectangle {
|
|
width: 180
|
|
height: parent.height
|
|
radius: activeTheme.cornerRadiusLarge
|
|
color: Qt.rgba(activeTheme.surfaceVariant.r, activeTheme.surfaceVariant.g, activeTheme.surfaceVariant.b, 0.3)
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
clip: true
|
|
|
|
Image {
|
|
id: albumArt
|
|
anchors.fill: parent
|
|
source: activePlayer?.trackArtUrl || ""
|
|
fillMode: Image.PreserveAspectCrop
|
|
smooth: true
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
visible: albumArt.status !== Image.Ready
|
|
color: "transparent"
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: "album"
|
|
font.family: activeTheme.iconFont
|
|
font.pixelSize: 48
|
|
color: activeTheme.surfaceVariantText
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - 180 - activeTheme.spacingXL
|
|
height: parent.height
|
|
spacing: activeTheme.spacingM
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: activeTheme.spacingS
|
|
|
|
Text {
|
|
text: activePlayer?.trackTitle || "No title"
|
|
font.pixelSize: activeTheme.fontSizeLarge + 2
|
|
font.weight: Font.Bold
|
|
color: activeTheme.surfaceText
|
|
elide: Text.ElideRight
|
|
width: parent.width
|
|
}
|
|
|
|
Text {
|
|
text: activePlayer?.trackArtist || "Unknown artist"
|
|
font.pixelSize: activeTheme.fontSizeLarge
|
|
color: activeTheme.surfaceVariantText
|
|
elide: Text.ElideRight
|
|
width: parent.width
|
|
}
|
|
|
|
Text {
|
|
text: activePlayer?.trackAlbum || ""
|
|
font.pixelSize: activeTheme.fontSizeMedium
|
|
color: activeTheme.surfaceVariantText
|
|
elide: Text.ElideRight
|
|
width: parent.width
|
|
visible: text.length > 0
|
|
}
|
|
}
|
|
|
|
Item { height: activeTheme.spacingM }
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: activeTheme.spacingS
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: 6
|
|
radius: 3
|
|
color: Qt.rgba(activeTheme.surfaceVariant.r, activeTheme.surfaceVariant.g, activeTheme.surfaceVariant.b, 0.3)
|
|
|
|
Rectangle {
|
|
width: parent.width * (activePlayer?.position / Math.max(activePlayer?.length || 1, 1))
|
|
height: parent.height
|
|
radius: parent.radius
|
|
color: activeTheme.primary
|
|
}
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
|
|
Text {
|
|
text: formatTime(activePlayer?.position || 0)
|
|
font.pixelSize: activeTheme.fontSizeSmall
|
|
color: activeTheme.surfaceVariantText
|
|
}
|
|
|
|
Item { width: parent.width - 100; height: 1 }
|
|
|
|
Text {
|
|
text: formatTime(activePlayer?.length || 0)
|
|
font.pixelSize: activeTheme.fontSizeSmall
|
|
color: activeTheme.surfaceVariantText
|
|
}
|
|
}
|
|
}
|
|
|
|
Item { height: activeTheme.spacingL }
|
|
|
|
Row {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: activeTheme.spacingL
|
|
|
|
Rectangle {
|
|
width: 48
|
|
height: 48
|
|
radius: 24
|
|
color: prevArea.containsMouse ? Qt.rgba(activeTheme.surfaceVariant.r, activeTheme.surfaceVariant.g, activeTheme.surfaceVariant.b, 0.12) : "transparent"
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: "skip_previous"
|
|
font.family: activeTheme.iconFont
|
|
font.pixelSize: activeTheme.iconSize
|
|
color: activeTheme.surfaceText
|
|
}
|
|
|
|
MouseArea {
|
|
id: prevArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: activePlayer?.previous()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 56
|
|
height: 56
|
|
radius: 28
|
|
color: activeTheme.primary
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
|
font.family: activeTheme.iconFont
|
|
font.pixelSize: activeTheme.iconSizeLarge
|
|
color: activeTheme.background
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: activePlayer?.togglePlaying()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 48
|
|
height: 48
|
|
radius: 24
|
|
color: nextArea.containsMouse ? Qt.rgba(activeTheme.surfaceVariant.r, activeTheme.surfaceVariant.g, activeTheme.surfaceVariant.b, 0.12) : "transparent"
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: "skip_next"
|
|
font.family: activeTheme.iconFont
|
|
font.pixelSize: activeTheme.iconSize
|
|
color: activeTheme.surfaceText
|
|
}
|
|
|
|
MouseArea {
|
|
id: nextArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: activePlayer?.next()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
running: activePlayer?.playbackState === MprisPlaybackState.Playing
|
|
interval: 1000
|
|
repeat: true
|
|
onTriggered: activePlayer?.positionChanged()
|
|
}
|
|
|
|
function formatTime(seconds) {
|
|
const mins = Math.floor(seconds / 60)
|
|
const secs = Math.floor(seconds % 60)
|
|
return mins + ":" + (secs < 10 ? "0" : "") + secs
|
|
}
|
|
|
|
function show() {
|
|
mediaPlayer.isVisible = true
|
|
}
|
|
|
|
function hide() {
|
|
mediaPlayer.isVisible = false
|
|
}
|
|
|
|
function toggle() {
|
|
if (mediaPlayer.isVisible) {
|
|
hide()
|
|
} else {
|
|
show()
|
|
}
|
|
}
|
|
} |