mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-29 16:02:51 -05:00
add shell
This commit is contained in:
420
MediaPlayer.qml
Normal file
420
MediaPlayer.qml
Normal file
@@ -0,0 +1,420 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user