Warning, /multimedia/elisa/src/qml/PlayListEntry.qml is written in an unsupported language. File is not indexed.
0001 /*
0002 SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003
0004 SPDX-License-Identifier: LGPL-3.0-or-later
0005 */
0006
0007 import QtQuick 2.7
0008 import QtQuick.Layouts 1.2
0009 import QtQuick.Controls 2.3
0010 import QtQuick.Window 2.2
0011 import org.kde.kirigami 2.17 as Kirigami
0012 import org.kde.elisa 1.0
0013
0014 import "shared"
0015
0016 BasePlayListDelegate {
0017 id: playListEntry
0018
0019 property bool editingRating: false
0020 // wideMode means there is enough room for the button row
0021 // otherwise display a menu button
0022 readonly property bool wideMode: width >= elisaTheme.playListEntryMinWidth
0023
0024 property var listDelegate
0025
0026 readonly property string previousAlbum: listDelegate.ListView.previousSection ? JSON.parse(listDelegate.ListView.previousSection)[0] : null
0027 readonly property string currentAlbum: model.album || null
0028 readonly property string nextAlbum: listDelegate.ListView.nextSection ? JSON.parse(listDelegate.ListView.nextSection)[0] : null
0029
0030 readonly property bool grouped: currentAlbum && (previousAlbum === currentAlbum || nextAlbum === currentAlbum)
0031 readonly property bool sectionVisible: currentAlbum && (previousAlbum !== currentAlbum && nextAlbum === currentAlbum)
0032
0033 readonly property bool hasActiveFocus: playListEntry.activeFocus || entryFocusScope.activeFocus
0034
0035 Accessible.role: Accessible.ListItem
0036 Accessible.name: title + ' ' + album + ' ' + artist
0037
0038 Keys.onReturnPressed: {
0039 playListEntry.switchToTrack(playListEntry.index)
0040 playListEntry.startPlayback()
0041 }
0042
0043 KeyNavigation.right: (buttonRowLoader.item && buttonRowLoader.item.children[0]) || menuButtonLoader.item || null
0044 KeyNavigation.left: (buttonRowLoader.item && buttonRowLoader.item.children[buttonRowLoader.item.children.length -1]) || menuButtonLoader.item || null
0045
0046 topPadding: grouped ? 0 : Kirigami.Units.smallSpacing
0047 bottomPadding: grouped ? 0 : Kirigami.Units.smallSpacing
0048 leftPadding: mirrored ? Kirigami.Units.smallSpacing : 0
0049 rightPadding: mirrored ? 0 : Kirigami.Units.smallSpacing
0050
0051 // Set this explicitly since we rely on it; we don't want the theme setting
0052 // it to false under us!
0053 hoverEnabled: true
0054
0055 onHasActiveFocusChanged: {
0056 if (!hasActiveFocus) {
0057 editingRating = false
0058 }
0059 }
0060
0061 contentItem: FocusScope {
0062 id: entryFocusScope
0063 implicitHeight: childrenRect.height
0064
0065 Loader {
0066 id: metadataLoader
0067 active: false
0068 onLoaded: item.show()
0069
0070 sourceComponent: MediaTrackMetadataView {
0071 fileName: playListEntry.fileName
0072 showImage: entryType !== ElisaUtils.Radio
0073 modelType: entryType
0074 showTrackFileName: entryType !== ElisaUtils.Radio
0075 showDeleteButton: entryType === ElisaUtils.Radio
0076 editableMetadata: playListEntry.metadataModifiableRole
0077 canAddMoreMetadata: entryType !== ElisaUtils.Radio
0078
0079 onRejected: metadataLoader.active = false
0080 }
0081 }
0082
0083 QtObject {
0084 id: actionList
0085 property var locateFileAction: Kirigami.Action {
0086 text: i18nc("@action:button Show the file for this song in the file manager", "Show in folder")
0087 icon.name: "document-open-folder"
0088 visible: playListEntry.fileName.toString().substring(0, 7) === 'file://'
0089 enabled: isValid
0090 onTriggered: ElisaApplication.showInFolder(playListEntry.fileName)
0091 }
0092 property var infoAction: Kirigami.Action {
0093 text: i18nc("@action:button Show track metadata", "View details")
0094 icon.name: "help-about"
0095 enabled: isValid
0096 onTriggered: {
0097 if (metadataLoader.active === false) {
0098 metadataLoader.active = true
0099 }
0100 else {
0101 metadataLoader.item.close();
0102 metadataLoader.active = false
0103 }
0104 }
0105 }
0106 property var ratingAction: Kirigami.Action {
0107 text: i18nc("@action:button", "Set track rating")
0108 icon.name: "view-media-favorite"
0109 visible: !ElisaApplication.useFavoriteStyleRatings
0110 enabled: isValid
0111
0112 onTriggered: {
0113 playListEntry.editingRating = true;
0114 }
0115 }
0116 property var favoriteAction: Kirigami.Action {
0117 text: playListEntry.isFavorite ? i18nc("@action:button", "Un-mark this song as a favorite") : i18nc("@action:button", "Mark this song as a favorite")
0118 icon.name: playListEntry.isFavorite ? "rating" : "rating-unrated"
0119 visible: ElisaApplication.useFavoriteStyleRatings
0120 enabled: isValid
0121
0122 onTriggered: {
0123 const newRating = playListEntry.isFavorite ? 0 : 10;
0124 // Change icon immediately in case backend is slow
0125 icon.name = playListEntry.isFavorite ? "rating-unrated" : "rating";
0126 ElisaApplication.musicManager.updateSingleFileMetaData(playListEntry.fileName, DataTypes.RatingRole, newRating);
0127 }
0128 }
0129 property var playPauseAction: Kirigami.Action {
0130 text: (isPlaying === MediaPlayList.IsPlaying) ? i18nc("@action:button Pause current track from playlist", "Pause") : i18nc("@action:button Play this track from playlist", "Play")
0131 icon.name: (isPlaying === MediaPlayList.IsPlaying) ? "media-playback-pause" : "media-playback-start"
0132 enabled: isValid
0133 onTriggered: {
0134 if (isPlaying === MediaPlayList.IsPlaying) {
0135 playListEntry.pausePlayback()
0136 } else if (isPlaying === MediaPlayList.IsPaused) {
0137 playListEntry.startPlayback()
0138 } else {
0139 playListEntry.switchToTrack(playListEntry.index)
0140 playListEntry.startPlayback()
0141 }
0142 }
0143 }
0144 property var removeAction: Kirigami.Action {
0145 text: i18nc("@action:button Remove current track from play list", "Remove")
0146 icon.name: "edit-delete-remove"
0147 onTriggered: {
0148 playListEntry.removeFromPlaylist(playListEntry.index)
0149 }
0150 }
0151 }
0152
0153 RowLayout {
0154 id: trackRow
0155
0156 width: parent.width
0157 // We want the list items to be a bit taller in touch mode
0158 height: Math.max(
0159 (playListEntry.grouped && !Kirigami.Settings.hasTransientTouchInput ? Kirigami.Units.gridUnit : Kirigami.Units.gridUnit * 2),
0160 (elisaTheme.toolButtonHeight + Kirigami.Units.smallSpacing))
0161
0162 spacing: Kirigami.Units.smallSpacing
0163
0164 Loader {
0165 Layout.leftMargin: trackRow.spacing
0166 active: !simpleMode && playListEntry.showDragHandle
0167 sourceComponent: Kirigami.ListItemDragHandle {
0168 listItem: playListEntry
0169 listView: playListEntry.listView
0170
0171 onMoveRequested: (oldIndex, newIndex) => {
0172 playListEntry.listView.draggingEntry = true
0173 ElisaApplication.mediaPlayListProxyModel.moveRow(oldIndex, newIndex)
0174 }
0175 onDropped: {
0176 playListEntry.listView.draggingEntry = false
0177 }
0178 }
0179 }
0180
0181 // Container for the play/pause icon and the track/disc label
0182 Item {
0183 Layout.preferredWidth: Math.max(elisaTheme.trackNumberWidth, elisaTheme.coverArtSize)
0184 Layout.fillHeight: true
0185
0186 Loader {
0187 active: !playListEntry.grouped
0188 opacity: playIcon.visible ? 0.2 : 1
0189 anchors.fill: parent
0190
0191 sourceComponent: ImageWithFallback {
0192 source: imageUrl
0193 fallback: elisaTheme.defaultAlbumImage
0194
0195 sourceSize.width: height
0196 sourceSize.height: height
0197
0198 fillMode: Image.PreserveAspectFit
0199 asynchronous: true
0200 }
0201 }
0202
0203 Kirigami.Icon {
0204 id: playIcon
0205
0206 anchors.centerIn: parent
0207
0208 source: (isPlaying === MediaPlayList.IsPlaying ?
0209 Qt.resolvedUrl(elisaTheme.playingIndicatorIcon) : Qt.resolvedUrl(elisaTheme.pausedIndicatorIcon))
0210
0211 width: Kirigami.Units.iconSizes.smallMedium
0212 height: Kirigami.Units.iconSizes.smallMedium
0213
0214 visible: isPlaying === MediaPlayList.IsPlaying || isPlaying === MediaPlayList.IsPaused
0215
0216 isMask: simpleMode
0217 color: Kirigami.Theme.textColor
0218 }
0219
0220 Loader {
0221 active: playListEntry.grouped && isValid && !playIcon.visible
0222 anchors.fill: parent
0223
0224 sourceComponent: Label {
0225 //trackAndDiscNumberLabel
0226
0227 horizontalAlignment: Text.AlignRight
0228 verticalAlignment: Text.AlignVCenter
0229
0230 text: {
0231 var trackNumberString;
0232 if (trackNumber !== -1) {
0233 trackNumberString = Number(trackNumber).toLocaleString(Qt.locale(), 'f', 0);
0234 } else {
0235 trackNumberString = ''
0236 }
0237 if (!isSingleDiscAlbum && discNumber !== 0 ) {
0238 return trackNumberString + "/" + Number(discNumber).toLocaleString(Qt.locale(), 'f', 0)
0239 } else {
0240 return trackNumberString
0241 }
0242 }
0243 textFormat: Text.PlainText
0244 font.weight: (isPlaying ? Font.Bold : Font.Normal)
0245 }
0246 }
0247 }
0248
0249 ColumnLayout {
0250 spacing: 0
0251 Layout.fillWidth: true
0252
0253 LabelWithToolTip {
0254 text: title
0255 font.weight: isPlaying ? Font.Bold : Font.Normal
0256 visible: isValid
0257 Layout.fillWidth: true
0258 }
0259
0260 Loader {
0261 active: !playListEntry.grouped && (artist || album)
0262 visible: active
0263 Layout.fillWidth: true
0264 sourceComponent: LabelWithToolTip {
0265 text: [artist, album].filter(Boolean).join(" - ")
0266 type: Kirigami.Heading.Type.Secondary
0267 }
0268 }
0269 }
0270
0271 // button row
0272 Loader {
0273 id: buttonRowLoader
0274
0275 active: false
0276 visible: active && !playListEntry.editingRating
0277
0278 sourceComponent: Row {
0279 Repeater {
0280 model: [
0281 actionList.locateFileAction,
0282 actionList.infoAction,
0283 actionList.playPauseAction,
0284 actionList.removeAction,
0285 actionList.ratingAction,
0286 actionList.favoriteAction
0287 ]
0288 delegate: FlatButtonWithToolTip {
0289 action: modelData
0290 visible: action.visible
0291 activeFocusOnTab: playListEntry.isSelected
0292 }
0293 }
0294 }
0295 }
0296
0297 FlatButtonWithToolTip {
0298 visible: playListEntry.editingRating && playListEntry.wideMode
0299 text: i18nc("@action:button", "Cancel rating this track")
0300 icon.name: "dialog-cancel"
0301 onClicked: { playListEntry.editingRating = false; }
0302 }
0303
0304 RatingStar {
0305 id: ratingWidget
0306
0307 readOnly: !playListEntry.editingRating
0308 starRating: rating
0309
0310 visible: (playListEntry.editingRating || (rating > 0 && !playListEntry.hovered && !playListEntry.hasActiveFocus && !simpleMode && !ElisaApplication.useFavoriteStyleRatings)) && playListEntry.wideMode
0311
0312 onRatingEdited: {
0313 ElisaApplication.musicManager.updateSingleFileMetaData(playListEntry.fileName, DataTypes.RatingRole, starRating);
0314 playListEntry.editingRating = false;
0315 }
0316 }
0317
0318 Loader {
0319 id: favoriteMark
0320
0321 visible: playListEntry.isFavorite && !playListEntry.hovered && !playListEntry.hasActiveFocus && !simpleMode && ElisaApplication.useFavoriteStyleRatings
0322
0323 sourceComponent: FlatButtonWithToolTip {
0324 visible: action.visible
0325 action: actionList.favoriteAction
0326 }
0327 }
0328
0329
0330 LabelWithToolTip {
0331 id: durationLabel
0332 text: duration
0333 font.weight: isPlaying ? Font.Bold : Font.Normal
0334 }
0335
0336 Loader {
0337 id: menuButtonLoader
0338 active: !playListEntry.wideMode || Kirigami.Settings.hasTransientTouchInput
0339 visible: active
0340 sourceComponent: FlatButtonWithToolTip {
0341 icon.name: "overflow-menu"
0342 text: entryType === ElisaUtils.Track ? i18nc("@action:button", "Track Options") : i18nc("@action:button", "Radio Options")
0343 down: pressed || menuLoader.menuVisible
0344 onPressed: menuLoader.item.open()
0345 Keys.onReturnPressed: menuLoader.item.open()
0346 Keys.onEnterPressed: menuLoader.item.open()
0347 activeFocusOnTab: playListEntry.isSelected
0348 }
0349 }
0350 Loader {
0351 id: menuLoader
0352 property bool menuVisible: false
0353 active: menuButtonLoader.active
0354 onActiveChanged: {
0355 if (!active) {
0356 menuVisible = false
0357 }
0358 }
0359 sourceComponent: Menu {
0360 dim: false
0361 modal: true
0362 x: -width
0363 parent: menuButtonLoader.item
0364
0365 onVisibleChanged: menuLoader.menuVisible = visible
0366
0367 MenuItem {
0368 action: actionList.playPauseAction
0369 }
0370 MenuItem {
0371 action: actionList.infoAction
0372 }
0373 MenuItem {
0374 action: actionList.locateFileAction
0375 }
0376 MenuItem {
0377 action: actionList.ratingAction
0378 visible: action.visible
0379 }
0380 MenuItem {
0381 action: actionList.favoriteAction
0382 visible: action.visible
0383 }
0384 MenuSeparator { }
0385 MenuItem {
0386 action: actionList.removeAction
0387 }
0388 }
0389 }
0390 }
0391
0392 states: [
0393 State {
0394 name: "dragging"
0395 when: playListEntry.listView.draggingEntry
0396 },
0397 State {
0398 name: 'hovered'
0399 when: playListEntry.hovered && !playListEntry.hasActiveFocus && !simpleMode
0400 PropertyChanges {
0401 target: buttonRowLoader
0402 active: playListEntry.wideMode
0403 }
0404 },
0405 State {
0406 name: 'focused'
0407 when: playListEntry.hasActiveFocus && !simpleMode
0408 PropertyChanges {
0409 target: buttonRowLoader
0410 active: playListEntry.wideMode
0411 }
0412 }
0413 ]
0414 }
0415 }