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 }