[vlc-devel] [RFC 72/82] qml: provide a widget to display the play queue

Pierre Lamot pierre at videolabs.io
Fri Feb 1 14:02:16 CET 2019


  this support key navigation and edition
---
 modules/gui/qt/Makefile.am                    |   4 +
 modules/gui/qt/qml/playlist/PLItem.qml        | 158 +++++++++++
 modules/gui/qt/qml/playlist/PLItemFooter.qml  |  55 ++++
 .../gui/qt/qml/playlist/PlaylistListView.qml  | 263 ++++++++++++++++++
 modules/gui/qt/qml/playlist/PlaylistMenu.qml  | 249 +++++++++++++++++
 modules/gui/qt/vlc.qrc                        |   6 +
 6 files changed, 735 insertions(+)
 create mode 100644 modules/gui/qt/qml/playlist/PLItem.qml
 create mode 100644 modules/gui/qt/qml/playlist/PLItemFooter.qml
 create mode 100644 modules/gui/qt/qml/playlist/PlaylistListView.qml
 create mode 100644 modules/gui/qt/qml/playlist/PlaylistMenu.qml

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index aecc08abcf..bd4905ba91 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -545,6 +545,10 @@ libqt_plugin_la_RES = \
 	gui/qt/qml/utils/SelectableDelegateModel.qml \
 	gui/qt/qml/utils/ComboBoxExt.qml \
 	gui/qt/qml/utils/StackViewExt.qml \
+	gui/qt/qml/playlist/PlaylistListView.qml \
+	gui/qt/qml/playlist/PLItem.qml \
+	gui/qt/qml/playlist/PLItemFooter.qml \
+	gui/qt/qml/playlist/PlaylistMenu.qml \
 	gui/qt/qml/style/qmldir \
 	gui/qt/qml/style/VLCIcons.qml \
 	gui/qt/qml/style/VLCStyle.qml \
