MediaPlayListView.qml 10.8 KB
Newer Older
1
/*
2
 * Copyright 2016-2017 Matthieu Gallien <matthieu_gallien@yahoo.fr>
3
 * Copyright 2019 Nate Graham <nate@kde.org>
4
 *
5
6
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
7
8
9
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
10
 * This program is distributed in the hope that it will be useful,
11
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
16
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18
 */

19
import QtQuick 2.5
20
import QtQuick.Controls 2.3
21
22
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
23
import Qt.labs.platform 1.0 as PlatformDialog
24
import org.kde.kirigami 2.5 as Kirigami
25
import org.kde.elisa 1.0
26

27
FocusScope {
28
    property StackView parentStackView
29

30
    property int placeholderHeight: elisaTheme.dragDropPlaceholderHeight
31

32
    signal startPlayback()
33
    signal pausePlayback()
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

    function showPlayListNotification(message, type, action) {
        if (!message) {
            return;
        }

        if (type) {
            playListNotification.type = type;
        } else {
            playListNotification.type = Kirigami.MessageType.Information;
        }

        if (action) {
            playListNotification.actions = action;
        } else {
            playListNotification.actions = [];
        }

        playListNotification.text = message ? message : "";
        playListNotification.visible = true;
    }

    function hideNotification() {
        playListNotification.visible = false;
    }

    Kirigami.Action {
        id: undoAction
        text: i18nc("Undo", "Undo")
        icon.name: "dialog-cancel"
        onTriggered: elisa.mediaPlayList.undoClearPlayList()
    }

    Kirigami.Action {
        id: retryLoadAction
        text: i18nc("Retry", "Retry")
        icon.name: "edit-redo"
        onTriggered: loadPlaylist.trigger()
    }

    Kirigami.Action {
        id: retrySaveAction
        text: i18nc("Retry", "Retry")
        icon.name: "edit-redo"
        onTriggered: savePlaylist.trigger()
    }

    Connections {
        target: elisa.mediaPlayList
        onPlayListLoadFailed: {
            showPlayListNotification(i18nc("Message when playlist load failed", "Loading failed"), Kirigami.MessageType.Error, retryLoadAction)
        }
    }

    Connections {
         target: elisa.mediaPlayList
         onDisplayUndoInline: {
             showPlayListNotification(i18nc("Playlist cleared", "Playlist cleared"), Kirigami.MessageType.Information, undoAction)
         }
    }

    Connections {
         target: elisa.mediaPlayList
         onHideUndoInline: hideNotification()
    }
99

100
    id: topItem
101

102
    Accessible.role: Accessible.Pane
Nate Graham's avatar
Nate Graham committed
103
    Accessible.name: viewTitle.text
104

105
    Action {
106
        id: clearPlayList
Diego Gangl's avatar
Diego Gangl committed
107
        text: i18nc("Remove all tracks from play list", "Clear Playlist")
108
        icon.name: 'edit-clear-all'
109
110
        enabled: elisa.mediaPlayList ? elisa.mediaPlayList.tracksCount > 0 : false
        onTriggered: elisa.mediaPlayList.clearPlayList()
111
112
    }

113
    Action {
114
115
        id: showCurrentTrack
        text: i18nc("Show currently played track inside playlist", "Show Current Track")
116
        icon.name: 'media-show-active-track-amarok'
117
        enabled: elisa.mediaPlayList ? elisa.mediaPlayList.tracksCount > 0 : false
118
        onTriggered: {
119
120
            playListView.positionViewAtIndex(elisa.mediaPlayList.currentTrackRow, ListView.Contain)
            playListView.currentIndex = elisa.mediaPlayList.currentTrackRow
121
122
            playListView.currentItem.forceActiveFocus()
        }
123
124
    }

125
    Action {
126
        id: loadPlaylist
Diego Gangl's avatar
Diego Gangl committed
127
        text: i18nc("Load a playlist file", "Load Playlist...")
128
        icon.name: 'document-open'
129
130
131
132
133
134
135
136
        onTriggered:
        {
            fileDialog.fileMode = PlatformDialog.FileDialog.OpenFile
            fileDialog.file = ''
            fileDialog.open()
        }
    }

137
    Action {
138
        id: savePlaylist
Diego Gangl's avatar
Diego Gangl committed
139
        text: i18nc("Save a playlist file", "Save Playlist...")
140
        icon.name: 'document-save'
141
        enabled: elisa.mediaPlayList ? elisa.mediaPlayList.tracksCount > 0 : false
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
        onTriggered:
        {
            fileDialog.fileMode = PlatformDialog.FileDialog.SaveFile
            fileDialog.file = ''
            fileDialog.open()
        }
    }

    PlatformDialog.FileDialog {
        id: fileDialog

        defaultSuffix: 'm3u'
        folder: PlatformDialog.StandardPaths.writableLocation(PlatformDialog.StandardPaths.MusicLocation)
        nameFilters: [i18nc("file type (mime type) for m3u playlist", "Playlist (*.m3u)")]

        onAccepted:
        {
            if (fileMode === PlatformDialog.FileDialog.SaveFile) {
160
                if (!elisa.mediaPlayList.savePlaylist(fileDialog.file)) {
161
                    showPlayListNotification(i18nc("Message when saving a playlist failed", "Saving failed"), Kirigami.MessageType.Error, retrySaveAction)
162
163
                }
            } else {
164
                elisa.mediaPlayList.loadPlaylist(fileDialog.file)
165
166
167
168
            }
        }
    }

169
170
171
172
    ColumnLayout {
        anchors.fill: parent
        spacing: 0

173
174
175
176
177
178
179
        // Header with title and toolbar buttons
        HeaderFooterToolbar {
            type: "header"
            contentItems: [

                // Header title
                LabelWithToolTip {
Nate Graham's avatar
Nate Graham committed
180
181
                    id: viewTitle

182
183
184
185
186
187
188
189
190
                    Layout.fillWidth: true

                    text: i18nc("Title of the view of the playlist", "Playlist")

                    font.pointSize: elisaTheme.headerTitleFontSize
                    Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
                },

                // Toolbar buttons
191
                FlatButtonWithToolTip {
192
                    action: showCurrentTrack
193
194
195

                    icon.height: elisaTheme.smallControlButtonSize
                    icon.width: elisaTheme.smallControlButtonSize
196
                },
197
                FlatButtonWithToolTip {
198
                    action: savePlaylist
199
200
201

                    icon.height: elisaTheme.smallControlButtonSize
                    icon.width: elisaTheme.smallControlButtonSize
202
                },
203
                FlatButtonWithToolTip {
204
                    action: loadPlaylist
205
206
207

                    icon.height: elisaTheme.smallControlButtonSize
                    icon.width: elisaTheme.smallControlButtonSize
208
                },
209
                FlatButtonWithToolTip {
210
                    action: clearPlayList
211
212
213

                    icon.height: elisaTheme.smallControlButtonSize
                    icon.width: elisaTheme.smallControlButtonSize
214
215
                }
            ]
216
217
        }

Diego Gangl's avatar
Diego Gangl committed
218
219
220
        ColumnLayout {
            id: emptyPlaylistText
            spacing: 0
221
            visible: true
Diego Gangl's avatar
Diego Gangl committed
222
223
224
225
            Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
            Layout.fillHeight: true
            Layout.fillWidth: true

226
227
228
            Item {
                id: emptyVisible
                visible: elisa.mediaPlayList ? elisa.mediaPlayList.tracksCount === 0 : true
229
                Layout.fillHeight: true
230
            }
Diego Gangl's avatar
Diego Gangl committed
231
232

            Image {
233
234
                id: emptyImage
                visible: emptyVisible.visible
Diego Gangl's avatar
Diego Gangl committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
                Layout.alignment: Qt.AlignHCenter

                width: elisaTheme.gridDelegateWidth * 5
                height: elisaTheme.gridDelegateWidth * 5

                source: elisaTheme.playlistIcon
                opacity: 0.25

                sourceSize {
                    width: elisaTheme.viewSelectorDelegateHeight * 5
                    height: elisaTheme.viewSelectorDelegateHeight * 5
                }
            }

249
            Label {
250
251
                id: emptyLabel0
                visible: emptyVisible.visible
Diego Gangl's avatar
Diego Gangl committed
252
253
                Layout.fillWidth: true
                Layout.alignment: Qt.AlignHCenter
254
255
256
                Layout.rightMargin: elisaTheme.layoutHorizontalMargin
                Layout.leftMargin: elisaTheme.layoutHorizontalMargin

257
                font.pointSize: elisaTheme.headerTitleFontSize
258
                wrapMode: Text.WordWrap
Diego Gangl's avatar
Diego Gangl committed
259
260
261
262
263

                horizontalAlignment: Text.AlignHCenter
                text: i18nc("Your playlist is empty", "Your playlist is empty")
            }

264
            Label {
265
266
                id: emptyLabel1
                visible: emptyVisible.visible
Diego Gangl's avatar
Diego Gangl committed
267
                Layout.topMargin: 5
268
                Layout.fillWidth: true
Diego Gangl's avatar
Diego Gangl committed
269
                Layout.alignment: Qt.AlignHCenter
270
271
272
                Layout.rightMargin: elisaTheme.layoutHorizontalMargin
                Layout.leftMargin: elisaTheme.layoutHorizontalMargin

Diego Gangl's avatar
Diego Gangl committed
273
                wrapMode: Text.WordWrap
274

Diego Gangl's avatar
Diego Gangl committed
275
276
277
278
                horizontalAlignment: Text.AlignHCenter
                text: i18nc("Text shown when play list is empty", "Add some songs to get started. You can browse your music using the views on the left.")
            }

279
280
281
282
            Item {
                visible: emptyVisible.visible
                Layout.fillHeight: true
            }
Diego Gangl's avatar
Diego Gangl committed
283

284
285
            PlayListBasicView {
                id: playListView
Diego Gangl's avatar
Diego Gangl committed
286

287
                visible: !emptyVisible.visible
288

289
290
291
                Layout.fillWidth: true
                Layout.fillHeight: true

Nate Graham's avatar
Nate Graham committed
292
                title: viewTitle.text
293
294
295
296
297
298
299
                playListModel: elisa.mediaPlayList

                focus: true

                onStartPlayback: topItem.startPlayback()

                onPausePlayback: topItem.pausePlayback()
300

301
                onDisplayError: showPlayListNotification(errorText, Kirigami.MessageType.Error)
302

303
304
305
            }

            Kirigami.InlineMessage {
306
                id: playListNotification
307

308
                Timer {
309
                    id: autoHideNotificationTimer
310
311
312

                    interval: 7000

313
                    onTriggered: playListNotification.visible = false
314
315
                }

316
317
318
319
320
321
                type: Kirigami.MessageType.Information
                showCloseButton: true
                Layout.topMargin: 5
                Layout.fillWidth: true
                Layout.rightMargin: elisaTheme.layoutHorizontalMargin
                Layout.leftMargin: elisaTheme.layoutHorizontalMargin
322

323
324
325
                onVisibleChanged:
                {
                    if (visible) {
326
                        autoHideNotificationTimer.start()
327
                    } else {
328
                        autoHideNotificationTimer.stop()
329
330
                    }
                }
331
            }
332
        }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347

        // Footer with number of tracks label
        HeaderFooterToolbar {
            type: "footer"
            contentItems: [
                LabelWithToolTip {
                    id: trackCountLabel

                    Layout.fillWidth: true

                    text: i18np("1 track", "%1 tracks", (elisa.mediaPlayList ? elisa.mediaPlayList.tracksCount : 0))
                    elide: Text.ElideLeft
                }
            ]
        }
348
349
350
    }
}

351