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 }