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 }