shift-shell/components/mobileshell/qml/widgets/mediacontrols/MediaControlsWidget.qml
Micah Stanley 7e2019cb0e MobileShell: Unify Item Background Elements
We so far have been recreating the background elements for all the item throughout the shell. This merge request simplifies this by unifying these elements into a single component, making is easier to keep things at a consistent design while also being able to adjust things when needed in the future.

Per request, I tried to keep thing looking mostly the same as before.

The first picture is what the action drawer looks like now, the one after is with these changes.

![Screenshot_20250621_154201](/uploads/7bd5129d942c8fbc3706dd1728b3a1dd/Screenshot_20250621_154201.png)
![Screenshot_20250621_155520](/uploads/9d2e925c9483579dec765813465e4d2d/Screenshot_20250621_155520.png)
2025-06-22 20:16:16 -04:00

274 lines
11 KiB
QML

// 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
import org.kde.plasma.private.mpris as Mpris
/**
* Embeddable component that provides MPRIS control.
*/
Item {
id: root
visible: sourceRepeater.count > 0
property int panelType: MobileShell.PanelBackground.PanelType.Drawer
property bool detailledView: false
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
}
}
MediaControlsSource {
id: mpris2Source
}
// page indicator
RowLayout {
z: 1
visible: view.count > 1
spacing: Kirigami.Units.smallSpacing
anchors.bottomMargin: Kirigami.Units.smallSpacing
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
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)
}
}
}
MobileShell.PanelBackground {
anchors.fill: parent
panelType: root.panelType
}
// list of app media widgets
QQC2.SwipeView {
id: view
clip: true
anchors.fill: parent
Repeater {
id: sourceRepeater
model: mpris2Source.mpris2Model
delegate: Loader {
id: delegate
// NOTE: model is PlayerContainer from KMpris in plasma-workspace
asynchronous: true
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();
}
onClicked: {
root.detailledView = !root.detailledView
}
BlurredBackground {
anchors.fill: parent
darken: mouseArea.pressed
inActionDrawer: root.inActionDrawer
imageSource: model.artUrl
}
MobileShell.BaseItem {
id: playerItem
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
padding: root.padding
contentItem: ColumnLayout {
Kirigami.Theme.inherit: true
width: playerItem.width - playerItem.leftPadding - playerItem.rightPadding
spacing: Kirigami.Units.largeSpacing
RowLayout {
id: controlsRow
spacing: 0
enabled: model.canControl
Image {
id: albumArt
Layout.preferredWidth: height
Layout.preferredHeight: controlsRow.height
asynchronous: true
fillMode: Image.PreserveAspectFit
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
// media track name text
MobileShell.MarqueeLabel {
id: trackLabel
Layout.fillWidth: true
inputText: model.track || i18n("No media playing");
textFormat: Text.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize
}
// media artist name text
MobileShell.MarqueeLabel {
id: artistLabel
Layout.fillWidth: true
// if no artist is given, show player name instead
inputText: model.artist || model.identity || ""
textFormat: Text.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
opacity: 0.9
}
}
QQC2.ToolButton {
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
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.goPrevious();
}
visible: model.canGoPrevious || model.canGoNext
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Previous track")
}
QQC2.ToolButton {
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
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.playPause();
}
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Play or Pause media")
}
QQC2.ToolButton {
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
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
}
}
}
}
}
}
}
}
}