shift-shell/components/mobileshell/qml/widgets/mediacontrols/MediaControlsWidget.qml

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

274 lines
11 KiB
QML
Raw Normal View History

2023-09-27 05:41:52 +00:00
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QQC2
import QtQuick.Effects
import org.kde.kirigami as Kirigami
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.state as MobileShellState
2023-09-27 05:41:52 +00:00
import org.kde.plasma.private.mpris as Mpris
/**
* Embeddable component that provides MPRIS control.
*/
Item {
id: root
2023-09-27 05:41:52 +00:00
visible: sourceRepeater.count > 0
property int panelType: MobileShell.PanelBackground.PanelType.Drawer
property bool detailledView: false
2023-09-27 05:41:52 +00:00
readonly property real padding: Kirigami.Units.gridUnit
readonly property real contentHeight: {
let heightMultiplier = detailledView ? 2 : 1
Kirigami.Units.gridUnit * 2 * heightMultiplier
}
implicitHeight: visible ? padding * 2 + contentHeight : 0
Behavior on implicitHeight {
NumberAnimation {
duration: implicitHeight == 0 ? 0 : Kirigami.Units.longDuration
easing.type: Easing.OutQuart
}
}
2024-07-27 03:47:44 +00:00
MediaControlsSource {
id: mpris2Source
}
2024-07-27 03:47:44 +00:00
// page indicator
RowLayout {
z: 1
visible: view.count > 1
spacing: Kirigami.Units.smallSpacing
2023-09-27 05:41:52 +00:00
anchors.bottomMargin: Kirigami.Units.smallSpacing
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
2024-07-27 03:47:44 +00:00
Repeater {
model: view.count
delegate: Rectangle {
width: Kirigami.Units.smallSpacing
height: Kirigami.Units.smallSpacing
radius: width / 2
color: Qt.rgba(255, 255, 255, view.currentIndex == model.index ? 1 : 0.5)
}
}
}
2024-07-27 03:47:44 +00:00
MobileShell.PanelBackground {
anchors.fill: parent
panelType: root.panelType
}
// list of app media widgets
QQC2.SwipeView {
id: view
clip: true
2024-07-27 03:47:44 +00:00
anchors.fill: parent
2024-07-27 03:47:44 +00:00
Repeater {
2023-09-27 05:41:52 +00:00
id: sourceRepeater
model: mpris2Source.mpris2Model
2024-07-27 03:47:44 +00:00
delegate: Loader {
2023-09-27 05:41:52 +00:00
id: delegate
// NOTE: model is PlayerContainer from KMpris in plasma-workspace
asynchronous: true
2024-07-27 03:47:44 +00:00
2023-09-27 05:41:52 +00:00
function getTrackName() {
console.log('track name: ' + model.title);
if (model.title) {
return model.title;
}
// if no track title is given, print out the file name
if (!model.url) {
return "";
}
const lastSlashPos = model.url.lastIndexOf('/')
if (lastSlashPos < 0) {
return ""
}
const lastUrlPart = model.url.substring(lastSlashPos + 1);
return decodeURIComponent(lastUrlPart);
}
function msecToString(duration: int): string {
let seconds = Math.floor(duration / 1000000)
let minutes = Math.floor(seconds / 60)
seconds -= minutes * 60
return `${minutes}:${seconds.toString().padStart(2, '0')}`
}
sourceComponent: MouseArea {
id: mouseArea
implicitHeight: root.contentHeight + root.padding * 2
implicitWidth: root.width
onPressAndHold: {
MobileShell.AppLaunch.launchOrActivateApp(model.desktopEntry + ".desktop");
MobileShellState.ShellDBusClient.closeActionDrawer();
}
2024-07-27 03:47:44 +00:00
onClicked: {
root.detailledView = !root.detailledView
}
BlurredBackground {
anchors.fill: parent
darken: mouseArea.pressed
inActionDrawer: root.inActionDrawer
imageSource: model.artUrl
}
2024-07-27 03:47:44 +00:00
MobileShell.BaseItem {
id: playerItem
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
2024-07-27 03:47:44 +00:00
padding: root.padding
2024-07-27 03:47:44 +00:00
contentItem: ColumnLayout {
width: playerItem.width - playerItem.leftPadding - playerItem.rightPadding
spacing: Kirigami.Units.largeSpacing
2024-07-27 03:47:44 +00:00
RowLayout {
id: controlsRow
spacing: 0
2023-09-27 05:41:52 +00:00
enabled: model.canControl
Image {
id: albumArt
Layout.preferredWidth: height
Layout.preferredHeight: controlsRow.height
asynchronous: true
fillMode: Image.PreserveAspectFit
2023-09-27 05:41:52 +00:00
source: model.artUrl
sourceSize.height: height
visible: status === Image.Loading || status === Image.Ready
}
ColumnLayout {
Layout.leftMargin: albumArt.visible ? Kirigami.Units.gridUnit : 0
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
2023-09-27 05:41:52 +00:00
// media track name text
MobileShell.MarqueeLabel {
2023-09-27 05:41:52 +00:00
id: trackLabel
Layout.fillWidth: true
2023-09-27 05:41:52 +00:00
inputText: model.track || i18n("No media playing");
textFormat: Text.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize
}
2023-09-27 05:41:52 +00:00
// media artist name text
MobileShell.MarqueeLabel {
2023-09-27 05:41:52 +00:00
id: artistLabel
Layout.fillWidth: true
// if no artist is given, show player name instead
2023-09-27 05:41:52 +00:00
inputText: model.artist || model.identity || ""
textFormat: Text.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
opacity: 0.9
}
}
QQC2.ToolButton {
2023-09-27 05:41:52 +00:00
enabled: model.canGoPrevious
icon.name: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
icon.width: Kirigami.Units.iconSizes.smallMedium
icon.height: Kirigami.Units.iconSizes.smallMedium
2023-09-27 05:41:52 +00:00
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.goPrevious();
}
visible: model.canGoPrevious || model.canGoNext
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Previous track")
}
QQC2.ToolButton {
2023-09-27 05:41:52 +00:00
icon.name: (model.playbackStatus === Mpris.PlaybackStatus.Playing) ? "media-playback-pause" : "media-playback-start"
icon.width: Kirigami.Units.iconSizes.smallMedium
icon.height: Kirigami.Units.iconSizes.smallMedium
2023-09-27 05:41:52 +00:00
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.playPause();
}
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Play or Pause media")
}
QQC2.ToolButton {
2023-09-27 05:41:52 +00:00
enabled: model.canGoNext
icon.name: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
icon.width: Kirigami.Units.iconSizes.smallMedium
icon.height: Kirigami.Units.iconSizes.smallMedium
2023-09-27 05:41:52 +00:00
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.goNext();
}
visible: model.canGoPrevious || model.canGoNext
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Next track")
}
}
RowLayout {
id: timerControlsRow
spacing: Kirigami.Units.largeSpacing
visible: root.detailledView
Text {
text: msecToString(model.position)
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
color: Kirigami.Theme.textColor
}
PC3.Slider {
Layout.fillWidth: true
from: 0
value: model.position
to: model.length
onMoved: model.position = value
Timer {
interval: 1000; running: true; repeat: true
onTriggered: {
mpris2Source.setIndex(model.index);
mpris2Source.updatePosition()
}
}
}
Text {
text: msecToString(model.length)
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
color: Kirigami.Theme.textColor
}
}
}
}
}
}
}
}
}