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