diff --git a/modules/gui/qt/qml/playlist/PLItem.qml b/modules/gui/qt/qml/playlist/PLItem.qml
new file mode 100644
index 0000000000..8d139bcd58
--- /dev/null
+++ b/modules/gui/qt/qml/playlist/PLItem.qml
@@ -0,0 +1,158 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 2.11
+import QtQuick.Controls 2.4
+import QtQuick.Layouts 1.3
+
+
+import "qrc:///utils/" as Utils
+import "qrc:///style/"
+
+
+Rectangle {
+    id: root
+
+    signal itemClicked(int keys, int modifier)
+    signal itemDoubleClicked(int keys, int modifier)
+    property alias hovered: mouse.containsMouse
+
+    property var dragitem: null
+    signal dropedMovedAt(int target, var drop)
+
+
+    // Should the cover be displayed
+    //property alias showCover: cover.visible
+
+    // This item will become the parent of the dragged item during the drag operation
+    //property alias draggedItemParent: draggable_item.draggedItemParent
+
+    height: Math.max( VLCStyle.fontHeight_normal, VLCStyle.icon_normal )
+
+    property bool dropVisible: false
+
+
+    Rectangle {
+        width: parent.width
+        height: 2
+        anchors.top: parent.top
+        antialiasing: true
+        visible: dropVisible
+        color: VLCStyle.colors.accent
+    }
+
+    MouseArea {
+        id: mouse
+        anchors.fill: parent
+        hoverEnabled: true
+
+
+        onClicked:{
+            root.itemClicked(mouse.buttons, mouse.modifiers);
+        }
+        onDoubleClicked:  root.itemDoubleClicked(mouse.buttons, mouse.modifiers);
+
+        drag.target: dragItem
+
+        property bool hold: false
+        onPositionChanged: {
+            if (hold)
+                dragItem.visible = true
+        }
+        onPressed:  {
+            hold = true
+            var pos = this.mapToGlobal( mouseX, mouseY)
+            dragItem.updatePos(pos.x, pos.y)
+        }
+        onReleased: {
+            if (dragItem.visible)
+                dragItem.Drag.drop()
+            dragItem.visible = false
+            hold = false
+        }
+
+        RowLayout {
+            id: content
+            anchors.fill: parent
+
+            /* Cover of the associated album */
+            Image {
+                id: cover
+
+                Layout.preferredHeight: VLCStyle.icon_normal
+                Layout.preferredWidth: VLCStyle.icon_normal
+
+                fillMode: Image.PreserveAspectFit
+                source: (model.artwork && model.artwork.toString()) ? model.artwork : VLCStyle.noArtCover
+            }
+
+            Image {
+                id: isVisible
+                visible: model.isCurrent
+                Layout.preferredHeight: VLCStyle.icon_small
+                Layout.preferredWidth: VLCStyle.icon_small
+                source:  "qrc:///toolbar/play_b.svg"
+            }
+
+            Rectangle {
+                id: bg
+
+                Layout.fillWidth: true
+                Layout.alignment: Layout.verticalCenter | Layout.left
+
+                height: VLCStyle.fontHeight_normal
+
+                color: "transparent"
+
+                /* Title/name of the item */
+                Text {
+                    id: textInfo
+
+                    x: VLCStyle.margin_small
+                    font.pixelSize: VLCStyle.fontSize_normal
+
+                    text: model.title
+                    color: VLCStyle.colors.text
+                }
+
+                Text {
+                    id: textDuration
+
+                    anchors.right: parent.right
+                    font.pixelSize: VLCStyle.fontSize_normal
+
+                    text: model.duration
+                    color: VLCStyle.colors.text
+                }
+            }
+        }
+
+        DropArea {
+            anchors { fill: parent }
+            onEntered: {
+                dropVisible = true
+                return true
+            }
+            onExited: dropVisible = false
+            onDropped: {
+                root.dropedMovedAt(model.index, drop)
+                dropVisible = false
+            }
+        }
+    }
+}
diff --git a/modules/gui/qt/qml/playlist/PLItemFooter.qml b/modules/gui/qt/qml/playlist/PLItemFooter.qml
new file mode 100644
index 0000000000..f4f89a84d4
--- /dev/null
+++ b/modules/gui/qt/qml/playlist/PLItemFooter.qml
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 2.11
+
+import "qrc:///style/"
+
+Item {
+    id: foot
+    property bool _dropVisible: false
+
+    width: parent.width
+    height: Math.max(VLCStyle.icon_normal, view.height - y)
+
+    Rectangle {
+        width: parent.width
+        anchors.top: parent.top
+        antialiasing: true
+        height: 2
+        visible: foot._dropVisible
+        color: VLCStyle.colors.accent
+    }
+
+    DropArea {
+        anchors { fill: parent }
+        onEntered: {
+            foot._dropVisible = true
+            return true
+        }
+        onExited: foot._dropVisible = false
+        onDropped: {
+            if (drop.hasUrls) {
+                console.log("add urls", drop.urls)
+                delegateModel.onDropUrlAtEnd(drop.urls)
+            } else {
+                delegateModel.onDropMovedAtEnd()
+            }
+            foot._dropVisible = false
+        }
+    }
+}
diff --git a/modules/gui/qt/qml/playlist/PlaylistListView.qml b/modules/gui/qt/qml/playlist/PlaylistListView.qml
new file mode 100644
index 0000000000..799b2072f3
--- /dev/null
+++ b/modules/gui/qt/qml/playlist/PlaylistListView.qml
@@ -0,0 +1,263 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 2.11
+import QtQuick.Controls 2.4
+import QtQml.Models 2.2
+
+import org.videolan.medialib 0.1
+import org.videolan.vlc 0.1
+
+import "qrc:///utils/" as Utils
+import "qrc:///style/"
+
+Utils.NavigableFocusScope {
+    id: root
+
+    property var plmodel: PlaylistListModel {
+        playlistId: mainctx.playlist
+    }
+
+
+    PlaylistControlerModel {
+        id: plControler
+        playlistPtr: mainctx.playlist
+    }
+
+    //label for DnD
+    Utils.DNDLabel {
+        id: dragItem
+        text: qsTr("%1 tracks selected").arg(delegateModel.selectedGroup.count)
+    }
+
+
+    /* popup side menu allowing to perform group action  */
+    PlaylistMenu {
+        id: overlay
+
+        anchors.verticalCenter: root.verticalCenter
+        anchors.right: view.right
+        z: 2
+
+        onMenuExit:{
+            delegateModel.mode = "normal"
+            view.focus = true
+        }
+        onClear: delegateModel.onDelete()
+        onPlay: delegateModel.onPlay()
+        onSelectionMode:  {
+            delegateModel.mode = selectionMode ? "select" : "normal"
+            view.focus = true
+        }
+        onMoveMode: {
+            delegateModel.mode = moveMode ? "move" : "normal"
+            view.focus = true
+        }
+    }
+
+    //model
+
+    Utils.SelectableDelegateModel {
+        id: delegateModel
+        model: root.plmodel
+
+        property string mode: "normal"
+
+        delegate: Package {
+            id: element
+
+            PLItem {
+                id: plitem
+                Package.name: "list"
+                width: root.width
+                color: VLCStyle.colors.getBgColor(element.DelegateModel.inSelected, plitem.hovered, plitem.activeFocus)
+
+                dragitem: dragItem
+
+                onItemClicked : {
+                    view.forceActiveFocus()
+                    if (delegateModel.mode == "move") {
+                        delegateModel.onMoveSelectionAtPos(index)
+                        view.currentIndex = index
+                    } else if ( delegateModel.mode == "select" ) {
+                    } else {
+                        delegateModel.onUpdateIndex( modifier , view.currentIndex, index)
+                        view.currentIndex = index
+                    }
+                }
+                onItemDoubleClicked:  delegateModel.onAction(index, true)
+
+                onDropedMovedAt: {
+                    if (drop.hasUrls) {
+                        delegateModel.onDropUrlAtPos(drop.urls, target)
+                    } else {
+                        delegateModel.onMoveSelectionAtPos(target)
+                    }
+                }
+            }
+        }
+
+        function onMoveSelectionAtPos(target) {
+            var list = []
+            for (var i = 0; i < delegateModel.selectedGroup.count; i++ ) {
+                list.push(delegateModel.selectedGroup.get(i).itemsIndex)
+            }
+            root.plmodel.moveItems(list, target)
+        }
+
+        function onDropMovedAtEnd() {
+            onMoveSelectionAtPos(items.count)
+        }
+
+        function onDropUrlAtPos(urls, target) {
+            var list = []
+            for (var i = 0; i < urls.length; i++){
+                list.push(urls[i])
+            }
+            plControler.insert(target, list)
+        }
+
+        function onDropUrlAtEnd(urls) {
+            var list = []
+            for (var i = 0; i < urls.length; i++){
+                list.push(urls[i])
+            }
+            plControler.append(list)
+        }
+
+        function onDelete() {
+            var list = []
+            for (var i = 0; i < delegateModel.selectedGroup.count; i++ ) {
+                list.push(delegateModel.selectedGroup.get(i).itemsIndex)
+            }
+            root.plmodel.removeItems(list)
+        }
+
+        function onPlay() {
+            if (delegateModel.selectedGroup.count > 0)
+                plControler.goTo(delegateModel.selectedGroup.get(0).itemsIndex, true)
+        }
+
+        function onAction(index) {
+            if (mode === "select")
+                updateSelection( Qt.ControlModifier, index, view.currentIndex )
+            else //normal
+                onPlay()
+        }
+
+        function onUpdateIndex( keyModifiers, oldIndex, newIndex )
+        {
+            if (delegateModel.mode === "select") {
+                console.log("update selection select")
+            } else if (delegateModel.mode === "move") {
+                if (delegateModel.selectedGroup.count === 0)
+                    return
+
+                var list = []
+                for (var i = 0; i < delegateModel.selectedGroup.count; i++ ) {
+                    list.push(delegateModel.selectedGroup.get(i).itemsIndex)
+                }
+                var minIndex= delegateModel.selectedGroup.get(0).itemsIndex
+                var maxIndex= delegateModel.selectedGroup.get(delegateModel.selectedGroup.count - 1).itemsIndex
+
+                if (newIndex > oldIndex) {
+                    //after the next item
+                    newIndex = Math.min(maxIndex + 2, delegateModel.items.count)
+                    view.currentIndex = Math.min(maxIndex, delegateModel.items.count)
+                } else if (newIndex < oldIndex) {
+                    //before the previous item
+                    view.currentIndex = Math.max(minIndex, 0)
+                    newIndex = Math.max(minIndex - 1, 0)
+                }
+
+                root.plmodel.moveItems(list, newIndex)
+
+            } else  { //normal
+                updateSelection( keyModifiers, oldIndex, newIndex )
+            }
+        }
+    }
+
+    Utils.KeyNavigableListView {
+        id: view
+
+        anchors.fill: parent
+        focus: true
+
+        model: delegateModel.parts.list
+        modelCount: delegateModel.items.count
+
+        footer: PLItemFooter {}
+
+        onSelectAll: delegateModel.selectAll()
+        onSelectionUpdated: delegateModel.onUpdateIndex( keyModifiers, oldIndex, newIndex )
+        Keys.onDeletePressed: delegateModel.onDelete()
+        onActionAtIndex: delegateModel.onAction(index)
+        onActionRight: {
+            overlay.state = "normal"
+            overlay.focus = true
+        }
+        onActionLeft: this.onCancel(index, root.actionLeft)
+        onActionCancel: this.onCancel(index, root.actionCancel)
+        onActionUp: root.actionUp(index)
+        onActionDown: root.actionDown(index)
+
+        function onCancel(index, fct) {
+            if (delegateModel.mode === "select" || delegateModel.mode === "move")
+            {
+                overlay.state = "hidden"
+                delegateModel.mode = "normal"
+            }
+            else
+            {
+                fct(index)
+            }
+        }
+
+        Connections {
+            target: root.plmodel
+            onCurrentIndexChanged: {
+                var plIndex = root.plmodel.currentIndex
+                if (view.currentIndex === -1 && plIndex >= 0) {
+                    delegateModel.items.get(plIndex).inSelected = true
+                    view.currentIndex = plIndex
+                }
+            }
+        }
+        Connections {
+            target: delegateModel.items
+            onCountChanged: {
+                if (view.currentIndex === -1 && delegateModel.items.count > 0) {
+                    delegateModel.items.get(0).inSelected = true
+                    view.currentIndex = 0
+                }
+            }
+        }
+    }
+
+    Label {
+        anchors.centerIn: parent
+        visible: delegateModel.items.count === 0
+        font.pixelSize: VLCStyle.fontHeight_xxlarge
+        color: root.activeFocus ? VLCStyle.colors.accent : VLCStyle.colors.text
+        text: qsTr("playlist is empty")
+    }
+
+    Keys.priority: Keys.AfterItem
+    Keys.forwardTo: view
+    Keys.onPressed: defaultKeyAction(event, 0)
+}
diff --git a/modules/gui/qt/qml/playlist/PlaylistMenu.qml b/modules/gui/qt/qml/playlist/PlaylistMenu.qml
new file mode 100644
index 0000000000..5bfb282ca4
--- /dev/null
+++ b/modules/gui/qt/qml/playlist/PlaylistMenu.qml
@@ -0,0 +1,249 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * 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 2.11
+import QtQuick.Controls 2.4
+import QtQuick.Layouts 1.3
+
+import "qrc:///style/"
+
+FocusScope{
+    id: root
+
+    signal menuExit()
+    signal play()
+    signal clear()
+    signal selectionMode()
+    signal moveMode()
+
+    Keys.onPressed: {
+        if (event.matches(StandardKey.MoveToPreviousChar)  //left
+            || event.matches(StandardKey.MoveToNextChar) //right
+            || event.matches(StandardKey.Back)
+            || event.matches(StandardKey.Cancel) //esc
+        ) {
+            _exitMenu();
+            event.accepted = true
+            return;
+        }
+    }
+
+    width: VLCStyle.icon_large
+    height: VLCStyle.icon_large * 5
+    property int _hiddentX: VLCStyle.icon_large
+
+    function _exitMenu() {
+        root.state = "hidden"
+        menuExit()
+    }
+
+    Item {
+        id: overlay
+        anchors.fill: parent
+
+        Column {
+            anchors.right: parent.right
+            spacing: VLCStyle.margin_xsmall
+
+            RoundButton {
+                id: playButton
+
+                height: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                width: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                x: root._hiddentX
+
+                KeyNavigation.down: clearButton
+                icon.source: "qrc:///toolbar/play_b.svg"
+                icon.width: VLCStyle.icon_normal
+                icon.height: VLCStyle.icon_normal
+                onClicked: {
+                    play()
+                    _exitMenu()
+                }
+                focus: true
+                background: Rectangle {
+                    radius: parent.radius
+                    implicitHeight: parent.width
+                    implicitWidth: parent.height
+                    color: "palegreen"
+                }
+            }
+            RoundButton {
+                id: clearButton
+
+                height: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                width: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                x: root._hiddentX
+
+                KeyNavigation.down: selectButton
+                icon.source: "qrc:///toolbar/clear.svg"
+                icon.width: VLCStyle.icon_normal
+                icon.height: VLCStyle.icon_normal
+                onClicked: {
+                    clear()
+                    _exitMenu()
+                }
+                background: Rectangle {
+                    radius: parent.radius
+                    implicitHeight: parent.width
+                    implicitWidth: parent.height
+                    color: "pink"
+                }
+            }
+            RoundButton {
+                id: selectButton
+
+                height: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                width: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                x: root._hiddentX
+
+                KeyNavigation.down: moveButton
+                icon.source: "qrc:///toolbar/playlist.svg"
+                icon.width: VLCStyle.icon_normal
+                icon.height: VLCStyle.icon_normal
+
+                checkable: true
+                checked: false
+                onClicked:  root.state = checked ? "select" : "normal"
+                onCheckedChanged: selectionMode(checked)
+                background: Rectangle {
+                    radius: parent.radius
+                    implicitHeight: parent.width
+                    implicitWidth: parent.height
+                    color: "lightblue"
+                }
+            }
+            RoundButton {
+                id: moveButton
+
+                height: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                width: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                x: root._hiddentX
+
+                KeyNavigation.down: backButton
+                icon.source: "qrc:///toolbar/space.svg"
+                icon.width: VLCStyle.icon_normal
+                icon.height: VLCStyle.icon_normal
+
+                checkable: true
+                checked: false
+                onClicked:  root.state = checked ? "move" : "normal"
+                onCheckedChanged: moveMode(checked)
+                background: Rectangle {
+                    radius: parent.radius
+                    implicitHeight: parent.width
+                    implicitWidth: parent.height
+                    color: "lightyellow"
+                }
+            }
+            RoundButton {
+                id: backButton
+                height: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                width: activeFocus ? VLCStyle.icon_normal * 1.3 : VLCStyle.icon_normal
+                x: root._hiddentX
+
+                icon.source: "qrc:///menu/exit.svg"
+                icon.width: VLCStyle.icon_normal
+                icon.height: VLCStyle.icon_normal
+
+                onClicked:  _exitMenu()
+
+                background: Rectangle {
+                    radius: parent.radius
+                    implicitHeight: parent.width
+                    implicitWidth: parent.height
+                    color: "lightgrey"
+                }
+            }
+        }
+    }
+
+    state: "hidden"
+    states: [
+        State {
+            name: "hidden"
+            PropertyChanges { target: selectButton; checked: false }
+            PropertyChanges { target: moveButton; checked: false }
+        },
+        State {
+            name: "normal"
+            PropertyChanges { target: moveButton; checked: false }
+            PropertyChanges { target: selectButton; checked: false }
+        },
+        State {
+            name: "select"
+            PropertyChanges { target: selectButton; checked: true }
+            PropertyChanges { target: moveButton; checked: false }
+        },
+        State {
+            name: "move"
+            PropertyChanges { target: selectButton; checked: false }
+            PropertyChanges { target: moveButton; checked: true }
+        }
+    ]
+
+    transitions: [
+        Transition {
+            from: "hidden"; to: "*"
+            ParallelAnimation {
+                SequentialAnimation {
+                    NumberAnimation { target: playButton; properties: "x"; duration: 200; from: _hiddentX; to: 0 }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 25 }
+                    NumberAnimation { target: clearButton; properties: "x"; duration: 200; from: _hiddentX; to: 0 }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 75 }
+                    NumberAnimation { target: selectButton; properties: "x"; duration: 200; from: _hiddentX; to: 0 }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 50 }
+                    NumberAnimation { target: moveButton; properties: "x"; duration: 200; from: _hiddentX; to: 0 }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 100 }
+                    NumberAnimation { target: backButton; properties: "x"; duration: 200; from: _hiddentX; to: 0 }
+                }
+            }
+        },
+        Transition {
+            from: "*"; to: "hidden"
+            ParallelAnimation {
+                SequentialAnimation {
+                    PauseAnimation { duration: 100 }
+                    NumberAnimation { target: playButton; properties: "x"; duration: 200; from: 0; to: _hiddentX }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 75 }
+                    NumberAnimation { target: clearButton; properties: "x"; duration: 200; from: 0; to: _hiddentX }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 50 }
+                    NumberAnimation { target: selectButton; properties: "x"; duration: 200; from: 0; to: _hiddentX }
+                }
+                SequentialAnimation {
+                    PauseAnimation { duration: 25 }
+                    NumberAnimation { target: moveButton; properties: "x"; duration: 200; from: 0; to: _hiddentX }
+                }
+                SequentialAnimation {
+                    NumberAnimation { target: backButton; properties: "x"; duration: 200; from: 0; to: _hiddentX }
+                }
+            }
+        }
+    ]
+}
diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
index 530e1c1aa5..de7bac2321 100644
--- a/modules/gui/qt/vlc.qrc
+++ b/modules/gui/qt/vlc.qrc
@@ -185,6 +185,12 @@
         <file alias="VLCColors.qml">qml/style/VLCColors.qml</file>
         <file alias="VLCIcons.qml">qml/style/VLCIcons.qml</file>
     </qresource>
+    <qresource prefix="/playlist">
+        <file alias="PLItem.qml">qml/playlist/PLItem.qml</file>
+        <file alias="PlaylistListView.qml">qml/playlist/PlaylistListView.qml</file>
+        <file alias="PlaylistMenu.qml">qml/playlist/PlaylistMenu.qml</file>
+        <file alias="PLItemFooter.qml">qml/playlist/PLItemFooter.qml</file>
+    </qresource>
     <qresource prefix="/about">
         <file alias="About.qml">qml/about/About.qml</file>
     </qresource>
-- 
2.19.1



More information about the vlc-devel mailing list