Warning, /multimedia/elisa/src/qml/GridBrowserDelegate.qml is written in an unsupported language. File is not indexed.

0001 /*
0002    SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003    SPDX-FileCopyrightText: 2021 (c) Devin Lin <espidev@gmail.com>
0004 
0005    SPDX-License-Identifier: LGPL-3.0-or-later
0006  */
0007 
0008 import QtQuick 2.15
0009 import QtQuick.Controls 2.2
0010 import QtQuick.Window 2.2
0011 import QtQml.Models 2.1
0012 import QtQuick.Layouts 1.2
0013 import Qt5Compat.GraphicalEffects
0014 import org.kde.kirigami 2.19 as Kirigami
0015 import org.kde.elisa 1.0
0016 
0017 import "mobile"
0018 
0019 FocusScope {
0020     id: gridEntry
0021 
0022     property url imageUrl
0023     property url imageFallbackUrl
0024     property var multipleImageUrls
0025     property url fileUrl
0026     property var entryType
0027     property string mainText
0028     property string secondaryText
0029     property var databaseId
0030     property bool delegateDisplaySecondaryText: true
0031     property bool isPartial
0032     property bool isSelected
0033     property bool hasChildren: true
0034 
0035     signal enqueue()
0036     signal replaceAndPlay()
0037     signal open()
0038     signal selected()
0039 
0040     property bool showPlayButton: true
0041     property bool showEnqueueButton: true
0042 
0043     property color stateIndicatorColor: {
0044         if (gridEntry.activeFocus || hoverHandle.pressed || hoverHandle.containsMouse) {
0045             return myPalette.highlight;
0046         } else if (gridEntry.isSelected && !Kirigami.Settings.isMobile) {
0047             return myPalette.mid;
0048         } else {
0049             return "transparent";
0050         }
0051     }
0052     property real stateIndicatorOpacity: {
0053         if ((!Kirigami.Settings.isMobile && gridEntry.activeFocus) ||
0054             (!Kirigami.Settings.isMobile && gridEntry.isSelected) || hoverHandle.pressed || hoverHandle.containsMouse) {
0055             return 0.3;
0056         } else {
0057             return 0;
0058         }
0059     }
0060 
0061     property list<Kirigami.Action> actions: [
0062         Kirigami.Action {
0063             text: i18nc("@action:button", "Play now, replacing current playlist")
0064             icon.name: Qt.application.layoutDirection !== Qt.RightToLeft ? "media-playback-start-symbolic"
0065                                                                          : "media-playback-start-symbolic-rtl"
0066             visible: gridEntry.showPlayButton
0067             onTriggered: gridEntry.replaceAndPlay()
0068         },
0069         Kirigami.Action {
0070             text: i18nc("@action:button", "Add to playlist")
0071             icon.name: 'list-add'
0072             visible: gridEntry.showEnqueueButton
0073             onTriggered: gridEntry.enqueue()
0074         },
0075         Kirigami.Action {
0076             visible: fileUrl.toString().substring(0, 7) === 'file://' && Kirigami.Settings.isMobile
0077             onTriggered: ElisaApplication.showInFolder(gridEntry.fileUrl)
0078             icon.name: "document-open-folder"
0079             text: i18nc("@action:button Show the file for this song in the file manager", "Show in folder")
0080         }
0081     ]
0082 
0083     Loader {
0084         id: metadataLoader
0085         active: false && gridEntry.fileUrl
0086         onLoaded: item.show()
0087 
0088         sourceComponent: MediaTrackMetadataView {
0089             fileName: gridEntry.fileUrl ? gridEntry.fileUrl : ''
0090             showImage: true
0091             modelType: gridEntry.entryType
0092             showTrackFileName: true
0093             showDeleteButton: false
0094             editableMetadata: false
0095             canAddMoreMetadata: false
0096 
0097             onRejected: metadataLoader.active = false;
0098         }
0099     }
0100 
0101     property bool delegateLoaded: true
0102 
0103     ListView.onPooled: delegateLoaded = false
0104     ListView.onReused: delegateLoaded = true
0105 
0106     // open mobile context menu
0107     function openContextMenu() {
0108         contextMenuLoader.active = true;
0109         contextMenuLoader.item.open();
0110     }
0111 
0112     Keys.onReturnPressed: open()
0113     Keys.onEnterPressed: open()
0114 
0115     Accessible.role: Accessible.ListItem
0116     Accessible.name: mainText
0117 
0118     Item {
0119         id: parentItem
0120         anchors.fill: parent
0121         // mobile uses more spacing between delegates
0122         anchors.margins: Kirigami.Settings.isMobile ? Kirigami.Units.largeSpacing : 0
0123 
0124         // highlight colour
0125         Rectangle {
0126             id: stateIndicator
0127 
0128             z: Kirigami.Settings.isMobile ? 1 : 0 // on desktop, we want hover actions to be above highlight
0129 
0130             anchors.fill: parent
0131             // expand margins of highlight box on mobile, so that it doesn't look like it's clipping the text
0132             anchors.leftMargin: Kirigami.Settings.isMobile ? -Kirigami.Units.smallSpacing : 0
0133             anchors.rightMargin: Kirigami.Settings.isMobile ? -Kirigami.Units.smallSpacing : 0
0134             anchors.bottomMargin: Kirigami.Settings.isMobile ? -Kirigami.Units.smallSpacing : 0
0135             color: stateIndicatorColor
0136             opacity: stateIndicatorOpacity
0137             radius: Kirigami.Settings.isMobile ? Kirigami.Units.smallSpacing : 3
0138         }
0139 
0140         // click handler
0141         MouseArea {
0142             id: hoverHandle
0143 
0144             anchors.fill: parent
0145             // fix mousearea from stealing swipes from flickable
0146             propagateComposedEvents: false
0147             onReleased: {
0148                 if (!propagateComposedEvents) {
0149                     propagateComposedEvents = true
0150                 }
0151             }
0152 
0153             hoverEnabled: true
0154 
0155             onClicked: hasChildren ? open() : enqueue()
0156 
0157             TextMetrics {
0158                 id: mainLabelSize
0159                 font: mainLabel.font
0160                 text: mainLabel.text
0161             }
0162 
0163             component CoverImage: ImageWithFallback {
0164                 id: coverImage
0165                 property var imageSource
0166 
0167                 sourceSize.width: width * Screen.devicePixelRatio
0168                 sourceSize.height: height * Screen.devicePixelRatio
0169                 fillMode: Image.PreserveAspectFit
0170 
0171                 source: imageSource ? imageSource : ""
0172                 fallback: gridEntry.imageFallbackUrl
0173 
0174                 asynchronous: true
0175 
0176                 layer.enabled: !coverImage.usingFallback && !Kirigami.Settings.isMobile // don't use drop shadow on mobile
0177                 layer.effect: DropShadow {
0178                     source: coverImage
0179                     radius: 16
0180                     samples: (radius * 2) + 1
0181                     cached: true
0182                     color: myPalette.shadow
0183                 }
0184             }
0185 
0186             Component {
0187                 id: quartersCover
0188                 Grid {
0189                     rows: 2
0190                     columns: 2
0191                     component QuarterImage: CoverImage {
0192                         width: parent.width / 2
0193                         height: parent.height / 2
0194                     }
0195 
0196                     QuarterImage {imageSource: gridEntry.multipleImageUrls[0]}
0197                     QuarterImage {imageSource: gridEntry.multipleImageUrls[1]}
0198                     QuarterImage {imageSource: gridEntry.multipleImageUrls[2]}
0199                     QuarterImage {imageSource: gridEntry.multipleImageUrls[3]}
0200                 }
0201             }
0202 
0203             Component {
0204                 id: singleCover
0205                 CoverImage {
0206                     width: parent.width
0207                     height: parent.height
0208 
0209                     imageSource: gridEntry.imageUrl
0210                 }
0211             }
0212 
0213             // cover image
0214             Loader {
0215                 id: coverImageLoader
0216                 anchors.top: parent.top
0217                 anchors.left: parent.left
0218                 anchors.right: parent.right
0219                 anchors.margins: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0220                 width: gridEntry.width - 2 * Kirigami.Units.largeSpacing
0221                 height: gridEntry.width - 2 * Kirigami.Units.largeSpacing
0222 
0223                 active: gridEntry.delegateLoaded && !isPartial
0224 
0225                 sourceComponent: gridEntry.multipleImageUrls && gridEntry.multipleImageUrls.length == 4
0226                                   ? quartersCover : singleCover
0227             }
0228 
0229             // ========== desktop hover actions ==========
0230             Loader {
0231                 id: hoverLoader
0232                 visible: !Kirigami.Settings.isMobile
0233                 active: gridEntry.activeFocus || hoverHandle.containsMouse
0234 
0235                 anchors {
0236                     bottom: parent.bottom
0237                     bottomMargin: labels.height
0238                     left: parent.left
0239                     leftMargin: 2 + Kirigami.Units.largeSpacing
0240                 }
0241 
0242                 opacity: gridEntry.activeFocus || hoverHandle.containsMouse
0243 
0244                 sourceComponent: Row {
0245                     spacing: 2
0246 
0247                     Repeater {
0248                         model: gridEntry.actions
0249 
0250                         delegate: ButtonWithToolTip {
0251                             action: modelData
0252                             visible: action.visible
0253                             hoverEnabled: true
0254                             display: AbstractButton.IconOnly
0255                         }
0256                     }
0257                 }
0258             }
0259 
0260 
0261             // labels
0262             RowLayout {
0263                 id: labels
0264                 anchors.left: parent.left
0265                 anchors.right: parent.right
0266                 anchors.bottom: parent.bottom
0267                 anchors.top: coverImageLoader.bottom
0268 
0269                 ColumnLayout {
0270                     Layout.alignment: Qt.AlignVCenter
0271                     Layout.fillWidth: true
0272                     spacing: 0
0273 
0274                     LabelWithToolTip {
0275                         id: mainLabel
0276                         text: gridEntry.mainText
0277 
0278                         level: Kirigami.Settings.isMobile ? 6 : 4
0279 
0280                         color: myPalette.text
0281 
0282                         // FIXME: Center-aligned text looks better overall, but
0283                         // sometimes results in font kerning issues
0284                         // See https://bugreports.qt.io/browse/QTBUG-49646
0285                         horizontalAlignment: Kirigami.Settings.isMobile ? Text.AlignLeft : Text.AlignHCenter
0286 
0287                         Layout.fillWidth: true
0288                         Layout.maximumHeight: delegateDisplaySecondaryText ? mainLabelSize.boundingRect.height : mainLabelSize.boundingRect.height * 2
0289                         Layout.alignment: Kirigami.Settings.isMobile ? Qt.AlignLeft : Qt.AlignVCenter
0290                         Layout.leftMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0291                         Layout.rightMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0292 
0293                         wrapMode: !Kirigami.Settings.isMobile && delegateDisplaySecondaryText ? Label.NoWrap : Label.Wrap
0294                         maximumLineCount: Kirigami.Settings.isMobile ? 1 : 2
0295                         elide: Text.ElideRight
0296                     }
0297 
0298                     LabelWithToolTip {
0299                         id: secondaryLabel
0300                         visible: delegateDisplaySecondaryText
0301                         text: gridEntry.secondaryText
0302 
0303                         opacity: 0.6
0304                         color: myPalette.text
0305 
0306                         // FIXME: Center-aligned text looks better overall, but
0307                         // sometimes results in font kerning issues
0308                         // See https://bugreports.qt.io/browse/QTBUG-49646
0309                         horizontalAlignment: Kirigami.Settings.isMobile ? Text.AlignLeft : Text.AlignHCenter
0310 
0311                         Layout.fillWidth: true
0312                         Layout.alignment: Kirigami.Settings.isMobile ? Qt.AlignLeft : Qt.AlignVCenter
0313                         Layout.topMargin: Kirigami.Settings.isMobile ? Kirigami.Units.smallSpacing : 0
0314                         Layout.leftMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0315                         Layout.rightMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0316 
0317                         maximumLineCount: Kirigami.Settings.isMobile ? 1 : -1
0318                         elide: Text.ElideRight
0319                         font: Kirigami.Settings.isMobile ? Kirigami.Theme.smallFont : Kirigami.Theme.defaultFont
0320                     }
0321                 }
0322 
0323                 // mobile context menu button
0324                 FlatButtonWithToolTip {
0325                     id: contextMenuButton
0326                     visible: Kirigami.Settings.isMobile
0327                     scale: LayoutMirroring.enabled ? -1 : 1
0328                     Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
0329                     Layout.preferredHeight: Math.round(Kirigami.Units.gridUnit * 2.5)
0330                     Layout.preferredWidth: Math.round(Kirigami.Units.gridUnit * 1.5)
0331 
0332                     text: i18nc("@action:button", "Options")
0333                     icon.name: "view-more-symbolic"
0334                     onClicked: openContextMenu()
0335                 }
0336             }
0337         }
0338 
0339         Loader {
0340             active: isPartial
0341             anchors.centerIn: parent
0342             height: Kirigami.Units.gridUnit * 5
0343             width: height
0344 
0345             sourceComponent: BusyIndicator {
0346                 anchors.centerIn: parent
0347                 running: true
0348             }
0349         }
0350     }
0351 
0352     // mobile context menu sheet
0353     Loader {
0354         id: contextMenuLoader
0355         active: false
0356 
0357         sourceComponent: Kirigami.MenuDialog {
0358             id: contextMenu
0359             title: gridEntry.mainText
0360             preferredWidth: Kirigami.Units.gridUnit * 20
0361 
0362             actions: gridEntry.actions
0363         }
0364     }
0365 }