diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b2e0790b..44eb5e72 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,291 +1,292 @@ include_directories(${elisa_BINARY_DIR}) set(elisaLib_SOURCES mediaplaylist.cpp musicalbum.cpp musicaudiotrack.cpp musicartist.cpp progressindicator.cpp databaseinterface.cpp musiclistenersmanager.cpp managemediaplayercontrol.cpp manageheaderbar.cpp manageaudioplayer.cpp trackslistener.cpp elisaapplication.cpp audiowrapper.cpp notificationitem.cpp topnotificationmanager.cpp elisautils.cpp trackdatahelper.cpp abstractfile/abstractfilelistener.cpp abstractfile/abstractfilelisting.cpp file/filelistener.cpp file/localfilelisting.cpp models/albummodel.cpp models/allalbumsmodel.cpp models/allartistsmodel.cpp models/alltracksmodel.cpp models/abstractmediaproxymodel.cpp models/allalbumsproxymodel.cpp models/allartistsproxymodel.cpp models/alltracksproxymodel.cpp models/singleartistproxymodel.cpp models/singlealbumproxymodel.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(elisaLib_SOURCES ${elisaLib_SOURCES} baloo/localbaloofilelisting.cpp baloo/baloolistener.cpp ) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.fileindexer.xml baloo/fileindexer) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.scheduler.xml baloo/scheduler) endif() endif() if (Qt5DBus_FOUND) set(elisaLib_SOURCES ${elisaLib_SOURCES} mpris2/mpris2.cpp mpris2/mediaplayer2.cpp mpris2/mediaplayer2player.cpp ) endif() if (UPNPQT_FOUND) set(elisaLib_SOURCES ${elisaLib_SOURCES} upnp/upnpcontrolcontentdirectory.cpp upnp/upnpcontentdirectorymodel.cpp upnp/upnpcontrolconnectionmanager.cpp upnp/upnpcontrolmediaserver.cpp upnp/didlparser.cpp upnp/upnplistener.cpp upnp/upnpdiscoverallmusic.cpp ) endif() if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.fileindexer.xml baloo/fileindexer) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.scheduler.xml baloo/scheduler) set(elisaLib_SOURCES ${elisaLib_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() kconfig_add_kcfg_files(elisaLib_SOURCES ../src/elisa_settings.kcfgc ) set(elisaLib_SOURCES ${elisaLib_SOURCES} ../src/elisa_core.kcfg ) add_library(elisaLib ${elisaLib_SOURCES}) target_link_libraries(elisaLib LINK_PUBLIC Qt5::Multimedia LINK_PRIVATE Qt5::Core Qt5::Sql Qt5::Widgets KF5::I18n Qt5::Concurrent Qt5::Qml KF5::ConfigCore KF5::ConfigGui KF5::FileMetaData) if (KF5XmlGui_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::XmlGui ) endif() if (KF5ConfigWidgets_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::ConfigWidgets ) endif() if (KF5KCMUtils_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::KCMUtils ) endif() if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::Baloo ) endif() endif() if (Qt5DBus_FOUND) target_link_libraries(elisaLib LINK_PUBLIC Qt5::DBus ) if (KF5DBusAddons_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::DBusAddons ) endif() endif() generate_export_header(elisaLib BASE_NAME ElisaLib EXPORT_FILE_NAME elisaLib_export.h) set_target_properties(elisaLib PROPERTIES VERSION 0.1 SOVERSION 0 EXPORT_NAME ElisaLib ) install(TARGETS elisaLib ${INSTALL_TARGETS_DEFAULT_ARGS}) set(elisaqmlplugin_SOURCES elisaqmlplugin.cpp elisautils.cpp ) add_library(elisaqmlplugin SHARED ${elisaqmlplugin_SOURCES}) target_link_libraries(elisaqmlplugin LINK_PRIVATE Qt5::Quick Qt5::Widgets KF5::FileMetaData KF5::ConfigCore KF5::ConfigGui elisaLib ) set_target_properties(elisaqmlplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/org/kde/elisa) install(TARGETS elisaqmlplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/elisa/) install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/elisa) add_custom_target(copy) add_custom_target(copy2) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/org/kde/elisa) add_custom_command(TARGET copy PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${CMAKE_BINARY_DIR}/bin/org/kde/elisa/) add_custom_command(TARGET copy2 PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/plugins.qmltypes ${CMAKE_BINARY_DIR}/bin/org/kde/elisa/) add_dependencies(elisaqmlplugin copy copy2) if (Qt5Quick_FOUND AND Qt5Widgets_FOUND AND KF5Declarative_FOUND) set(elisa_SOURCES main.cpp windows/WindowsTheme.qml windows/PlatformIntegration.qml android/ElisaMainWindow.qml android/AndroidTheme.qml android/PlatformIntegration.qml qml/ElisaMainWindow.qml qml/ApplicationMenu.qml qml/Theme.qml qml/PlatformIntegration.qml qml/LabelWithToolTip.qml qml/RatingStar.qml qml/PlayListEntry.qml qml/MediaBrowser.qml qml/DraggableItem.qml qml/PassiveNotification.qml qml/TopNotification.qml qml/TopNotificationItem.qml qml/TrackImportNotification.qml qml/HeaderBar.qml qml/NavigationActionBar.qml qml/MediaPlayerControl.qml qml/ContextView.qml qml/ContentView.qml qml/ViewSelector.qml + qml/ViewManager.qml qml/MediaPlayListView.qml qml/MediaTrackDelegate.qml qml/MediaAlbumTrackDelegate.qml qml/MediaTrackMetadataView.qml qml/GridBrowserView.qml qml/GridBrowserDelegate.qml qml/ListBrowserView.qml ) qt5_add_resources(elisa_SOURCES resources.qrc) set_property(SOURCE qrc_resources.cpp PROPERTY SKIP_AUTOMOC ON) set(elisa_ICONS_PNG ../icons/128-apps-elisa.png ../icons/64-apps-elisa.png ../icons/48-apps-elisa.png ../icons/32-apps-elisa.png ../icons/22-apps-elisa.png ../icons/16-apps-elisa.png ) # add icons to application sources, to have them bundled ecm_add_app_icon(elisa_SOURCES ICONS ${elisa_ICONS_PNG}) add_executable(elisa ${elisa_SOURCES}) target_include_directories(elisa PRIVATE ${KDSoap_INCLUDE_DIRS}) target_link_libraries(elisa LINK_PRIVATE elisaLib Qt5::Widgets KF5::Declarative KF5::I18n KF5::Crash KF5::ConfigCore KF5::ConfigGui ) endif() install(TARGETS elisa ${INSTALL_TARGETS_DEFAULT_ARGS}) if (KF5ConfigWidgets_FOUND) add_subdirectory(localFileConfiguration) endif() set(elisaImport_SOURCES elisaimport.cpp elisaimportapplication.cpp ) kconfig_add_kcfg_files(elisaImport_SOURCES ../src/elisa_settings.kcfgc ) set(elisaImport_SOURCES ${elisaImport_SOURCES} ../src/elisa_core.kcfg ) add_executable(elisaImport ${elisaImport_SOURCES}) target_link_libraries(elisaImport LINK_PRIVATE KF5::ConfigCore KF5::ConfigGui KF5::FileMetaData elisaLib ) set(QML_IMPORT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "qml import path" FORCE) diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index ea686561..ca081bc9 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,700 +1,634 @@ /* * 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() } + ViewManager { + id: viewManager + + onSwitchAllAlbumsView: { + listViews.currentIndex = 1 + localArtistsLoader.opacity = 0 + localTracksLoader.opacity = 0 + localAlbumsLoader.opacity = 1 + } + + onSwitchOneAlbumView: { + elisa.singleAlbumProxyModel.loadAlbumData(databaseId) + currentStackView.push(albumView, { + mainTitle: mainTitle, + secondaryTitle: secondaryTitle, + image: imageUrl, + stackView: currentStackView, + }) + } + + onSwitchAllArtistsView: { + listViews.currentIndex = 2 + localArtistsLoader.opacity = 1 + localTracksLoader.opacity = 0 + localAlbumsLoader.opacity = 0 + } + + onSwitchOneArtistView: { + elisa.singleArtistProxyModel.setArtistFilterText(mainTitle) + currentStackView.push(innerAlbumView, { + mainTitle: mainTitle, + secondaryTitle: secondaryTitle, + image: imageUrl, + stackView: currentStackView, + }) + } + + onSwitchAllTracksView: { + listViews.currentIndex = 3 + localArtistsLoader.opacity = 0 + localTracksLoader.opacity = 1 + localAlbumsLoader.opacity = 0 + } + } + ViewSelector { id: listViews Layout.fillHeight: true Layout.preferredWidth: mainWindow.width * 0.11 Layout.maximumWidth: mainWindow.width * 0.11 + + onSwitchView: if (index === 1) { + viewManager.openAllAlbums() + } else if (index === 2) { + viewManager.openAllArtists() + } else if (index === 3) { + viewManager.openAllTracks() + } } 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 + onLoaded: viewManager.allAlbumsViewIsLoaded(item.stackView) + 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, - }) + viewManager.openOneAlbum(localAlbums.stackView, innerMainTitle, innerSecondaryTitle, innerImage, databaseId) } onGoBack: localAlbums.stackView.pop() Binding { target: allAlbumsView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } + + Behavior on opacity { + NumberAnimation { + easing.type: Easing.InOutQuad + duration: 300 + } + } } Loader { id: localArtistsLoader active: opacity > 0 visible: opacity > 0 + opacity: 0 + anchors.fill: parent + onLoaded: viewManager.allArtistsViewIsLoaded(item.stackView) + 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, - stackView: localArtists.stackView - }) - + viewManager.openOneArtist(localArtists.stackView, innerMainTitle, innerImage, 0) } onGoBack: localArtists.stackView.pop() Binding { target: allArtistsView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } + + Behavior on opacity { + NumberAnimation { + easing.type: Easing.InOutQuad + duration: 300 + } + } } Loader { id: localTracksLoader active: opacity > 0 visible: opacity > 0 + opacity: 0 + anchors.fill: parent + onLoaded: viewManager.allTracksViewIsLoaded(item) + 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 } } + + Behavior on opacity { + NumberAnimation { + easing.type: Easing.InOutQuad + duration: 300 + } + } } 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' + name: 'playList' 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 - } }, State { - name: 'allAlbums' - when: listViews.currentIndex === 1 + name: 'browsingViews' + when: listViews.currentIndex !== 0 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 - } - }, - State { - name: 'allArtists' - when: listViews.currentIndex === 2 - 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 - } - }, - 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 - } } ] 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) - innerAlbumGridView.stackView.push(albumView, { - mainTitle: innerMainTitle, - secondaryTitle: innerSecondaryTitle, - image: innerImage, - stackView: innerAlbumGridView.stackView, - }) + viewManager.openOneAlbumFromArtist(stackView, innerMainTitle, innerSecondaryTitle, innerImage, databaseId) } - onGoBack: innerAlbumGridView.stackView.pop() + onGoBack: 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, '') + viewManager.openOneArtist(stackView, name, elisaTheme.artistIcon, 0) } onGoBack: stackView.pop() expandedFilterView: true Binding { target: albumGridView property: 'expandedFilterView' value: persistentSettings.expandedFilterView } onFilterViewChanged: persistentSettings.expandedFilterView = expandedFilterView } } } diff --git a/src/qml/ListBrowserView.qml b/src/qml/ListBrowserView.qml index 0399e8d1..1e675fed 100644 --- a/src/qml/ListBrowserView.qml +++ b/src/qml/ListBrowserView.qml @@ -1,119 +1,120 @@ /* * Copyright 2016-2017 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.Window 2.2 import QtQml.Models 2.2 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.elisa 1.0 FocusScope { id: listView property bool isSubPage: false property alias mainTitle: navigationBar.mainTitle property alias secondaryTitle: navigationBar.secondaryTitle property alias image: navigationBar.image property alias delegate: contentDirectoryView.delegate property alias contentModel: contentDirectoryView.model property alias expandedFilterView: navigationBar.expandedFilterView property alias showRating: navigationBar.showRating property alias allowArtistNavigation: navigationBar.allowArtistNavigation property var delegateWidth: scrollBar.visible ? contentDirectoryView.width - scrollBar.width : contentDirectoryView.width + property var stackView signal goBack() signal showArtist(var name) signal filterViewChanged(bool expandedFilterView) SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } ColumnLayout { anchors.fill: parent spacing: 0 NavigationActionBar { id: navigationBar enableGoBack: listView.isSubPage sortOrder: contentModel.sortedAscending height: elisaTheme.navigationBarHeight Layout.preferredHeight: height Layout.minimumHeight: height Layout.maximumHeight: height Layout.fillWidth: true Binding { target: contentModel property: 'filterText' value: navigationBar.filterText } Binding { target: contentModel property: 'filterRating' value: navigationBar.filterRating } onEnqueue: contentModel.enqueueToPlayList() onFilterViewChanged: listView.filterViewChanged(expandedFilterView) onReplaceAndPlay: contentModel.replaceAndPlayOfPlayList() onGoBack: listView.goBack() onShowArtist: listView.showArtist(listView.contentModel.sourceModel.author) onSort: contentModel.sortModel(order) } Rectangle { color: myPalette.base Layout.fillHeight: true Layout.fillWidth: true ListView { id: contentDirectoryView anchors.topMargin: 20 anchors.fill: parent focus: true ScrollBar.vertical: ScrollBar { id: scrollBar } boundsBehavior: Flickable.StopAtBounds clip: true } } } } diff --git a/src/qml/ViewManager.qml b/src/qml/ViewManager.qml new file mode 100644 index 00000000..c850f9aa --- /dev/null +++ b/src/qml/ViewManager.qml @@ -0,0 +1,174 @@ +/* + * Copyright 2016-2017 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 + +QtObject { + + enum ViewsType { + AllAlbums, + OneAlbum, + AllArtists, + OneArtist, + OneAlbumFromArtist, + AllTracks + } + + property int currentView + property int targetView + + property string targetAlbumTitle + property string targetArtistName + property url targetImageUrl + property int targetDatabaseId + property var targetStackView + + signal switchAllAlbumsView() + signal switchOneAlbumView(var currentStackView, string mainTitle, string imageUrl, string secondaryTitle, int databaseId) + signal switchAllArtistsView() + signal switchOneArtistView(var currentStackView, string mainTitle, string imageUrl, string secondaryTitle, int databaseId) + signal switchAllTracksView() + + function openAllAlbums() + { + console.log('open all albums') + + targetView = ViewManager.ViewsType.AllAlbums + + console.log('target view is ' + targetView) + + if (currentView != targetView) { + switchAllAlbumsView() + } + } + + function openOneAlbum(currentStackView, albumTitle, albumAuthor, albumCover, albumDatabaseId) + { + console.log('open album ' + albumTitle + ' from ' + albumAuthor) + + targetAlbumTitle = albumTitle + targetArtistName = albumAuthor + targetDatabaseId = albumDatabaseId + targetImageUrl = albumCover + targetStackView = currentStackView + + targetView = ViewManager.ViewsType.OneAlbum + + console.log('target view is ' + targetView) + + if (currentView == ViewManager.ViewsType.AllAlbums) { + switchOneAlbumView(targetStackView, targetAlbumTitle, targetImageUrl, targetArtistName, targetDatabaseId) + } else { + switchAllAlbumsView() + } + } + + function openAllArtists() + { + console.log('open all artists') + + targetView = ViewManager.ViewsType.AllArtists + + console.log('target view is ' + targetView) + + if (currentView != targetView) { + switchAllArtistsView() + } + } + + function openOneArtist(currentStackView, artistName, artistImageUrl, artistDatabaseId) + { + console.log('open artist ' + artistName) + targetArtistName = artistName + targetDatabaseId = artistDatabaseId + targetImageUrl = artistImageUrl + targetStackView = currentStackView + + targetView = ViewManager.ViewsType.OneArtist + + console.log('target view is ' + targetView) + + if (currentView == ViewManager.ViewsType.AllArtists) { + switchOneArtistView(targetStackView, targetArtistName, targetImageUrl, '', targetDatabaseId) + } else { + switchAllArtistsView() + } + } + + function openOneAlbumFromArtist(currentStackView, albumTitle, albumAuthor, albumCover, albumDatabaseId) + { + targetAlbumTitle = albumTitle + targetArtistName = albumAuthor + targetDatabaseId = albumDatabaseId + targetImageUrl = albumCover + targetStackView = currentStackView + + targetView = ViewManager.ViewsType.OneAlbumFromArtist + + if (currentView == ViewManager.ViewsType.AllArtists) { + switchOneAlbumView(targetStackView, targetAlbumTitle, targetImageUrl, targetArtistName, targetDatabaseId) + } else { + switchAllAlbumsView() + } + } + + function openAllTracks() + { + targetView = ViewManager.ViewsType.AllTracks + if (currentView != targetView) { + switchAllTracksView() + } + } + + function allAlbumsViewIsLoaded(currentStackView) + { + console.log('all albums view is loaded and target view is ' + targetView) + targetStackView = currentStackView + currentView = ViewManager.ViewsType.AllAlbums + if (targetView == ViewManager.ViewsType.OneAlbum) { + switchOneAlbumView(targetStackView, targetAlbumTitle, targetImageUrl, targetArtistName, targetDatabaseId) + } + } + + function oneAlbumViewIsLoaded(oneAlbumView) + { + currentView = ViewManager.ViewsType.OneAlbum + } + + function allArtistsViewIsLoaded(currentStackView) + { + console.log('all artists view is loaded and target view is ' + targetView) + targetStackView = currentStackView + currentView = ViewManager.ViewsType.AllArtists + if (targetView == ViewManager.ViewsType.OneArtist) { + console.log('switch to artist: ' + targetArtistName) + switchOneArtistView(targetStackView, targetArtistName, targetImageUrl, '', targetDatabaseId) + } + } + + function oneArtistViewIsLoaded(oneArtistView) + { + currentView = ViewManager.ViewsType.OneArtist + } + + function allTracksViewIsLoaded(allTracksView) + { + currentView = ViewManager.ViewsType.AllTracks + } +} diff --git a/src/qml/ViewSelector.qml b/src/qml/ViewSelector.qml index fcb35aba..bda6cd68 100644 --- a/src/qml/ViewSelector.qml +++ b/src/qml/ViewSelector.qml @@ -1,182 +1,186 @@ /* * Copyright 2016-2017 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 QtQml.Models 2.2 import QtGraphicalEffects 1.0 FocusScope { id: rootFocusScope + property alias currentIndex: viewModeView.currentIndex + signal switchView(int index) + Rectangle { anchors.fill: parent color: myPalette.base border { color: (rootFocusScope.activeFocus ? myPalette.highlight : "transparent") width: 1 } ScrollView { focus: true anchors.fill: parent clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ListView { id: viewModeView focus: true z: 2 anchors.topMargin: elisaTheme.layoutHorizontalMargin * 2 model: DelegateModel { id: pageDelegateModel model: ListModel { id: pageModel } delegate: MouseArea { id: item height: elisaTheme.viewSelectorDelegateHeight * 1.4 width: viewModeView.width hoverEnabled: true acceptedButtons: Qt.LeftButton Image { id: viewIcon z: 2 anchors { verticalCenter: parent.verticalCenter leftMargin: elisaTheme.layoutHorizontalMargin left: parent.left } height: elisaTheme.viewSelectorDelegateHeight width: elisaTheme.viewSelectorDelegateHeight sourceSize { width: elisaTheme.viewSelectorDelegateHeight height: elisaTheme.viewSelectorDelegateHeight } source: iconName visible: false } ColorOverlay { source: viewIcon z: 2 anchors { verticalCenter: parent.verticalCenter leftMargin: elisaTheme.layoutHorizontalMargin left: parent.left } height: elisaTheme.viewSelectorDelegateHeight width: elisaTheme.viewSelectorDelegateHeight color: (index === viewModeView.currentIndex || item.containsMouse ? myPalette.highlight : "transparent") Behavior on color { ColorAnimation { duration: 300 } } } LabelWithToolTip { id: nameLabel z: 2 anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: elisaTheme.layoutHorizontalMargin anchors.left: viewIcon.right anchors.right: parent.right anchors.rightMargin: elisaTheme.layoutHorizontalMargin verticalAlignment: "AlignVCenter" font.pointSize: elisaTheme.defaultFontPointSize * 1.4 text: model.name elide: Text.ElideRight color: (viewModeView.currentIndex === index || item.containsMouse ? myPalette.highlight : myPalette.text) Behavior on color { ColorAnimation { duration: 300 } } } onClicked: { viewModeView.currentIndex = index rootFocusScope.focus = true + switchView(index) } } Component.onCompleted: { pageModel.insert(0, {"name": i18nc("Title of the view of the playlist", "Now Playing"), "iconName": elisaTheme.playlistIcon}) pageModel.insert(1, {"name": i18nc("Title of the view of all albums", "Albums"), "iconName": elisaTheme.albumIcon}) pageModel.insert(2, {"name": i18nc("Title of the view of all artists", "Artists"), "iconName": elisaTheme.artistIcon}) pageModel.insert(3, {"name": i18nc("Title of the view of all tracks", "Tracks"), "iconName": elisaTheme.tracksIcon}) viewModeView.currentIndex = 1 } } footer: MouseArea { width: viewModeView.width height: viewModeView.height - y acceptedButtons: Qt.LeftButton onClicked: { rootFocusScope.focus = true } } } } Behavior on border.color { ColorAnimation { duration: 300 } } } } diff --git a/src/resources.qrc b/src/resources.qrc index c84f7112..659a9831 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,42 +1,43 @@ qml/MediaPlayerControl.qml qml/RatingStar.qml qml/MediaPlayListView.qml qml/ElisaMainWindow.qml qml/ApplicationMenu.qml qml/HeaderBar.qml qml/ContextView.qml qml/ContentView.qml qml/DraggableItem.qml qml/PassiveNotification.qml qml/NavigationActionBar.qml qml/PlayListEntry.qml qml/MediaBrowser.qml qml/Theme.qml qml/PlatformIntegration.qml qml/LabelWithToolTip.qml qml/TopNotification.qml qml/TrackImportNotification.qml qml/TopNotificationItem.qml qml/ViewSelector.qml qml/MediaTrackDelegate.qml qml/MediaAlbumTrackDelegate.qml qml/MediaTrackMetadataView.qml qml/GridBrowserView.qml qml/GridBrowserDelegate.qml qml/ListBrowserView.qml qtquickcontrols2.conf background.png + qml/ViewManager.qml windows/WindowsTheme.qml windows/PlatformIntegration.qml windows/LabelWithToolTip.qml android/ElisaMainWindow.qml android/AndroidTheme.qml android/PlatformIntegration.qml