Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  PlaylistMediaList.qml  Sprache: unbekannt

 
Spracherkennung für: .qml vermutete Sprache: Delphi {Delphi[54] SPS[54] ABAP[50]} [Methode: maximale Elemente, drei Dimensionen]

/*****************************************************************************
 * Copyright (C) 2021-23 VLC authors and VideoLAN
 *
 * Authors: Benjamin Arnaud <bunjee@omega.gg>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * ( at your option ) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

import QtQuick
import QtQuick.Layouts
import QtQml.Models

import VLC.MediaLibrary

import VLC.Widgets as Widgets
import VLC.MainInterface
import VLC.Util
import VLC.Style
import VLC.Dialogs

MainViewLoader {
    id: root

    Accessible.role: Accessible.List
    Accessible.name: qsTr("Playlist media list")
    AccessibleCompat.id: "playlistMediaList"

    //---------------------------------------------------------------------------------------------
    // Properties
    //---------------------------------------------------------------------------------------------

    property bool isMusic: false

    readonly property int currentIndex: currentItem?.currentIndex ?? -1

    property Component header: null

    readonly property int contentLeftMargin: currentItem?.contentLeftMargin ?? 0
    readonly property int contentRightMargin: currentItem?.contentRightMargin ?? 0

    property int displayMarginBeginning: 0
    property int displayMarginEnd: 0

    // Currently only respected by the list view:
    property bool enableBeginningFade: true
    property bool enableEndFade: true

    property alias searchPattern: playlistModel.searchPattern
    property alias sortOrder: playlistModel.sortOrder
    property alias sortCriteria: playlistModel.sortCriteria

    //---------------------------------------------------------------------------------------------
    // Private

    property string _placeHolder: (isMusic) ? VLCStyle.noArtAlbumCover
                                            : VLCStyle.noArtVideoCover

    // FIXME: remove this
    property var _currentView: currentItem

    //---------------------------------------------------------------------------------------------
    // Signals
    //---------------------------------------------------------------------------------------------

    signal showList(var model, int reason)

    //---------------------------------------------------------------------------------------------
    // Events
    //---------------------------------------------------------------------------------------------

    // NOTE: Define the initial position and selection. This is done on activeFocus rather than
    //       Component.onCompleted because selectionModel.selectedGroup update itself after this
    //       event.
    onActiveFocusChanged: {
        if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
            return;

        resetFocus()
    }

    Component.onCompleted: {
        root.Keys.deletePressed.connect(root.deleteSelectedPlaylists)
    }

    //---------------------------------------------------------------------------------------------
    // Private

    grid: grid
    list: table
    emptyLabel: emptyLabel

    isSearchable: true
    sortModel: [{ text: qsTr("Alphabetic"), criteria: "title" }]


    model: MLPlaylistListModel {
        id: playlistModel
        ml: MediaLib

        playlistType: isMusic ? MLPlaylistListModel.PLAYLIST_TYPE_AUDIO
                              : MLPlaylistListModel.PLAYLIST_TYPE_VIDEO

        coverSize: (isMusic) ? Qt.size(512, 512)
                             : Qt.size(1024, 640)

        coverDefault: root._placeHolder

        coverPrefix: (isMusic) ? "playlist-music" : "playlist-video"

        function onBusynessChanged() {
            if (transactionPending || loading) {
                MainCtx.setCursor(root, Qt.BusyCursor)
                visibilityTimer.start()
            } else {
                visibilityTimer.stop()
                progressIndicator.visible = false
                MainCtx.unsetCursor(root)
            }
        }

        Component.onCompleted: {
            playlistModel.transactionPendingChanged.connect(playlistModel.onBusynessChanged)
            playlistModel.loadingChanged.connect(playlistModel.onBusynessChanged)
            playlistModel.onBusynessChanged()
        }
    }

    function _actionAtIndex() {
        if (root.selectionModel.selectedIndexes.length > 1) {
            model.addAndPlay( selectionModel.selectedIndexes );
        } else if (root.selectionModel.selectedIndexes.length === 1) {
            const index = selectionModel.selectedIndexes[0];
            showList(model.getDataAt(index), Qt.TabFocusReason);
        }
    }

    function _adjustDragAccepted(drag) {
        if (!root.model || root.model.transactionPending)
        {
            drag.accepted = false
            return
        }

        if (drag.source !== dragItemPlaylist && Helpers.isValidInstanceOf(drag.source, Widgets.DragItem))
            drag.accepted = true
        else if (drag.hasUrls)
            drag.accepted = true
        else {
            drag.accepted = false
        }
    }

    function _dropAction(drop, index) {
        const item = drop.source
        if (Helpers.isValidInstanceOf(item, Widgets.DragItem)) {
            drop.accepted = true
            return item.getSelectedInputItem().then(inputItems => {
                if (index === undefined)
                    DialogsProvider.playlistsDialog(inputItems, (root.model as MLPlaylistListModel).playlistType)
                else
                    root.model.append(root.model.getItemId(index), inputItems)
            })
        } else if (drop.hasUrls) {
            drop.accepted = true
            const urlList = []
            for (let url in drop.urls)
                urlList.push(drop.urls[url])
            if (index === undefined)
                DialogsProvider.playlistsDialog(inputItems, (root.model as MLPlaylistListModel).playlistType)
            else
                root.model.append(root.model.getItemId(index), urlList)
        } else {
            drop.accepted = false
        }
        return Promise.resolve()
    }

    function deleteSelectedPlaylists() {
        console.assert(root.model)
        console.assert(root.selectionModel)
        if (root.selectionModel.hasSelection) {
            const items = []
            for (const i of root.selectionModel.selectedIndexes) {
                items.push(root.model.data(i, MLPlaylistListModel.PLAYLIST_ID))
            }
            console.assert(items.length > 0)
            if (DialogsProvider.questionDialog(qsTr("Do you really want to delete the selected playlist?",
                                                    "Do you really want to delete the selected playlists?",
                                                    items.length),
                                               qsTr("Delete playlist", "Delete playlists", items.length)))
                root.model.deletePlaylists(items)
        } else {
            console.warn(root, ": Nothing to delete")
        }
    }

    //---------------------------------------------------------------------------------------------
    // Childs
    //---------------------------------------------------------------------------------------------

    DropArea {
        anchors.fill: parent

        onEntered: function(drag) {
            root._adjustDragAccepted(drag)
        }

        onDropped: function(drop) {
            root._dropAction(drop)
        }
    }

    Widgets.ProgressIndicator {
        id: progressIndicator
        anchors.bottom: parent.bottom
        anchors.right: parent.right
        anchors.margins: VLCStyle.margin_small

        visible: false

        z: 99

        text: root.model?.transactionPending ? qsTr("Processing...") : ""

        Timer {
            id: visibilityTimer

            interval: VLCStyle.duration_humanMoment

            onTriggered: {
                progressIndicator.visible = true
            }
        }
    }

    MLDragItem {
        id: dragItemPlaylist

        objectName: "PlaylistMediaListDragItem"

        view: root.currentItem

        indexes: indexesFlat ? root.selectionModel.selectedIndexesFlat
                             : root.selectionModel.selectedIndexes
        indexesFlat: !!root.selectionModel.selectedIndexesFlat

        coverRole: "thumbnail"

        defaultCover: root._placeHolder
    }

    PlaylistListContextMenu {
        id: contextMenu

        model: root.model

        ctx: MainCtx

        function tableView_popup(index, selectedIndexes, globalPos) {
            popup(selectedIndexes, globalPos)
        }

        Component.onCompleted: {
            contextMenu.requestDeleteSelectedPlaylists.connect(root.deleteSelectedPlaylists)
        }
    }

    // TBD: Refactor this with MusicGenres ?
    Component {
        id: grid

        Widgets.ExpandGridItemView {
            id: gridView

            //-------------------------------------------------------------------------------------
            // Settings

            basePictureWidth: isMusic ? VLCStyle.gridCover_music_width : VLCStyle.gridCover_video_width
            basePictureHeight: isMusic ? VLCStyle.gridCover_music_height : VLCStyle.gridCover_video_height

            model: root.model

            selectionModel: root.selectionModel

            headerDelegate: root.header

            displayMarginBeginning: root.displayMarginBeginning
            displayMarginEnd: root.displayMarginEnd

            Navigation.parentItem: root

            delegate: VideoGridItem {
                //---------------------------------------------------------------------------------
                // Properties

                property var model: ({})

                property int index: -1

                //---------------------------------------------------------------------------------
                // Settings

                width: gridView.cellWidth;
                height: gridView.cellHeight;

                pictureWidth : gridView.maxPictureWidth
                pictureHeight: gridView.maxPictureHeight

                title: (model.name) ? model.name
                                    : qsTr("Unknown title")

                labels: [
                    (function () {
                        const labels = []
                        if (model.nb_audio > 0)
                            labels.push(qsTr("%1 Track", "%1 Tracks", model.nb_audio).arg(model.nb_audio))
                        if (model.nb_video > 0)
                            labels.push(qsTr("%1 Video", "%1 Videos", model.nb_video).arg(model.nb_video))
                        if (model.nb_unknown > 0)
                            labels.push(qsTr("%1 Other", "%1 Other", model.nb_unknown).arg(model.nb_unknown))

                        return labels.join("\n")
                    })()
                ]

                dragItem: dragItemPlaylist


                //---------------------------------------------------------------------------------
                // Events

                onItemClicked: (modifier, select) => { gridView.leftClickOnItem(modifier, index, select) }

                onItemDoubleClicked: showList(model, Qt.MouseFocusReason)

                onPlayClicked: if (model.id) MediaLib.addAndPlay(model.id)

                onContextMenuButtonClicked: (_, globalMousePos) => {
                    gridView.rightClickOnItem(index);

                    contextMenu.popup(selectionModel.selectedRows(), globalMousePos);
                }

                //---------------------------------------------------------------------------------
                // Animations

                Behavior on opacity {
                    OpacityAnimator {
                        duration: VLCStyle.duration_short
                    }
                }

                DropArea {
                    anchors.fill: parent

                    onEntered: function(drag) {
                        root._adjustDragAccepted(drag)
                    }

                    onDropped: function(drop) {
                        MainCtx.setCursor(gridView, Qt.BusyCursor)
                        root._dropAction(drop, index).then(() => {
                            if (gridView)
                                MainCtx.unsetCursor(gridView)
                        })
                    }
                }
            }

            //-------------------------------------------------------------------------------------
            // Events

            // NOTE: Define the initial position and selection. This is done on activeFocus rather
            //       than Component.onCompleted because selectionModel.selectedGroup update itself
            //       after this event.
            onActiveFocusChanged: {
                if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
                    return;

                selectionModel.select(model.index(0,0), ItemSelectionModel.ClearAndSelect)
            }

            onActionAtIndex: _actionAtIndex()

            //-------------------------------------------------------------------------------------
            // Childs

        }
    }

    Component {
        id: table

        Widgets.TableViewExt {
            id: tableView

            //-------------------------------------------------------------------------------------
            // Properties

            property var _modelSmall: [{
                weight: 1,

                model: {
                    criteria: "name",

                    subCriterias: [ "count" ],

                    text: qsTr("Name"),

                    headerDelegate: columns.titleHeaderDelegate,
                    colDelegate   : columns.titleDelegate
                }
            }]

            property var _modelMedium: [{
                weight: 1,

                model: {
                    criteria: "name",

                    text: qsTr("Name"),

                    headerDelegate: columns.titleHeaderDelegate,
                    colDelegate   : columns.titleDelegate
                }
            }, {
                size: 1,

                model: {
                    criteria: "count",

                    text: qsTr("Items"),

                    isSortable: false
                }
            }]

            //-------------------------------------------------------------------------------------
            // Settings

            rowHeight: VLCStyle.tableCoverRow_height

            model: root.model

            sortModel: (_availableRowWidth < VLCStyle.colWidth(4)) ? _modelSmall
                                                                   : _modelMedium

            selectionModel: root.selectionModel

            dragItem: dragItemPlaylist

            preferredHeader: root.header

            rowContextMenu: contextMenu

            displayMarginBeginning: root.displayMarginBeginning
            displayMarginEnd: root.displayMarginEnd

            fadingEdge.enableBeginningFade: root.enableBeginningFade
            fadingEdge.enableEndFade: root.enableEndFade

            isDropAcceptableFunc: function(drag, index) {
                root._adjustDragAccepted(drag)
                return drag.accepted
            }

            acceptDropFunc: function(index, drop) {
                return root._dropAction(drop, tableView.itemContainsDrag?.index)
            }

            dropIndicator: null

            Navigation.parentItem: root

            //-------------------------------------------------------------------------------------
            // Events

            onActionForSelection: _actionAtIndex()

            onItemDoubleClicked: (_, model) => showList(model, Qt.MouseFocusReason)

            onRightClick: (_, _, globalMousePos) => {
                contextMenu.popup(selectionModel.selectedRows(), globalMousePos)
            }

            //-------------------------------------------------------------------------------------
            // Childs

            Widgets.TableColumns {
                id: columns

                showCriterias: (tableView.sortModel === tableView._modelSmall)

                criteriaCover: "thumbnail"

                //---------------------------------------------------------------------------------
                // NOTE: When it's music we want the cover to be square

                titleCover_width: (isMusic) ? VLCStyle.trackListAlbumCover_width
                                            : VLCStyle.listAlbumCover_width

                titleCover_height: (isMusic) ? VLCStyle.trackListAlbumCover_heigth
                                             : VLCStyle.listAlbumCover_height

                titleCover_radius: (isMusic) ? VLCStyle.trackListAlbumCover_radius
                                             : VLCStyle.listAlbumCover_radius
            }
        }
    }

    Component {
        id: emptyLabel

        Widgets.EmptyLabelHint {
            visible: (model.count === 0)

            focus: true

            text: qsTr("No playlists found")
            hint: qsTr("Right click on a media\nto add it to a playlist")

            cover: VLCStyle.noArtAlbumCover
        }
    }
}

[Dauer der Verarbeitung: 0.2 Sekunden, vorverarbeitet 2026-10-24]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104857
#Domains=438682