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 }