diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -389,7 +389,6 @@ qml/GridBrowserDelegate.qml qml/ListBrowserView.qml qml/ListBrowserDelegate.qml - qml/FileBrowserDelegate.qml qml/FileBrowserView.qml qml/ScrollHelper.qml qml/FlatButtonWithToolTip.qml diff --git a/src/mediaplaylist.h b/src/mediaplaylist.h --- a/src/mediaplaylist.h +++ b/src/mediaplaylist.h @@ -237,6 +237,8 @@ void switchTo(int row); + void loadPlaylist(const QString &localFileName); + void loadPlaylist(const QUrl &fileName); void enqueue(const ElisaUtils::EntryData &newEntry, ElisaUtils::PlayListEntryType databaseIdType); diff --git a/src/mediaplaylist.cpp b/src/mediaplaylist.cpp --- a/src/mediaplaylist.cpp +++ b/src/mediaplaylist.cpp @@ -578,6 +578,12 @@ dOld->mRepeatPlay = d->mRepeatPlay; } +void MediaPlayList::loadPlaylist(const QString &localFileName) +{ + d->mLoadPlaylist.clear(); + d->mLoadPlaylist.load(QUrl::fromLocalFile(localFileName), "m3u"); +} + void MediaPlayList::loadPlaylist(const QUrl &fileName) { d->mLoadPlaylist.clear(); diff --git a/src/models/filebrowsermodel.h b/src/models/filebrowsermodel.h --- a/src/models/filebrowsermodel.h +++ b/src/models/filebrowsermodel.h @@ -33,9 +33,9 @@ enum ColumnsRoles { NameRole = Qt::UserRole + 1, - ContainerDataRole = Qt::UserRole + 2, + FileUrlRole = Qt::UserRole + 2, ImageUrlRole = Qt::UserRole + 3, - DirectoryRole = Qt::UserRole + 4, + IsDirectoryRole = Qt::UserRole + 4, IsPlayListRole = Qt::UserRole + 5 }; diff --git a/src/models/filebrowsermodel.cpp b/src/models/filebrowsermodel.cpp --- a/src/models/filebrowsermodel.cpp +++ b/src/models/filebrowsermodel.cpp @@ -67,9 +67,9 @@ auto roles = KDirModel::roleNames(); roles[static_cast(ColumnsRoles::NameRole)] = "name"; - roles[static_cast(ColumnsRoles::ContainerDataRole)] = "containerData"; + roles[static_cast(ColumnsRoles::FileUrlRole)] = "fileUrl"; roles[static_cast(ColumnsRoles::ImageUrlRole)] = "imageUrl"; - roles[static_cast(ColumnsRoles::DirectoryRole)] = "directory"; + roles[static_cast(ColumnsRoles::IsDirectoryRole)] = "isDirectory"; roles[static_cast(ColumnsRoles::IsPlayListRole)] = "isPlaylist"; return roles; @@ -91,7 +91,7 @@ result = item.name(); break; } - case ColumnsRoles::ContainerDataRole: + case ColumnsRoles::FileUrlRole: { KFileItem item = itemForIndex(index); result = item.url().toLocalFile(); @@ -107,7 +107,7 @@ } break; } - case ColumnsRoles::DirectoryRole: + case ColumnsRoles::IsDirectoryRole: { KFileItem item = itemForIndex(index); result = item.isDir(); diff --git a/src/models/filebrowserproxymodel.h b/src/models/filebrowserproxymodel.h --- a/src/models/filebrowserproxymodel.h +++ b/src/models/filebrowserproxymodel.h @@ -65,8 +65,6 @@ bool canGoBack() const; - Q_INVOKABLE MusicAudioTrack loadMetaDataFromUrl(const QUrl &url); - bool sortedAscending() const; void setSourceModel(QAbstractItemModel *sourceModel) override; @@ -85,8 +83,6 @@ void sortModel(Qt::SortOrder order); - void replaceAndPlayOfUrl(const QUrl &fileUrl); - Q_SIGNALS: void filesToEnqueue(const ElisaUtils::EntryDataList &newFiles, @@ -102,8 +98,6 @@ void sortedAscendingChanged(); - void loadPlayListFromUrl(const QUrl &playListUrl); - protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; diff --git a/src/models/filebrowserproxymodel.cpp b/src/models/filebrowserproxymodel.cpp --- a/src/models/filebrowserproxymodel.cpp +++ b/src/models/filebrowserproxymodel.cpp @@ -92,8 +92,8 @@ auto allTrackUrls = ElisaUtils::EntryDataList{}; for (int rowIndex = 0, maxRowCount = rowCount(); rowIndex < maxRowCount; ++rowIndex) { auto currentIndex = index(rowIndex, 0); - if (!data(currentIndex, FileBrowserModel::DirectoryRole).toBool()) { - allTrackUrls.push_back({0, data(currentIndex, FileBrowserModel::ContainerDataRole).toString()}); + if (!data(currentIndex, FileBrowserModel::IsDirectoryRole).toBool()) { + allTrackUrls.push_back({0, data(currentIndex, FileBrowserModel::FileUrlRole).toString()}); } } Q_EMIT filesToEnqueue(allTrackUrls, @@ -110,8 +110,8 @@ auto allTrackUrls = ElisaUtils::EntryDataList{}; for (int rowIndex = 0, maxRowCount = rowCount(); rowIndex < maxRowCount; ++rowIndex) { auto currentIndex = index(rowIndex, 0); - if (!data(currentIndex, FileBrowserModel::DirectoryRole).toBool()) { - allTrackUrls.push_back({0, data(currentIndex, FileBrowserModel::ContainerDataRole).toString()}); + if (!data(currentIndex, FileBrowserModel::IsDirectoryRole).toBool()) { + allTrackUrls.push_back({0, data(currentIndex, FileBrowserModel::FileUrlRole).toString()}); } } Q_EMIT filesToEnqueue(allTrackUrls, @@ -121,19 +121,6 @@ }); } -void FileBrowserProxyModel::replaceAndPlayOfUrl(const QUrl &fileUrl) -{ - if (mMimeDb.mimeTypeForUrl(fileUrl).inherits(QStringLiteral("audio/x-mpegurl"))) - { - Q_EMIT loadPlayListFromUrl(fileUrl); - } else { - Q_EMIT filesToEnqueue({{0, fileUrl.toString()}}, - ElisaUtils::FileName, - ElisaUtils::ReplacePlayList, - ElisaUtils::TriggerPlay); - } -} - QString FileBrowserProxyModel::parentFolder() const { auto fileBrowserModel = dynamic_cast(sourceModel()); @@ -198,13 +185,6 @@ } } -MusicAudioTrack FileBrowserProxyModel::loadMetaDataFromUrl(const QUrl &url) -{ - auto newTrack = mFileScanner.scanOneFile(url, mMimeDb); - qDebug() << "loaded metadata " << url << newTrack; - return newTrack; -} - QString FileBrowserProxyModel::url() const { auto fileBrowserModel = dynamic_cast(sourceModel()); diff --git a/src/qml/FileBrowserDelegate.qml b/src/qml/FileBrowserDelegate.qml deleted file mode 100644 --- a/src/qml/FileBrowserDelegate.qml +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2016-2018 Matthieu Gallien - * Copyright 2018 Alexander Stippich - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.2 - -import org.kde.elisa 1.0 - -FocusScope { - id: gridEntry - - property var fileName - property string fileUrl - property url imageUrl - property bool isDirectory - property bool isPlayList - property bool isSelected - - signal enqueue(var data) - signal replaceAndPlay(var data) - signal loadPlayList(var data) - signal open(var data) - signal selected() - - Loader { - id: metadataLoader - active: false - onLoaded: item.show() - - sourceComponent: MediaTrackMetadataView { - fileName: gridEntry.fileUrl - onRejected: metadataLoader.active = false; - } - } - - Keys.onReturnPressed: gridEntry.enqueue(fileUrl) - Keys.onEnterPressed: gridEntry.enqueue(fileUrl) - - Accessible.role: Accessible.ListItem - Accessible.name: fileName - - Rectangle { - id: stateIndicator - - anchors.fill: parent - z: 1 - - color: "transparent" - opacity: 0.4 - - radius: 3 - } - - ColumnLayout { - anchors.fill: parent - z: 2 - - spacing: 0 - - MouseArea { - id: hoverHandle - - hoverEnabled: true - acceptedButtons: Qt.LeftButton - - Layout.preferredHeight: gridEntry.width * 0.85 + elisaTheme.layoutVerticalMargin * 0.5 + - (mainLabelSize.boundingRect.height - mainLabelSize.boundingRect.y) - Layout.fillWidth: true - - onClicked: gridEntry.selected() - - onDoubleClicked: isDirectory ? open(fileUrl) : enqueue(fileUrl) - - TextMetrics { - id: mainLabelSize - font: mainLabel.font - text: mainLabel.text - } - - ColumnLayout { - id: mainData - - spacing: 0 - anchors.fill: parent - - Item { - Layout.preferredHeight: gridEntry.width * 0.85 - Layout.preferredWidth: gridEntry.width * 0.85 - - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - - Loader { - id: hoverLoader - active: false - - anchors { - bottom: parent.bottom - bottomMargin: 2 - left: parent.left - leftMargin: 2 - } - - z: 1 - - opacity: 0 - - sourceComponent: Row { - spacing: 2 - - Button { - id: detailsButton - - Layout.preferredHeight: elisaTheme.delegateHeight - Layout.preferredWidth: elisaTheme.delegateHeight - - visible: !isDirectory && !isPlayList - - icon.name: "help-about" - onClicked: { - if (metadataLoader.active === false) { - metadataLoader.active = true - } - else { - metadataLoader.item.close(); - metadataLoader.active = false - } - } - - Accessible.onPressAction: onClicked - - ToolTip { - text: i18nc("Show track metadata", "View Details") - } - } - - Button { - id: enqueueOpenButton - - Layout.preferredHeight: elisaTheme.delegateHeight - Layout.preferredWidth: elisaTheme.delegateHeight - - visible: !isPlayList - - icon.name: isDirectory ? - "go-next-view-page" : - "media-track-add-amarok" - onClicked: isDirectory ? - open(fileUrl) : - enqueue(fileUrl) - - Accessible.onPressAction: onClicked - - ToolTip { - text: isDirectory ? - i18nc("Open view of the container", "Open") : - i18nc("Enqueue current track", "Enqueue") - } - } - - Button { - id: replaceAndPlayButton - - Layout.preferredHeight: elisaTheme.delegateHeight - Layout.preferredWidth: elisaTheme.delegateHeight - - scale: LayoutMirroring.enabled ? -1 : 1 - visible: !isDirectory - - icon.name: "media-playback-start" - onClicked: replaceAndPlay(fileUrl) - - Accessible.onPressAction: onClicked - - ToolTip { - text: i18nc("Clear play list and enqueue current track", "Play Now and Replace Play List") - } - } - } - } - - Image { - id: icon - anchors.fill: parent - - sourceSize.width: parent.width - sourceSize.height: parent.height - fillMode: Image.PreserveAspectFit - smooth: true - - source: imageUrl - - asynchronous: true - } - } - - LabelWithToolTip { - id: mainLabel - - font.weight: Font.Bold - color: myPalette.text - - // FIXME: Center-aligned text looks better overall, but - // sometimes results in font kerning issues - // See https://bugreports.qt.io/browse/QTBUG-49646 - horizontalAlignment: Text.AlignHCenter - - Layout.topMargin: elisaTheme.layoutVerticalMargin * 0.5 - Layout.maximumWidth: gridEntry.width * 0.9 - Layout.minimumWidth: Layout.maximumWidth - Layout.maximumHeight: (mainLabelSize.boundingRect.height - mainLabelSize.boundingRect.y) * 2 - Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom - - text: fileName - wrapMode: Label.Wrap - elide: Text.ElideRight - } - - Item { - Layout.fillHeight: true - } - } - } - - Item { - Layout.fillHeight: true - } - } - - states: [ - State { - name: 'notSelected' - when: !gridEntry.activeFocus && !hoverHandle.containsMouse && !gridEntry.isSelected - PropertyChanges { - target: stateIndicator - color: 'transparent' - } - PropertyChanges { - target: stateIndicator - opacity: 1.0 - } - PropertyChanges { - target: hoverLoader - active: false - } - PropertyChanges { - target: hoverLoader - opacity: 0.0 - } - }, - State { - name: 'hovered' - when: hoverHandle.containsMouse && !gridEntry.activeFocus - PropertyChanges { - target: stateIndicator - color: myPalette.highlight - } - PropertyChanges { - target: stateIndicator - opacity: 0.2 - } - PropertyChanges { - target: hoverLoader - active: true - } - PropertyChanges { - target: hoverLoader - opacity: 1.0 - } - }, - State { - name: 'selected' - when: gridEntry.isSelected && !gridEntry.activeFocus - PropertyChanges { - target: stateIndicator - color: myPalette.mid - } - PropertyChanges { - target: stateIndicator - opacity: 0.6 - } - PropertyChanges { - target: hoverLoader - active: false - } - PropertyChanges { - target: hoverLoader - opacity: 0. - } - }, - State { - name: 'hoveredOrSelected' - when: gridEntry.activeFocus - PropertyChanges { - target: stateIndicator - color: myPalette.highlight - } - PropertyChanges { - target: stateIndicator - opacity: 0.6 - } - PropertyChanges { - target: hoverLoader - active: true - } - PropertyChanges { - target: hoverLoader - opacity: 1.0 - } - } - ] -} - diff --git a/src/qml/FileBrowserView.qml b/src/qml/FileBrowserView.qml --- a/src/qml/FileBrowserView.qml +++ b/src/qml/FileBrowserView.qml @@ -48,8 +48,6 @@ sourceModel: realModel - onLoadPlayListFromUrl: elisa.mediaPlayList.loadPlaylist(playListUrl) - onFilesToEnqueue: elisa.mediaPlayList.enqueue(newFiles, databaseIdType, enqueueMode, triggerPlay) } @@ -114,6 +112,11 @@ model: proxyModel + TextMetrics { + id: secondaryLabelSize + text: 'example' + } + ScrollHelper { id: scrollHelper flickable: contentDirectoryView @@ -139,24 +142,33 @@ } cellWidth: elisaTheme.gridDelegateWidth - cellHeight:elisaTheme.gridDelegateHeight + cellHeight: elisaTheme.gridDelegateHeight - (secondaryLabelSize.boundingRect.height - secondaryLabelSize.boundingRect.y) - delegate: FileBrowserDelegate { + delegate: GridBrowserDelegate { width: contentDirectoryView.cellWidth height: contentDirectoryView.cellHeight focus: true isSelected: contentDirectoryView.currentIndex === index - isDirectory: model.directory - isPlayList: model.isPlaylist - fileName: model.name - fileUrl: model.containerData + mainText: model.name + delegateDisplaySecondaryText: false + fileUrl: model.fileUrl imageUrl: model.imageUrl - - onEnqueue: elisa.mediaPlayList.enqueue(0, data, ElisaUtils.FileName, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) - onReplaceAndPlay: elisa.mediaPlayList.enqueue(0, data, ElisaUtils.FileName, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) + showDetailsButton: !model.isDirectory && !model.isPlaylist + showEnqueueButton: !model.isDirectory && !model.isPlaylist + showPlayButton: !model.isDirectory + showOpenButton: model.isDirectory && !model.isPlaylist + + onEnqueue: elisa.mediaPlayList.enqueue(0, model.fileUrl, ElisaUtils.FileName, ElisaUtils.AppendPlayList, ElisaUtils.DoNotTriggerPlay) + onReplaceAndPlay: { + if (model.isPlaylist) { + elisa.mediaPlayList.loadPlaylist(model.fileUrl) + } else { + elisa.mediaPlayList.enqueue(0, model.fileUrl, ElisaUtils.FileName, ElisaUtils.ReplacePlayList, ElisaUtils.TriggerPlay) + } + } onSelected: { forceActiveFocus() contentDirectoryView.currentIndex = model.index @@ -168,7 +180,7 @@ } } - onOpen: loadFolderAndClear(data) + onOpen: loadFolderAndClear(model.fileUrl) } } } diff --git a/src/qml/GridBrowserDelegate.qml b/src/qml/GridBrowserDelegate.qml --- a/src/qml/GridBrowserDelegate.qml +++ b/src/qml/GridBrowserDelegate.qml @@ -28,18 +28,35 @@ property var imageUrl property bool shadowForImage + property string fileUrl property alias mainText: mainLabel.text property alias secondaryText: secondaryLabel.text property var databaseId property bool delegateDisplaySecondaryText: true property bool isPartial property bool isSelected + property bool showDetailsButton: false + property bool showOpenButton: true + property bool showPlayButton: true + property bool showEnqueueButton: true signal enqueue(var databaseId, var name) signal replaceAndPlay(var databaseId, var name) signal open() signal selected() + Loader { + id: metadataLoader + active: false && gridEntry.fileUrl + onLoaded: item.show() + + sourceComponent: MediaTrackMetadataView { + fileName: gridEntry.fileUrl ? gridEntry.fileUrl : '' + onRejected: metadataLoader.active = false; + } + } + + Keys.onReturnPressed: open() Keys.onEnterPressed: open() @@ -124,6 +141,41 @@ sourceComponent: Row { spacing: 2 + Button { + id: detailsButton + objectName: 'detailsButton' + + icon.name: 'help-about' + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.delay: 1000 + ToolTip.text: i18nc("Show track metadata", "View Details") + + Accessible.role: Accessible.Button + Accessible.name: ToolTip.text + Accessible.description: ToolTip.text + Accessible.onPressAction: onClicked + + onClicked: { + if (metadataLoader.active === false) { + metadataLoader.active = true + } + else { + metadataLoader.item.close(); + metadataLoader.active = false + } + } + + Keys.onReturnPressed: replaceAndPlay(databaseId, mainText) + Keys.onEnterPressed: replaceAndPlay(databaseId, mainText) + + visible: showDetailsButton + + width: elisaTheme.delegateToolButtonSize + height: elisaTheme.delegateToolButtonSize + } + Button { id: replaceAndPlayButton objectName: 'replaceAndPlayButton' @@ -144,8 +196,7 @@ Keys.onReturnPressed: replaceAndPlay(databaseId, mainText) Keys.onEnterPressed: replaceAndPlay(databaseId, mainText) - - visible: databaseId !== undefined + visible: showPlayButton width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize @@ -170,7 +221,7 @@ Keys.onReturnPressed: enqueue(databaseId, mainText) Keys.onEnterPressed: enqueue(databaseId, mainText) - visible: databaseId !== undefined + visible: showEnqueueButton width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize @@ -193,6 +244,8 @@ onClicked: open() + visible: showOpenButton + width: elisaTheme.delegateToolButtonSize height: elisaTheme.delegateToolButtonSize } diff --git a/src/resources.qrc b/src/resources.qrc --- a/src/resources.qrc +++ b/src/resources.qrc @@ -23,7 +23,6 @@ qml/GridBrowserDelegate.qml qml/ListBrowserView.qml qml/ListBrowserDelegate.qml - qml/FileBrowserDelegate.qml qml/FileBrowserView.qml qtquickcontrols2.conf background.png