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: Button {
0251                             action: modelData
0252                             visible: action.visible
0253                             hoverEnabled: true
0254                             flat: false
0255 
0256                             display: AbstractButton.IconOnly
0257 
0258                             ToolTip.visible: hovered
0259                             ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
0260                             ToolTip.text: action.text
0261 
0262                             Accessible.name: ToolTip.text
0263                             Accessible.description: ToolTip.text
0264                             Accessible.onPressAction: onClicked
0265 
0266                             Keys.onReturnPressed: action.trigger()
0267                             Keys.onEnterPressed: action.trigger()
0268                         }
0269                     }
0270                 }
0271             }
0272 
0273 
0274             // labels
0275             RowLayout {
0276                 id: labels
0277                 anchors.left: parent.left
0278                 anchors.right: parent.right
0279                 anchors.bottom: parent.bottom
0280                 anchors.top: coverImageLoader.bottom
0281 
0282                 ColumnLayout {
0283                     Layout.alignment: Qt.AlignVCenter
0284                     Layout.fillWidth: true
0285                     spacing: 0
0286 
0287                     LabelWithToolTip {
0288                         id: mainLabel
0289                         text: gridEntry.mainText
0290 
0291                         level: Kirigami.Settings.isMobile ? 6 : 4
0292 
0293                         color: myPalette.text
0294 
0295                         // FIXME: Center-aligned text looks better overall, but
0296                         // sometimes results in font kerning issues
0297                         // See https://bugreports.qt.io/browse/QTBUG-49646
0298                         horizontalAlignment: Kirigami.Settings.isMobile ? Text.AlignLeft : Text.AlignHCenter
0299 
0300                         Layout.fillWidth: true
0301                         Layout.maximumHeight: delegateDisplaySecondaryText ? mainLabelSize.boundingRect.height : mainLabelSize.boundingRect.height * 2
0302                         Layout.alignment: Kirigami.Settings.isMobile ? Qt.AlignLeft : Qt.AlignVCenter
0303                         Layout.leftMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0304                         Layout.rightMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0305 
0306                         wrapMode: !Kirigami.Settings.isMobile && delegateDisplaySecondaryText ? Label.NoWrap : Label.Wrap
0307                         maximumLineCount: Kirigami.Settings.isMobile ? 1 : 2
0308                         elide: Text.ElideRight
0309                     }
0310 
0311                     LabelWithToolTip {
0312                         id: secondaryLabel
0313                         visible: delegateDisplaySecondaryText
0314                         text: gridEntry.secondaryText
0315 
0316                         opacity: 0.6
0317                         color: myPalette.text
0318 
0319                         // FIXME: Center-aligned text looks better overall, but
0320                         // sometimes results in font kerning issues
0321                         // See https://bugreports.qt.io/browse/QTBUG-49646
0322                         horizontalAlignment: Kirigami.Settings.isMobile ? Text.AlignLeft : Text.AlignHCenter
0323 
0324                         Layout.fillWidth: true
0325                         Layout.alignment: Kirigami.Settings.isMobile ? Qt.AlignLeft : Qt.AlignVCenter
0326                         Layout.topMargin: Kirigami.Settings.isMobile ? Kirigami.Units.smallSpacing : 0
0327                         Layout.leftMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0328                         Layout.rightMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing
0329 
0330                         maximumLineCount: Kirigami.Settings.isMobile ? 1 : -1
0331                         elide: Text.ElideRight
0332                         font: Kirigami.Settings.isMobile ? Kirigami.Theme.smallFont : Kirigami.Theme.defaultFont
0333                     }
0334                 }
0335 
0336                 // mobile context menu button
0337                 FlatButtonWithToolTip {
0338                     id: contextMenuButton
0339                     visible: Kirigami.Settings.isMobile
0340                     scale: LayoutMirroring.enabled ? -1 : 1
0341                     Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
0342                     Layout.preferredHeight: Math.round(Kirigami.Units.gridUnit * 2.5)
0343                     Layout.preferredWidth: Math.round(Kirigami.Units.gridUnit * 1.5)
0344 
0345                     text: i18nc("@action:button", "Options")
0346                     icon.name: "view-more-symbolic"
0347                     onClicked: openContextMenu()
0348                 }
0349             }
0350         }
0351 
0352         Loader {
0353             active: isPartial
0354             anchors.centerIn: parent
0355             height: Kirigami.Units.gridUnit * 5
0356             width: height
0357 
0358             sourceComponent: BusyIndicator {
0359                 anchors.centerIn: parent
0360                 running: true
0361             }
0362         }
0363     }
0364 
0365     // mobile context menu sheet
0366     Loader {
0367         id: contextMenuLoader
0368         active: false
0369 
0370         sourceComponent: Kirigami.MenuDialog {
0371             id: contextMenu
0372             title: gridEntry.mainText
0373             preferredWidth: Kirigami.Units.gridUnit * 20
0374 
0375             actions: gridEntry.actions
0376         }
0377     }
0378 }