diff --git a/src/models/filebrowserproxymodel.cpp b/src/models/filebrowserproxymodel.cpp index 96767b21..e639c59f 100644 --- a/src/models/filebrowserproxymodel.cpp +++ b/src/models/filebrowserproxymodel.cpp @@ -1,183 +1,194 @@ /* * Copyright 2016-2017 Matthieu Gallien * Copyright 2018 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "filebrowserproxymodel.h" #include "filebrowsermodel.h" #include #include #include #include #include "elisautils.h" FileBrowserProxyModel::FileBrowserProxyModel(QObject *parent) : KDirSortFilterProxyModel(parent) { setFilterCaseSensitivity(Qt::CaseInsensitive); mThreadPool.setMaxThreadCount(1); mFileModel = std::make_unique(); setSourceModel(mFileModel.get()); setSortFoldersFirst(true); sort(Qt::AscendingOrder); QObject::connect(mFileModel.get(), &FileBrowserModel::urlChanged,this, &FileBrowserProxyModel::urlChanged); mTopFolder = QDir::homePath(); openFolder(mTopFolder, true); } FileBrowserProxyModel::~FileBrowserProxyModel() { } QString FileBrowserProxyModel::filterText() const { return mFilterText; } void FileBrowserProxyModel::setFilterText(const QString &filterText) { QWriteLocker writeLocker(&mDataLock); if (mFilterText == filterText) return; mFilterText = filterText; mFilterExpression.setPattern(mFilterText); mFilterExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption); mFilterExpression.optimize(); invalidate(); Q_EMIT filterTextChanged(mFilterText); } bool FileBrowserProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { bool result = false; for (int column = 0, columnCount = sourceModel()->columnCount(source_parent); column < columnCount; ++column) { auto currentIndex = sourceModel()->index(source_row, column, source_parent); const auto &nameValue = sourceModel()->data(currentIndex, FileBrowserModel::NameRole).toString(); if (mFilterExpression.match(nameValue).hasMatch()) { result = true; continue; } if (result) { continue; } if (!result) { break; } } return result; } void FileBrowserProxyModel::enqueueToPlayList() { qDebug() << "enqueue"; QtConcurrent::run(&mThreadPool, [=] () { QReadLocker locker(&mDataLock); auto allTrackUrls = QList(); for (int rowIndex = 0, maxRowCount = rowCount(); rowIndex < maxRowCount; ++rowIndex) { auto currentIndex = index(rowIndex, 0); if (!data(currentIndex, FileBrowserModel::DirectoryRole).toBool()) { allTrackUrls.push_back(data(currentIndex, FileBrowserModel::ContainerDataRole).toUrl()); } } Q_EMIT filesToEnqueue(allTrackUrls, ElisaUtils::AppendPlayList, ElisaUtils::DoNotTriggerPlay); }); } void FileBrowserProxyModel::replaceAndPlayOfPlayList() { qDebug() << "replace"; QtConcurrent::run(&mThreadPool, [=] () { QReadLocker locker(&mDataLock); auto allTrackUrls = QList(); for (int rowIndex = 0, maxRowCount = rowCount(); rowIndex < maxRowCount; ++rowIndex) { auto currentIndex = index(rowIndex, 0); if (!data(currentIndex, FileBrowserModel::DirectoryRole).toBool()) { allTrackUrls.push_back(data(currentIndex, FileBrowserModel::ContainerDataRole).toUrl()); } } Q_EMIT filesToEnqueue(allTrackUrls, ElisaUtils::ReplacePlayList, ElisaUtils::TriggerPlay); }); } QString FileBrowserProxyModel::parentFolder() const { //return to the top folder if parent directory does not exist QDir dir(mFileModel->dirLister()->url().toLocalFile()); if (dir.cdUp()) { return dir.path(); } else { return mTopFolder; } } void FileBrowserProxyModel::openParentFolder() { if (canGoBack()) { QString parent = parentFolder(); mFileModel->setUrl(parent); if (parent == mTopFolder) { Q_EMIT canGoBackChanged(); } } } bool FileBrowserProxyModel::canGoBack() const { return mFileModel->dirLister()->url().toLocalFile() != mTopFolder; } void FileBrowserProxyModel::openFolder(const QString &folder, bool isDisplayRoot) { if (folder.isEmpty()) { return; } mFileModel->setUrl(folder); if (!isDisplayRoot) { Q_EMIT canGoBackChanged(); } } MusicAudioTrack FileBrowserProxyModel::loadMetaDataFromUrl(const QUrl &url) { auto newTrack = ElisaUtils::scanOneFile(url,mMimeDb,mExtractors); qDebug() << "loaded metadata " << url << newTrack; return newTrack; } QString FileBrowserProxyModel::url() const { return mFileModel->dirLister()->url().toLocalFile(); } +bool FileBrowserProxyModel::sortedAscending() const +{ + return sortOrder() ? false : true; +} + +void FileBrowserProxyModel::sortModel(Qt::SortOrder order) +{ + this->sort(0,order); + Q_EMIT sortedAscendingChanged(); +} + #include "moc_filebrowserproxymodel.cpp" diff --git a/src/models/filebrowserproxymodel.h b/src/models/filebrowserproxymodel.h index f46ecf26..07183e0e 100644 --- a/src/models/filebrowserproxymodel.h +++ b/src/models/filebrowserproxymodel.h @@ -1,113 +1,128 @@ /* * Copyright 2016-2018 Matthieu Gallien * Copyright 2018 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef FILEBROWSERPROXYMODEL_H #define FILEBROWSERPROXYMODEL_H #include "elisaLib_export.h" #include #include #include #include #include #include "filebrowsermodel.h" #include "musicaudiotrack.h" #include "elisautils.h" class ELISALIB_EXPORT FileBrowserProxyModel : public KDirSortFilterProxyModel { Q_OBJECT Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged) - Q_PROPERTY(bool canGoBack READ canGoBack NOTIFY canGoBackChanged) + Q_PROPERTY(bool canGoBack + READ canGoBack + NOTIFY canGoBackChanged) - Q_PROPERTY(QString url READ url NOTIFY urlChanged) + Q_PROPERTY(QString url + READ url + NOTIFY urlChanged) + + Q_PROPERTY(bool sortedAscending + READ sortedAscending + NOTIFY sortedAscendingChanged) public: explicit FileBrowserProxyModel(QObject *parent = nullptr); ~FileBrowserProxyModel() override; QString filterText() const; QString url() const; bool canGoBack() const; Q_INVOKABLE MusicAudioTrack loadMetaDataFromUrl(const QUrl &url); public Q_SLOTS: void enqueueToPlayList(); void replaceAndPlayOfPlayList(); void setFilterText(const QString &filterText); + void openParentFolder(); void openFolder(const QString &folder, bool isDisplayRoot = false); + bool sortedAscending() const; + + void sortModel(Qt::SortOrder order); + Q_SIGNALS: void filesToEnqueue(QList newFiles, ElisaUtils::PlayListEnqueueMode enqueueMode, ElisaUtils::PlayListEnqueueTriggerPlay triggerPlay); void urlChanged(); void canGoBackChanged(); void filterTextChanged(const QString &filterText); + void sortedAscendingChanged(); + protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; private: QString parentFolder() const; QString mTopFolder; KFileMetaData::ExtractorCollection mExtractors; QMimeDatabase mMimeDb; QString mFilterText; QRegularExpression mFilterExpression; QReadWriteLock mDataLock; QThreadPool mThreadPool; std::unique_ptr mFileModel; }; #endif // FILEBROWSERPROXYMODEL_H diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index cb4b16ab..153805bb 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,799 +1,809 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.elisa 1.0 RowLayout { id: contentViewContainer spacing: 0 signal toggleSearch() function goBack() { localAlbums.goBack() localArtists.goBack() } ViewSelector { id: listViews Layout.fillHeight: true Layout.preferredWidth: mainWindow.width * 0.11 Layout.maximumWidth: mainWindow.width * 0.11 } Rectangle { id: viewSelectorSeparatorItem border.width: 1 border.color: myPalette.mid color: myPalette.mid visible: true Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true Layout.preferredWidth: 1 Layout.minimumWidth: 1 Layout.maximumWidth: 1 } ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true spacing: 0 TopNotification { id: invalidBalooConfiguration Layout.fillWidth: true musicManager: elisa.musicManager focus: true } Item { Layout.fillHeight: true Layout.fillWidth: true RowLayout { anchors.fill: parent spacing: 0 id: contentZone FocusScope { id: mainContentView focus: true Layout.fillHeight: true Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 visible: Layout.minimumWidth != 0 Rectangle { border { color: (mainContentView.activeFocus ? myPalette.highlight : myPalette.base) width: 1 } radius: 3 color: myPalette.base anchors.fill: parent Loader { anchors.fill: parent anchors.leftMargin: parent.width / 3 anchors.rightMargin: parent.width / 3 anchors.topMargin: parent.height / 3 anchors.bottomMargin: parent.height / 3 z: 2 sourceComponent: BusyIndicator { id: busyScanningMusic hoverEnabled: false anchors.fill: parent opacity: 0.8 visible: true running: true z: 2 } active: elisa.musicManager.indexerBusy } Loader { id: localAlbumsLoader active: opacity > 0 visible: opacity > 0 anchors.fill: parent sourceComponent: MediaBrowser { id: localAlbums focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: GridBrowserView { id: allAlbumsView focus: true contentModel: elisa.allAlbumsProxyModel image: elisaTheme.albumIcon mainTitle: i18nc("Title of the view of all albums", "Albums") onOpen: { elisa.singleAlbumProxyModel.loadAlbumData(databaseId) localAlbums.stackView.push(albumView, { mainTitle: innerMainTitle, secondaryTitle: innerSecondaryTitle, image: innerImage, stackView: localAlbums.stackView, }) } onGoBack: localAlbums.stackView.pop() Binding { target: allAlbumsView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } Loader { id: localArtistsLoader active: opacity > 0 visible: opacity > 0 anchors.fill: parent sourceComponent: MediaBrowser { id: localArtists focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: GridBrowserView { id: allArtistsView focus: true showRating: false delegateDisplaySecondaryText: false contentModel: elisa.allArtistsProxyModel image: elisaTheme.artistIcon mainTitle: i18nc("Title of the view of all artists", "Artists") onOpen: { elisa.singleArtistProxyModel.setArtistFilterText(innerMainTitle) localArtists.stackView.push(innerAlbumView, { mainTitle: innerMainTitle, secondaryTitle: innerSecondaryTitle, image: innerImage, }) } onGoBack: localArtists.stackView.pop() Binding { target: allArtistsView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } Loader { id: localTracksLoader active: opacity > 0 visible: opacity > 0 anchors.fill: parent sourceComponent: MediaBrowser { id: localTracks focus: true anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } firstPage: ListBrowserView { id: allTracksView focus: true contentModel: elisa.allTracksProxyModel delegate: MediaTrackDelegate { id: entry width: allTracksView.delegateWidth height: elisaTheme.trackDelegateHeight focus: true trackData: model.containerData isFirstTrackOfDisc: false isSingleDiscAlbum: model.isSingleDiscAlbum onEnqueue: elisa.mediaPlayList.enqueue(data) onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) onClicked: contentDirectoryView.currentIndex = index } image: elisaTheme.tracksIcon mainTitle: i18nc("Title of the view of all tracks", "Tracks") Binding { target: allTracksView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } - FileBrowserView { - id: localFiles + Loader { + id: localFilesLoader + + active: opacity > 0 - focus: true + visible: opacity > 0 anchors { fill: parent leftMargin: elisaTheme.layoutHorizontalMargin rightMargin: elisaTheme.layoutHorizontalMargin } - contentModel: elisa.fileBrowserProxyModel + sourceComponent: FileBrowserView { + id: localFiles + + focus: true + + contentModel: elisa.fileBrowserProxyModel - Binding { - target: localFiles - property: 'expandedFilterView' - value: persistentSettings.expandedFilterView + Binding { + target: localFiles + property: 'expandedFilterView' + value: persistentSettings.expandedFilterView + } + + onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } + } - visible: opacity > 0 - onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView - } Behavior on border.color { ColorAnimation { duration: 300 } } } } Rectangle { id: firstViewSeparatorItem border.width: 1 border.color: myPalette.mid color: myPalette.mid visible: true Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true Layout.preferredWidth: 1 Layout.minimumWidth: 1 Layout.maximumWidth: 1 } MediaPlayListView { id: playList playListModel: elisa.mediaPlayList Layout.fillHeight: true Layout.leftMargin: elisaTheme.layoutHorizontalMargin Layout.rightMargin: elisaTheme.layoutHorizontalMargin Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width onStartPlayback: elisa.audioControl.ensurePlay() onPausePlayback: elisa.audioControl.playPause() onDisplayError: messageNotification.showNotification(errorText) } Rectangle { id: viewSeparatorItem border.width: 1 border.color: myPalette.mid color: myPalette.mid visible: Layout.minimumWidth != 0 Layout.bottomMargin: elisaTheme.layoutVerticalMargin Layout.topMargin: elisaTheme.layoutVerticalMargin Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.fillHeight: true Layout.preferredWidth: 1 Layout.minimumWidth: 1 Layout.maximumWidth: 1 } ContextView { id: albumContext Layout.fillHeight: true Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width visible: Layout.minimumWidth != 0 artistName: elisa.manageHeaderBar.artist albumName: elisa.manageHeaderBar.album albumArtUrl: elisa.manageHeaderBar.image } } } states: [ - State { - name: 'full' - when: listViews.currentIndex === 0 - PropertyChanges { - target: mainContentView - Layout.fillWidth: false - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: firstViewSeparatorItem - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: playList - Layout.minimumWidth: contentZone.width / 2 - Layout.maximumWidth: contentZone.width / 2 - Layout.preferredWidth: contentZone.width / 2 - } - PropertyChanges { - target: viewSeparatorItem - Layout.minimumWidth: 1 - Layout.maximumWidth: 1 - Layout.preferredWidth: 1 - } - PropertyChanges { - target: albumContext - Layout.minimumWidth: contentZone.width / 2 - Layout.maximumWidth: contentZone.width / 2 - Layout.preferredWidth: contentZone.width / 2 - } - PropertyChanges { - target: localAlbums - opacity: 0 - } - PropertyChanges { - target: localArtists - opacity: 0 - } - PropertyChanges { - target: localTracks - opacity: 0 - } - PropertyChanges { - target: localFiles - opacity: 0 - } - }, - State { - name: 'allAlbums' - when: listViews.currentIndex === 1 - StateChangeScript { - script: { - localAlbums.stackView.pop({item: null, immediate: true}) - } - } - PropertyChanges { - target: mainContentView - Layout.fillWidth: true - Layout.minimumWidth: contentZone.width * 0.66 - Layout.maximumWidth: contentZone.width * 0.68 - Layout.preferredWidth: contentZone.width * 0.68 - } - PropertyChanges { - target: firstViewSeparatorItem - Layout.minimumWidth: 1 - Layout.maximumWidth: 1 - Layout.preferredWidth: 1 - } - PropertyChanges { - target: playList - Layout.minimumWidth: contentZone.width * 0.33 - Layout.maximumWidth: contentZone.width * 0.33 - Layout.preferredWidth: contentZone.width * 0.33 - } - PropertyChanges { - target: viewSeparatorItem - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: albumContext - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: localAlbums - opacity: 1 - } - PropertyChanges { - target: localArtists - opacity: 0 - } - PropertyChanges { - target: localTracks - opacity: 0 - } - PropertyChanges { - target: localFiles - opacity: 0 - } - }, - State { - name: 'allArtists' - when: listViews.currentIndex === 2 - StateChangeScript { - script: { - localArtists.stackView.pop({item: null, immediate: true}) - } - } - PropertyChanges { - target: mainContentView - Layout.fillWidth: true - Layout.minimumWidth: contentZone.width * 0.66 - Layout.maximumWidth: contentZone.width * 0.68 - Layout.preferredWidth: contentZone.width * 0.68 - } - PropertyChanges { - target: firstViewSeparatorItem - Layout.minimumWidth: 1 - Layout.maximumWidth: 1 - Layout.preferredWidth: 1 - } - PropertyChanges { - target: playList - Layout.minimumWidth: contentZone.width * 0.33 - Layout.maximumWidth: contentZone.width * 0.33 - Layout.preferredWidth: contentZone.width * 0.33 - } - PropertyChanges { - target: viewSeparatorItem - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: albumContext - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: localAlbums - opacity: 0 - } - PropertyChanges { - target: localArtists - opacity: 1 - } - PropertyChanges { - target: localTracks - opacity: 0 - } - PropertyChanges { - target: localFiles - opacity: 0 - } - }, - State { - name: 'allTracks' - when: listViews.currentIndex === 3 - PropertyChanges { - target: mainContentView - Layout.fillWidth: true - Layout.minimumWidth: contentZone.width * 0.66 - Layout.maximumWidth: contentZone.width * 0.68 - Layout.preferredWidth: contentZone.width * 0.68 - } - PropertyChanges { - target: firstViewSeparatorItem - Layout.minimumWidth: 1 - Layout.maximumWidth: 1 - Layout.preferredWidth: 1 - } - PropertyChanges { - target: playList - Layout.minimumWidth: contentZone.width * 0.33 - Layout.maximumWidth: contentZone.width * 0.33 - Layout.preferredWidth: contentZone.width * 0.33 - } - PropertyChanges { - target: viewSeparatorItem - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: albumContext - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: localAlbums - opacity: 0 - } - PropertyChanges { - target: localArtists - opacity: 0 - } - PropertyChanges { - target: localTracks - opacity: 1 - } - PropertyChanges { - target: localFiles - opacity: 0 - } - }, - State { - name: 'files' - when: listViews.currentIndex === 4 - PropertyChanges { - target: mainContentView - Layout.fillWidth: true - Layout.minimumWidth: contentZone.width * 0.66 - Layout.maximumWidth: contentZone.width * 0.68 - Layout.preferredWidth: contentZone.width * 0.68 - } - PropertyChanges { - target: firstViewSeparatorItem - Layout.minimumWidth: 1 - Layout.maximumWidth: 1 - Layout.preferredWidth: 1 - } - PropertyChanges { - target: playList - Layout.minimumWidth: contentZone.width * 0.33 - Layout.maximumWidth: contentZone.width * 0.33 - Layout.preferredWidth: contentZone.width * 0.33 - } - PropertyChanges { - target: viewSeparatorItem - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: albumContext - Layout.minimumWidth: 0 - Layout.maximumWidth: 0 - Layout.preferredWidth: 0 - } - PropertyChanges { - target: localAlbums - opacity: 0 - } - PropertyChanges { - target: localArtists - opacity: 0 - } - PropertyChanges { - target: localTracks - opacity: 0 - } - PropertyChanges { - target: localFiles - opacity: 1 - } - } - ] - transitions: Transition { - NumberAnimation { - properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" - easing.type: Easing.InOutQuad - duration: 300 - } - } - } + State { + name: 'full' + when: listViews.currentIndex === 0 + PropertyChanges { + target: mainContentView + Layout.fillWidth: false + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: firstViewSeparatorItem + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: playList + Layout.minimumWidth: contentZone.width / 2 + Layout.maximumWidth: contentZone.width / 2 + Layout.preferredWidth: contentZone.width / 2 + } + PropertyChanges { + target: viewSeparatorItem + Layout.minimumWidth: 1 + Layout.maximumWidth: 1 + Layout.preferredWidth: 1 + } + PropertyChanges { + target: albumContext + Layout.minimumWidth: contentZone.width / 2 + Layout.maximumWidth: contentZone.width / 2 + Layout.preferredWidth: contentZone.width / 2 + } + PropertyChanges { + target: localAlbumsLoader + opacity: 0 + } + PropertyChanges { + target: localArtistsLoader + opacity: 0 + } + PropertyChanges { + target: localTracksLoader + opacity: 0 + } + PropertyChanges { + target: localFilesLoader + opacity: 0 + } + }, + State { + name: 'allAlbums' + when: listViews.currentIndex === 1 + StateChangeScript { + script: { + if (localAlbumsLoader.localAlbums) + localAlbumsLoader.localAlbums.stackView.pop({item: null, immediate: true}) + } + } + PropertyChanges { + target: mainContentView + Layout.fillWidth: true + Layout.minimumWidth: contentZone.width * 0.66 + Layout.maximumWidth: contentZone.width * 0.68 + Layout.preferredWidth: contentZone.width * 0.68 + } + PropertyChanges { + target: firstViewSeparatorItem + Layout.minimumWidth: 1 + Layout.maximumWidth: 1 + Layout.preferredWidth: 1 + } + PropertyChanges { + target: playList + Layout.minimumWidth: contentZone.width * 0.33 + Layout.maximumWidth: contentZone.width * 0.33 + Layout.preferredWidth: contentZone.width * 0.33 + } + PropertyChanges { + target: viewSeparatorItem + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: albumContext + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: localAlbumsLoader + opacity: 1 + } + PropertyChanges { + target: localArtistsLoader + opacity: 0 + } + PropertyChanges { + target: localTracksLoader + opacity: 0 + } + PropertyChanges { + target: localFilesLoader + opacity: 0 + } + }, + State { + name: 'allArtists' + when: listViews.currentIndex === 2 + StateChangeScript { + script: { + if (localArtistsLoader.localArtists) + localArtistsLoader.localArtists.stackView.pop({item: null, immediate: true}) + } + } + PropertyChanges { + target: mainContentView + Layout.fillWidth: true + Layout.minimumWidth: contentZone.width * 0.66 + Layout.maximumWidth: contentZone.width * 0.68 + Layout.preferredWidth: contentZone.width * 0.68 + } + PropertyChanges { + target: firstViewSeparatorItem + Layout.minimumWidth: 1 + Layout.maximumWidth: 1 + Layout.preferredWidth: 1 + } + PropertyChanges { + target: playList + Layout.minimumWidth: contentZone.width * 0.33 + Layout.maximumWidth: contentZone.width * 0.33 + Layout.preferredWidth: contentZone.width * 0.33 + } + PropertyChanges { + target: viewSeparatorItem + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: albumContext + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: localAlbumsLoader + opacity: 0 + } + PropertyChanges { + target: localArtistsLoader + opacity: 1 + } + PropertyChanges { + target: localTracksLoader + opacity: 0 + } + PropertyChanges { + target: localFilesLoader + opacity: 0 + } + }, + State { + name: 'allTracks' + when: listViews.currentIndex === 3 + PropertyChanges { + target: mainContentView + Layout.fillWidth: true + Layout.minimumWidth: contentZone.width * 0.66 + Layout.maximumWidth: contentZone.width * 0.68 + Layout.preferredWidth: contentZone.width * 0.68 + } + PropertyChanges { + target: firstViewSeparatorItem + Layout.minimumWidth: 1 + Layout.maximumWidth: 1 + Layout.preferredWidth: 1 + } + PropertyChanges { + target: playList + Layout.minimumWidth: contentZone.width * 0.33 + Layout.maximumWidth: contentZone.width * 0.33 + Layout.preferredWidth: contentZone.width * 0.33 + } + PropertyChanges { + target: viewSeparatorItem + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: albumContext + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: localAlbumsLoader + opacity: 0 + } + PropertyChanges { + target: localArtistsLoader + opacity: 0 + } + PropertyChanges { + target: localTracksLoader + opacity: 1 + } + PropertyChanges { + target: localFilesLoader + opacity: 0 + } + }, + State { + name: 'files' + when: listViews.currentIndex === 4 + PropertyChanges { + target: mainContentView + Layout.fillWidth: true + Layout.minimumWidth: contentZone.width * 0.66 + Layout.maximumWidth: contentZone.width * 0.68 + Layout.preferredWidth: contentZone.width * 0.68 + } + PropertyChanges { + target: firstViewSeparatorItem + Layout.minimumWidth: 1 + Layout.maximumWidth: 1 + Layout.preferredWidth: 1 + } + PropertyChanges { + target: playList + Layout.minimumWidth: contentZone.width * 0.33 + Layout.maximumWidth: contentZone.width * 0.33 + Layout.preferredWidth: contentZone.width * 0.33 + } + PropertyChanges { + target: viewSeparatorItem + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: albumContext + Layout.minimumWidth: 0 + Layout.maximumWidth: 0 + Layout.preferredWidth: 0 + } + PropertyChanges { + target: localAlbumsLoader + opacity: 0 + } + PropertyChanges { + target: localArtistsLoader + opacity: 0 + } + PropertyChanges { + target: localTracksLoader + opacity: 0 + } + PropertyChanges { + target: localFilesLoader + opacity: 1 + } + } + ] + transitions: Transition { + NumberAnimation { + properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" + easing.type: Easing.InOutQuad + duration: 300 + } + } + } Component { id: innerAlbumView GridBrowserView { id: innerAlbumGridView contentModel: elisa.singleArtistProxyModel isSubPage: true onOpen: { elisa.singleAlbumProxyModel.loadAlbumData(databaseId) localArtists.stackView.push(albumView, { mainTitle: innerMainTitle, secondaryTitle: innerSecondaryTitle, image: innerImage, stackView: localArtists.stackView, }) } onGoBack: localArtists.stackView.pop() Binding { target: innerAlbumGridView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } Component { id: albumView ListBrowserView { id: albumGridView property var stackView contentModel: elisa.singleAlbumProxyModel isSubPage: true delegate: MediaAlbumTrackDelegate { id: entry width: albumGridView.delegateWidth height: ((model.isFirstTrackOfDisc && !isSingleDiscAlbum) ? elisaTheme.delegateHeight*2 : elisaTheme.delegateHeight) focus: true mediaTrack.trackData: model.containerData mediaTrack.isFirstTrackOfDisc: model.isFirstTrackOfDisc mediaTrack.isSingleDiscAlbum: model.isSingleDiscAlbum mediaTrack.onEnqueue: elisa.mediaPlayList.enqueue(data) mediaTrack.onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) mediaTrack.isAlternateColor: (index % 2) === 1 mediaTrack.onClicked: contentDirectoryView.currentIndex = index } allowArtistNavigation: true onShowArtist: { listViews.currentIndex = 2 if (localArtists.stackView.depth === 3) { localArtists.stackView.pop() } if (localArtists.stackView.depth === 2) { var artistPage = localArtists.stackView.get(1) if (artistPage.mainTitle === name) { return } else { localArtists.stackView.pop() } } allArtistsView.open(name, name, elisaTheme.defaultArtistImage, '') } onGoBack: stackView.pop() expandedFilterView: true Binding { target: albumGridView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } diff --git a/src/qml/FileBrowserView.qml b/src/qml/FileBrowserView.qml index 185bc97e..0f5e4545 100644 --- a/src/qml/FileBrowserView.qml +++ b/src/qml/FileBrowserView.qml @@ -1,155 +1,157 @@ /* * Copyright 2016-2018 Matthieu Gallien * Copyright 2018 Alexander Stippich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import org.kde.elisa 1.0 FocusScope { id: fileView property bool isSubPage: false property alias contentModel: contentDirectoryView.model property alias expandedFilterView: navigationBar.expandedFilterView signal filterViewChanged(bool expandedFilterView) function goBack() { contentModel.openParentFolder() } function loadFolderAndClear(data) { contentModel.openFolder(data) navigationBar.filterText = "" } SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } MouseArea { anchors.fill: parent hoverEnabled: false acceptedButtons: Qt.BackButton onClicked: contentModel.openParentFolder() } ColumnLayout { anchors.fill: parent spacing: 0 NavigationActionBar { id: navigationBar mainTitle: i18nc("Title of the file browser view", "Files") image: elisaTheme.folderIcon secondaryTitle: contentModel.url enableGoBack: contentModel.canGoBack + sortOrder: contentModel.sortedAscending showRating: false height: elisaTheme.navigationBarHeight Layout.preferredHeight: height Layout.minimumHeight: height Layout.maximumHeight: height Layout.fillWidth: true Binding { target: contentModel property: 'filterText' value: navigationBar.filterText } onEnqueue: contentModel.enqueueToPlayList() onReplaceAndPlay: contentModel.replaceAndPlayOfPlayList() onGoBack: contentModel.openParentFolder() onFilterViewChanged: fileView.filterViewChanged(expandedFilterView) + onSort: contentModel.sortModel(order) } Rectangle { color: myPalette.base Layout.fillHeight: true Layout.fillWidth: true clip: true GridView { id: contentDirectoryView anchors.topMargin: 20 anchors.fill: parent focus: true ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds add: Transition { PropertyAnimation { property: "opacity" from: 0 to: 1 duration: 100 } } remove: Transition { PropertyAnimation { property: "opacity" from: 0 to: 1 duration: 100 } } cellWidth: elisaTheme.gridDelegateWidth cellHeight:elisaTheme.gridDelegateHeight delegate: FileBrowserDelegate { width: contentDirectoryView.cellWidth height: contentDirectoryView.cellHeight focus: true isDirectory: model.directory fileName: model.name fileUrl: model.containerData imageUrl: model.imageUrl contentModel: fileView.contentModel onEnqueue: elisa.mediaPlayList.enqueue(data) onReplaceAndPlay: elisa.mediaPlayList.replaceAndPlay(data) onSelected: { forceActiveFocus() contentDirectoryView.currentIndex = model.index } onOpen: loadFolderAndClear(data) } } } } }