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 }