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 }