MusicGenres.qml 10.5 KB
Newer Older
Pierre Lamot's avatar
Pierre Lamot committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*****************************************************************************
 * 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.vlc 0.1
import org.videolan.medialib 0.1

import "qrc:///util/" as Util
import "qrc:///widgets/" as Widgets
26
import "qrc:///main/" as MainInterface
Pierre Lamot's avatar
Pierre Lamot committed
27
28
29
30
import "qrc:///style/"

Widgets.NavigableFocusScope {
    id: root
31
    property alias model: genreModel
Pierre Lamot's avatar
Pierre Lamot committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    property var sortModel: [
        { text: i18n.qtr("Alphabetic"), criteria: "title" }
    ]

    readonly property var currentIndex: view.currentItem.currentIndex
    //the index to "go to" when the view is loaded
    property var initialIndex: 0

    onInitialIndexChanged:  resetFocus()

    navigationCancel: function() {
        if (view.currentItem.currentIndex <= 0)
            defaultNavigationCancel()
        else
            view.currentItem.currentIndex = 0;
    }

    Component.onCompleted: loadView()

    function loadView() {
52
        if (mainInterface.gridView) {
Pierre Lamot's avatar
Pierre Lamot committed
53
54
            view.replace(gridComponent)
        } else {
55
            view.replace(tableComponent)
Pierre Lamot's avatar
Pierre Lamot committed
56
57
58
        }
    }

59
60
    function showAlbumView( m ) {
        history.push([ "mc", "music", "genres", "albums", { parentId: m.id, genreName: m.name } ])
Pierre Lamot's avatar
Pierre Lamot committed
61
62
63
    }

    function resetFocus() {
64
        if (genreModel.count === 0) {
Pierre Lamot's avatar
Pierre Lamot committed
65
66
67
            return
        }
        var initialIndex = root.initialIndex
68
        if (initialIndex >= genreModel.count)
Pierre Lamot's avatar
Pierre Lamot committed
69
            initialIndex = 0
70
        selectionModel.select(genreModel.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
71
72
        if (view.currentItem)
            view.currentItem.positionViewAtIndex(initialIndex, ItemView.Contain)
Pierre Lamot's avatar
Pierre Lamot committed
73
74
    }

75
76
77
78
79
    MLGenreModel {
        id: genreModel
        ml: medialib

        onCountChanged: {
80
            if (genreModel.count > 0 && !selectionModel.hasSelection) {
81
82
83
84
                root.resetFocus()
            }
        }
    }
Pierre Lamot's avatar
Pierre Lamot committed
85

86
87
88
89
90
91
    function _actionAtIndex(index) {
        if (selectionModel.selectedIndexes.length > 1) {
            medialib.addAndPlay(model.getIdsForIndexes(selectionModel.selectedIndexes))
        } else if (selectionModel.selectedIndexes.length === 1) {
            var sel = selectionModel.selectedIndexes[0]
            showAlbumView( genreModel.getDataAt(sel) )
Pierre Lamot's avatar
Pierre Lamot committed
92
        }
93
    }
Pierre Lamot's avatar
Pierre Lamot committed
94

95
96
    Util.SelectableDelegateModel {
        id: selectionModel
Pierre Lamot's avatar
Pierre Lamot committed
97

98
        model: genreModel
Pierre Lamot's avatar
Pierre Lamot committed
99
100
    }

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    Widgets.DragItem {
        id: genreDragItem

        function updateComponents(maxCovers) {
          var items = selectionModel.selectedIndexes.slice(0, maxCovers).map(function (x){
            return genreModel.getDataAt(x.row)
          })
          var title = items.map(function (item){ return item.name}).join(", ")
          var covers = items.map(function (item) { return {artwork: item.cover || VLCStyle.noArtCover}})
          return {
            covers: covers,
            title: title,
            count: selectionModel.selectedIndexes.length
          }
        }

117
118
        function getSelectedInputItem() {
            return genreModel.getItemsForIndexes(selectionModel.selectedIndexes);
119
120
121
        }
    }

Pierre Lamot's avatar
Pierre Lamot committed
122
123
    /*
     *define the intial position/selection
124
     * This is done on activeFocus rather than Component.onCompleted because selectionModel.
Pierre Lamot's avatar
Pierre Lamot committed
125
126
127
     * selectedGroup update itself after this event
     */
    onActiveFocusChanged: {
128
        if (activeFocus && genreModel.count > 0 && !selectionModel.hasSelection) {
Pierre Lamot's avatar
Pierre Lamot committed
129
130
131
            var initialIndex = 0
            if (view.currentItem.currentIndex !== -1)
                initialIndex = view.currentItem.currentIndex
132
            selectionModel.select(genreModel.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
Pierre Lamot's avatar
Pierre Lamot committed
133
134
135
136
            view.currentItem.currentIndex = initialIndex
        }
    }

137
138
139
140
141
    GenreContextMenu {
        id: contextMenu
        model: genreModel
    }

Pierre Lamot's avatar
Pierre Lamot committed
142
143
144
    /* Grid View */
    Component {
        id: gridComponent
145
        MainInterface.MainGridView {
Pierre Lamot's avatar
Pierre Lamot committed
146
147
            id: gridView_id

148
            delegateModel: selectionModel
149
            model: genreModel
150
            topMargin: VLCStyle.margin_large
Pierre Lamot's avatar
Pierre Lamot committed
151

152
153
154
155
156
157
158
159
160
            Widgets.GridShadows {
                id: shadows

                leftPadding: 0
                coverWidth: VLCStyle.colWidth(2)
                coverHeight: shadows.coverWidth / 2
            }

           delegate: Widgets.GridItem {
Prince Gupta's avatar
Prince Gupta committed
161
                id: item
Pierre Lamot's avatar
Pierre Lamot committed
162

Prince Gupta's avatar
Prince Gupta committed
163
164
165
166
167
168
169
                property var model: ({})
                property int index: -1

                width: VLCStyle.colWidth(2)
                height: width / 2
                pictureWidth: width
                pictureHeight: height
Pierre Lamot's avatar
Pierre Lamot committed
170
                image: model.cover || VLCStyle.noArtAlbum
171
                playCoverBorderWidth: VLCStyle.dp(3, VLCStyle.scale)
172
                dragItem: genreDragItem
173
174
                unselectedUnderlay: shadows.unselected
                selectedUnderlay: shadows.selected
Pierre Lamot's avatar
Pierre Lamot committed
175

Prince Gupta's avatar
Prince Gupta committed
176
                onItemDoubleClicked: root.showAlbumView(model)
177
                onItemClicked: gridView_id.leftClickOnItem(modifier, item.index)
178

Prince Gupta's avatar
Prince Gupta committed
179
180
181
182
                onPlayClicked: {
                    if (model.id)
                        medialib.addAndPlay(model.id)
                }
Pierre Lamot's avatar
Pierre Lamot committed
183

184
                onContextMenuButtonClicked: {
185
                    gridView_id.rightClickOnItem(index)
186
187
188
                    contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
                }

189
                pictureOverlay: Item {
190
191
192
193
194
195
196
197
198
199
200
201
                    Rectangle
                    {
                        anchors.fill: parent

                        radius: VLCStyle.gridCover_radius

                        gradient: Gradient {
                            GradientStop { position: 0.0; color: Qt.rgba(0, 0, 0, 0.3) }
                            GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.7) }
                        }
                    }

202
203
204
205
206
207
208
209
                    Column {
                        anchors.centerIn: parent

                        Label {
                             width: item.width
                             elide: Text.ElideRight
                             font.pixelSize: VLCStyle.fontSize_large
                             font.weight: Font.DemiBold
210
                             text: model.name || i18n.qtr("Unknown genre")
211
212
213
214
215
216
217
218
219
220
221
                             color: "white"
                             horizontalAlignment: Text.AlignHCenter
                        }

                        Widgets.CaptionLabel {
                            width: item.width
                            text: model.nb_tracks > 1 ? i18n.qtr("%1 Tracks").arg(model.nb_tracks) : i18n.qtr("%1 Track").arg(model.nb_tracks)
                            opacity: .7
                            color: "white"
                            horizontalAlignment: Text.AlignHCenter
                        }
Prince Gupta's avatar
Prince Gupta committed
222
223
                    }
                }
Pierre Lamot's avatar
Pierre Lamot committed
224
225
226
227
            }

            focus: true

Prince Gupta's avatar
Prince Gupta committed
228
229
            cellWidth: VLCStyle.colWidth(2)
            cellHeight: cellWidth / 2
Pierre Lamot's avatar
Pierre Lamot committed
230

231
232
233
            onSelectAll: selectionModel.selectAll()
            onSelectionUpdated:  selectionModel.updateSelection( keyModifiers, oldIndex, newIndex )
            onActionAtIndex: _actionAtIndex(index)
Pierre Lamot's avatar
Pierre Lamot committed
234
235
236
237
238
239

            navigationParent: root
        }
    }

    Component {
240
241
        id: tableComponent
        /* Table View */
242
        MainInterface.MainTableView {
243
            id: tableView_id
Pierre Lamot's avatar
Pierre Lamot committed
244

245
246
247
248
            readonly property int _nameColSpan: Math.max(
                                                    VLCStyle.gridColumnsForWidth(tableView_id.availableRowWidth - VLCStyle.listAlbumCover_width - VLCStyle.column_margin_width) - 1
                                                    , 1)

249
            model: genreModel
250
            selectionDelegateModel: selectionModel
251
            headerColor: VLCStyle.colors.bg
Pierre Lamot's avatar
Pierre Lamot committed
252
            focus: true
253
            onActionForSelection: _actionAtIndex(selection)
Pierre Lamot's avatar
Pierre Lamot committed
254
            navigationParent: root
255
            dragItem: genreDragItem
256
            rowHeight: VLCStyle.tableCoverRow_height
257
            headerTopPadding: VLCStyle.margin_normal
258

259
            sortModel:  [
260
                { isPrimary: true, criteria: "cover", width: VLCStyle.listAlbumCover_width, headerDelegate: tableColumns.titleHeaderDelegate, colDelegate: tableColumns.titleDelegate },
261
                { criteria: "name", width: VLCStyle.colWidth(tableView_id._nameColSpan), text: i18n.qtr("Name") },
262
263
                { criteria: "nb_tracks", width: VLCStyle.colWidth(1), text: i18n.qtr("Tracks") }
            ]
264

265
266
267
            onItemDoubleClicked: {
                root.showAlbumView(model)
            }
268

269
270
            onContextMenuButtonClicked: contextMenu.popup(selectionModel.selectedIndexes, menuParent.mapToGlobal(0,0))
            onRightClick: contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
271
272
273
274
275
276
277
278
279

            Widgets.TableColumns {
                id: tableColumns

                showTitleText: false
                titleCover_height: VLCStyle.listAlbumCover_height
                titleCover_width: VLCStyle.listAlbumCover_width
                titleCover_radius: VLCStyle.listAlbumCover_radius
            }
Pierre Lamot's avatar
Pierre Lamot committed
280
281
282
283
284
285
        }
    }

    Widgets.StackViewExt {
        id: view

286
        initialItem: mainInterface.gridView ? gridComponent : tableComponent
Pierre Lamot's avatar
Pierre Lamot committed
287
288

        anchors.fill: parent
289
        focus: genreModel.count !== 0
Pierre Lamot's avatar
Pierre Lamot committed
290
    }
291
292
293
294
295
296
297
298
299
300
301

    Connections {
        target: mainInterface
        onGridViewChanged: {
            if (mainInterface.gridView) {
                view.replace(gridComponent)
            } else {
                view.replace(tableComponent)
            }
        }
    }
302
303
304
305
306
307
308
309
310

    EmptyLabel {
        anchors.fill: parent
        visible: genreModel.count === 0
        focus: genreModel.count === 0
        text: i18n.qtr("No genres found\nPlease try adding sources, by going to the Network tab")
        navigationParent: root
        cover: VLCStyle.noArtAlbumCover
    }
Pierre Lamot's avatar
Pierre Lamot committed
311
}