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

0001 /*
0002  * SPDX-FileCopyrightText: 2023 George Florea Bănuș <georgefb899@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 import QtQuick
0008 import QtQuick.Controls
0009 import QtQuick.Layouts
0010 import Qt5Compat.GraphicalEffects
0011 import Qt.labs.platform as Platform
0012 
0013 import org.kde.kirigami as Kirigami
0014 import org.kde.haruna
0015 import org.kde.haruna.settings
0016 
0017 Item {
0018     id: root
0019 
0020     property alias scrollPositionTimer: scrollPositionTimer
0021     property alias playlistView: playlistView
0022 
0023     height: mpv.height
0024     width: {
0025         if (PlaylistSettings.style === "compact") {
0026             return Kirigami.Units.gridUnit * 21
0027         } else {
0028             const w = Kirigami.Units.gridUnit * 31
0029             return (window.width * 0.33) < w ? w : window.width * 0.33
0030         }
0031     }
0032     x: PlaylistSettings.position === "right" ? window.width : -width
0033     y: 0
0034     state: PlaylistSettings.rememberState
0035            ? (PlaylistSettings.visible ? "visible" : "hidden")
0036            : "hidden"
0037 
0038     onStateChanged: {
0039         PlaylistSettings.visible = state === "visible" ? true : false
0040         PlaylistSettings.save()
0041         if (state === "hidden") {
0042             contextMenuLoader.active = false
0043         }
0044     }
0045 
0046     Rectangle {
0047 
0048         Rectangle {
0049             z: 20
0050             width: 1
0051             height: parent.height
0052             color: {
0053                 let color = Kirigami.Theme.backgroundColor
0054                 Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, 0.12)
0055             }
0056         }
0057 
0058         anchors.fill: parent
0059         color: Kirigami.Theme.backgroundColor
0060 
0061         ScrollView {
0062             id: playlistScrollView
0063 
0064             z: 20
0065             anchors.fill: parent
0066             ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
0067 
0068             ListView {
0069                 id: playlistView
0070 
0071                 model: mpv.playlistProxyModel
0072                 spacing: 1
0073                 currentIndex: mpv.playlistProxyModel.getPlayingItem()
0074 
0075                 headerPositioning: ListView.OverlayHeader
0076                 header: ToolBar {
0077                     id: toolbar
0078 
0079                     z: 100
0080                     width: parent.width
0081 
0082                     InputPopup {
0083                         id: addUrlPopup
0084 
0085                         width: toolbar.width - Kirigami.Units.largeSpacing
0086                         buttonText: i18nc("@action:button", "Add")
0087 
0088                         onUrlOpened: function(url) {
0089                             mpv.playlistModel.addItem(url, PlaylistModel.Append)
0090                         }
0091                     }
0092 
0093                     RowLayout {
0094                         anchors.fill: parent
0095                         Kirigami.ActionToolBar {
0096                             actions: [
0097                                 Kirigami.Action {
0098                                     text: i18nc("@action:button", "Open playlist")
0099                                     icon.name: "media-playlist-append"
0100                                     onTriggered: {
0101                                         fileDialog.fileType = "playlist"
0102                                         fileDialog.fileMode = Platform.FileDialog.OpenFile
0103                                         fileDialog.open()
0104                                     }
0105                                 },
0106                                 Kirigami.Action {
0107                                     text: i18nc("@action:button", "Add ...")
0108                                     icon.name: "list-add"
0109                                     Kirigami.Action {
0110                                         text: i18nc("@action:button", "File")
0111                                         onTriggered: {
0112                                             fileDialog.fileType = "video"
0113                                             fileDialog.fileMode = Platform.FileDialog.OpenFile
0114                                             fileDialog.open()
0115                                         }
0116                                     }
0117                                     Kirigami.Action {
0118                                         text: i18nc("@action:button", "Url")
0119                                         onTriggered: {
0120                                             if (addUrlPopup.opened) {
0121                                                 addUrlPopup.close()
0122                                             } else {
0123                                                 addUrlPopup.open()
0124                                             }
0125                                         }
0126                                     }
0127                                 },
0128                                 Kirigami.Action {
0129                                     text: i18nc("@action:button", "Sort")
0130                                     icon.name: "view-sort"
0131 
0132                                     Kirigami.Action {
0133                                         text: i18nc("@action:button", "Name ascending")
0134                                         onTriggered: {
0135                                             mpv.playlistProxyModel.sortItems(PlaylistProxyModel.NameAscending)
0136                                         }
0137                                     }
0138                                     Kirigami.Action {
0139                                         text: i18nc("@action:button", "Name descending")
0140                                         onTriggered: {
0141                                             mpv.playlistProxyModel.sortItems(PlaylistProxyModel.NameDescending)
0142                                         }
0143                                     }
0144                                     Kirigami.Action {
0145                                         text: i18nc("@action:button", "Duration ascending")
0146                                         onTriggered: {
0147                                             mpv.playlistProxyModel.sortItems(PlaylistProxyModel.DurationAscending)
0148                                         }
0149                                     }
0150                                     Kirigami.Action {
0151                                         text: i18nc("@action:button", "Duration descending")
0152                                         onTriggered: {
0153                                             mpv.playlistProxyModel.sortItems(PlaylistProxyModel.DurationDescending)
0154                                         }
0155                                     }
0156                                 },
0157                                 Kirigami.Action {
0158                                     text: i18nc("@action:button", "Clear")
0159                                     icon.name: "edit-clear-all"
0160                                     displayHint: Kirigami.DisplayHint.AlwaysHide
0161                                     onTriggered: {
0162                                         mpv.playlistModel.clear()
0163                                     }
0164                                 },
0165                                 Kirigami.Action {
0166                                     text: i18nc("@action:button", "Save as")
0167                                     icon.name: "document-save-as"
0168                                     displayHint: Kirigami.DisplayHint.AlwaysHide
0169                                     onTriggered: {
0170                                         fileDialog.fileType = "playlist"
0171                                         fileDialog.fileMode = Platform.FileDialog.SaveFile
0172                                         fileDialog.open()
0173                                     }
0174                                 }
0175                             ]
0176                         }
0177 
0178                     }
0179                 }
0180 
0181                 delegate: {
0182                     switch (PlaylistSettings.style) {
0183                     case "default":
0184                         playlistItemSimple
0185                         break
0186                     case "withThumbnails":
0187                         playlistItemWithThumbnail
0188                         break
0189                     case "compact":
0190                         playlistItemCompact
0191                         break
0192                     }
0193                 }
0194 
0195                 MouseArea {
0196                     anchors.fill: playlistView.contentItem
0197                     acceptedButtons: Qt.RightButton
0198                     onClicked: {
0199                         const item = playlistView.itemAt(mouseX, mouseY)
0200                         if (!item) {
0201                             return
0202                         }
0203 
0204                         contextMenuLoader.row = playlistView.indexAt(mouseX, mouseY)
0205                         contextMenuLoader.isLocal = item.isLocal
0206                         contextMenuLoader.active = true
0207                         contextMenuLoader.item.popup(item)
0208                     }
0209                 }
0210 
0211             }
0212         }
0213 
0214         Loader {
0215             id: contextMenuLoader
0216 
0217             property int row: -1
0218             property bool isLocal: false
0219 
0220             active: false
0221             sourceComponent: Menu {
0222 
0223                 MenuItem {
0224                     text: i18nc("@action:inmenu", "Open containing folder")
0225                     icon.name: "folder"
0226                     visible: contextMenuLoader.isLocal
0227                     onClicked: mpv.playlistProxyModel.highlightInFileManager(row)
0228                 }
0229                 MenuItem {
0230                     text: i18nc("@action:inmenu", "Copy file name")
0231                     onClicked: mpv.playlistProxyModel.copyFileName(row)
0232                 }
0233                 MenuItem {
0234                     text: i18nc("@action:inmenu", "Copy file path")
0235                     onClicked: mpv.playlistProxyModel.copyFilePath(row)
0236                 }
0237                 MenuSeparator {}
0238                 MenuItem {
0239                     text: i18nc("@action:inmenu", "Remove from playlist")
0240                     icon.name: "remove"
0241                     visible: contextMenuLoader.isLocal
0242                     onClicked: mpv.playlistProxyModel.removeItem(row)
0243                 }
0244                 MenuItem {
0245                     text: i18nc("@action:inmenu", "Rename file")
0246                     icon.name: "edit-rename"
0247                     visible: contextMenuLoader.isLocal
0248                     onClicked: mpv.playlistProxyModel.renameFile(row)
0249                 }
0250                 MenuItem {
0251                     text: i18nc("@action:inmenu", "Trash file")
0252                     icon.name: "delete"
0253                     visible: contextMenuLoader.isLocal && app.frameworksVersionMinor() >= 100
0254                     onClicked: mpv.playlistProxyModel.trashFile(row)
0255                 }
0256             }
0257         }
0258 
0259         Component {
0260             id: playlistItemWithThumbnail
0261             PlayListItemWithThumbnail {}
0262         }
0263 
0264         Component {
0265             id: playlistItemSimple
0266             PlayListItem {}
0267         }
0268 
0269         Component {
0270             id: playlistItemCompact
0271             PlayListItemCompact {}
0272         }
0273 
0274         // without a timer the scroll position is incorrect
0275         Timer {
0276             id: scrollPositionTimer
0277 
0278             interval: 100
0279             running: false
0280             repeat: false
0281 
0282             onTriggered: {
0283                 playlistView.positionViewAtIndex(playlistView.model.playingVideo, ListView.Beginning)
0284             }
0285         }
0286 
0287         ShaderEffectSource {
0288             id: shaderEffect
0289 
0290             visible: PlaylistSettings.overlayVideo
0291             anchors.fill: playlistScrollView
0292             sourceItem: mpv
0293             sourceRect: PlaylistSettings.position === "right"
0294                         ? Qt.rect(mpv.width - root.width, mpv.y, root.width, root.height)
0295                         : Qt.rect(0, 0, root.width, root.height)
0296         }
0297 
0298         FastBlur {
0299             visible: PlaylistSettings.overlayVideo
0300             anchors.fill: shaderEffect
0301             radius: 100
0302             source: shaderEffect
0303             z: 10
0304         }
0305     }
0306 
0307     Platform.FileDialog {
0308         id: fileDialog
0309 
0310         property url location: GeneralSettings.fileDialogLocation
0311                                ? app.pathToUrl(GeneralSettings.fileDialogLocation)
0312                                : app.pathToUrl(GeneralSettings.fileDialogLastLocation)
0313         property string fileType: "video"
0314 
0315         folder: location
0316         title: i18nc("@title:window", "Select file")
0317         fileMode: Platform.FileDialog.OpenFile
0318 
0319         onAccepted: {
0320             switch (fileType) {
0321             case "video":
0322                 mpv.playlistModel.addItem(fileDialog.file, PlaylistModel.Append)
0323                 break
0324             case "playlist":
0325                 if (fileMode === Platform.FileDialog.OpenFile) {
0326                     mpv.playlistModel.openM3uFile(fileDialog.file.toString())
0327                 } else {
0328                     mpv.playlistProxyModel.saveM3uFile(fileDialog.file.toString())
0329                 }
0330 
0331                 break
0332             }
0333         }
0334         onRejected: mpv.focus = true
0335     }
0336 
0337     states: [
0338         State {
0339             name: "hidden"
0340 
0341             PropertyChanges {
0342                 target: root;
0343                 x: PlaylistSettings.position === "right" ? window.width : -width
0344             }
0345 
0346             PropertyChanges {
0347                 target: root;
0348                 visible: false
0349             }
0350         },
0351         State {
0352             name : "visible"
0353 
0354             PropertyChanges {
0355                 target: root;
0356                 x: PlaylistSettings.position === "right" ? window.width - root.width : 0
0357             }
0358 
0359             PropertyChanges {
0360                 target: root;
0361                 visible: true
0362             }
0363         }
0364     ]
0365 
0366     transitions: [
0367         Transition {
0368             from: "visible"
0369             to: "hidden"
0370 
0371             SequentialAnimation {
0372                 NumberAnimation {
0373                     target: root
0374                     property: "x"
0375                     duration: 150
0376                     easing.type: Easing.InQuad
0377                 }
0378 
0379                 PropertyAction {
0380                     target: root
0381                     property: "visible"
0382                     value: false
0383                 }
0384             }
0385         },
0386         Transition {
0387             from: "hidden"
0388             to: "visible"
0389 
0390             SequentialAnimation {
0391                 PropertyAction {
0392                     target: root
0393                     property: "visible"
0394                     value: true
0395                 }
0396 
0397                 NumberAnimation {
0398                     target: root
0399                     property: "x"
0400                     duration: 150
0401                     easing.type: Easing.OutQuad
0402                 }
0403             }
0404         }
0405     ]
0406 }