diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e5ce108f..3451f35a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,324 +1,325 @@ include_directories(${elisa_BINARY_DIR}) if (Qt5Quick_FOUND AND Qt5Widgets_FOUND) set(elisa_SOURCES main.cpp 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 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/MediaPlayListView.qml qml/MediaAlbumView.qml qml/MediaAllTracksView.qml qml/MediaTrackDelegate.qml qml/MediaAlbumTrackDelegate.qml qml/MediaTrackMetadataView.qml qml/GridBrowserView.qml qml/GridBrowserDelegate.qml ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(elisa_SOURCES ${elisa_SOURCES} baloo/localbaloofilelisting.cpp baloo/baloolistener.cpp ) qt5_add_dbus_interface(elisa_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.fileindexer.xml baloo/fileindexer) qt5_add_dbus_interface(elisa_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.scheduler.xml baloo/scheduler) endif() endif() if (Qt5DBus_FOUND) set(elisa_SOURCES ${elisa_SOURCES} mpris2/mpris2.cpp mpris2/mediaplayer2.cpp mpris2/mediaplayer2player.cpp ) endif() if (UPNPQT_FOUND) set(elisa_SOURCES ${elisa_SOURCES} upnp/upnpcontrolcontentdirectory.cpp upnp/upnpcontentdirectorymodel.cpp upnp/upnpcontrolconnectionmanager.cpp upnp/upnpcontrolmediaserver.cpp upnp/didlparser.cpp upnp/upnplistener.cpp upnp/upnpdiscoverallmusic.cpp ) endif() kconfig_add_kcfg_files(elisa_SOURCES elisa_settings.kcfgc) set(elisa_SOURCES ${elisa_SOURCES} elisa_core.kcfg ) 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}) set_target_properties(elisa PROPERTIES LINK_FLAGS "-Wl,--no-undefined") target_include_directories(elisa PRIVATE ${KDSoap_INCLUDE_DIRS}) target_link_libraries(elisa LINK_PRIVATE Qt5::Core Qt5::Quick Qt5::Gui Qt5::Widgets Qt5::Multimedia Qt5::Svg Qt5::Sql Qt5::Concurrent KF5::I18n KF5::ConfigCore KF5::ConfigGui KF5::FileMetaData KF5::CoreAddons ) if (Qt5DBus_FOUND) target_link_libraries(elisa LINK_PRIVATE Qt5::DBus ) endif() if (KF5KCMUtils_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::KCMUtils ) endif() if (Qt5AndroidExtras_FOUND) target_link_libraries(elisa LINK_PRIVATE Qt5::AndroidExtras ) endif() if (KF5Baloo_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::Baloo ) endif() if (KF5Declarative_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::Declarative ) endif() if (KF5XmlGui_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::XmlGui ) endif() if (KF5Crash_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::Crash ) endif() if (UPNPQT_FOUND) target_link_libraries(elisa LINK_PRIVATE UPNP::upnpQt Qt5::Network ) endif() if (KF5DBusAddons_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::DBusAddons ) endif() endif() install(TARGETS elisa ${INSTALL_TARGETS_DEFAULT_ARGS}) if (KF5ConfigWidgets_FOUND) add_subdirectory(localFileConfiguration) endif() set(elisaImport_SOURCES elisaimport.cpp elisaimportapplication.cpp elisaapplication.cpp mediaplaylist.cpp musicalbum.cpp musicaudiotrack.cpp musicartist.cpp manageaudioplayer.cpp progressindicator.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 databaseinterface.cpp musiclistenersmanager.cpp trackslistener.cpp notificationitem.cpp topnotificationmanager.cpp elisautils.cpp abstractfile/abstractfilelistener.cpp abstractfile/abstractfilelisting.cpp file/filelistener.cpp file/localfilelisting.cpp audiowrapper.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(elisaImport_SOURCES ${elisaImport_SOURCES} baloo/localbaloofilelisting.cpp baloo/baloolistener.cpp ) qt5_add_dbus_interface(elisaImport_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.fileindexer.xml baloo/fileindexer) qt5_add_dbus_interface(elisaImport_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.scheduler.xml baloo/scheduler) endif() endif() kconfig_add_kcfg_files(elisaImport_SOURCES elisa_settings.kcfgc) set(elisaImport_SOURCES ${elisaImport_SOURCES} elisa_core.kcfg ) add_executable(elisaImport ${elisaImport_SOURCES}) set_target_properties(elisaImport PROPERTIES LINK_FLAGS "-Wl,--no-undefined") target_link_libraries(elisaImport LINK_PRIVATE Qt5::Core Qt5::Sql Qt5::Multimedia Qt5::Widgets Qt5::Concurrent KF5::I18n KF5::ConfigCore KF5::ConfigGui KF5::FileMetaData ) if (KF5XmlGui_FOUND) target_link_libraries(elisaImport LINK_PRIVATE KF5::XmlGui ) endif() if (KF5KCMUtils_FOUND) target_link_libraries(elisaImport LINK_PRIVATE KF5::KCMUtils ) endif() if (KF5Baloo_FOUND) target_link_libraries(elisaImport LINK_PRIVATE KF5::Baloo ) endif() diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml new file mode 100644 index 00000000..607933c4 --- /dev/null +++ b/src/qml/ContentView.qml @@ -0,0 +1,611 @@ +/* + * 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 { + spacing: 0 + + property alias playList: playList + + function goBack() { + localAlbums.goBack() + localArtists.goBack() + } + + ViewSelector { + id: listViews + + Layout.fillHeight: true + Layout.preferredWidth: mainWindow.width * 0.15 + Layout.maximumWidth: mainWindow.width * 0.15 + } + + 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 + + BusyIndicator { + id: busyScanningMusic + hoverEnabled: false + anchors.fill: parent + + anchors.leftMargin: parent.width / 3 + anchors.rightMargin: parent.width / 3 + anchors.topMargin: parent.height / 3 + anchors.bottomMargin: parent.height / 3 + + opacity: 0.8 + + z: 2 + } + + Loader { + sourceComponent: Binding { + target: busyScanningMusic + property: 'running' + value: elisa.musicManager.indexerBusy + } + + active: elisa.musicManager !== undefined + } + + 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, { + stackView: localAlbums.stackView, + albumName: innerMainTitle, + artistName: innerSecondaryTitle, + albumArtUrl: innerImage, + }) + } + onGoBack: localAlbums.stackView.pop() + + Binding { + target: allAlbumsView + property: 'filterState' + value: persistentSettings.filterState + } + + onFilterViewChanged: persistentSettings.filterState = filterState + } + + visible: opacity > 0 + } + + 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 + }) + + } + onGoBack: localArtists.stackView.pop() + + Binding { + target: allArtistsView + property: 'filterState' + value: persistentSettings.filterState + } + + onFilterViewChanged: persistentSettings.filterState = filterState + } + + visible: opacity > 0 + } + + MediaBrowser { + id: localTracks + + focus: true + + anchors { + fill: parent + + leftMargin: elisaTheme.layoutHorizontalMargin + rightMargin: elisaTheme.layoutHorizontalMargin + } + + firstPage: MediaAllTracksView { + id: allTracksView + focus: true + stackView: localTracks.stackView + + contentModel: elisa.allTracksProxyModel + + Binding { + target: allTracksView + property: 'filterState' + value: persistentSettings.filterState + } + + onFilterViewChanged: persistentSettings.filterState = filterState + } + + visible: opacity > 0 + } + + 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 + + randomPlayChecked: elisa.mediaPlayList.randomPlay + repeatPlayChecked: elisa.mediaPlayList.repeatPlay + + Layout.fillHeight: true + Layout.leftMargin: elisaTheme.layoutHorizontalMargin + Layout.rightMargin: elisaTheme.layoutHorizontalMargin + + Layout.minimumWidth: contentZone.width + Layout.maximumWidth: contentZone.width + Layout.preferredWidth: contentZone.width + + onStartPlayback: manageAudioPlayer.ensurePlay() + + onPausePlayback: manageAudioPlayer.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: myHeaderBarManager.artist + albumName: myHeaderBarManager.album + albumArtUrl: myHeaderBarManager.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 + } + }, + 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 + } + }, + 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 + } + }, + 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 + } + } + ] + transitions: Transition { + NumberAnimation { + properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" + easing.type: Easing.InOutQuad + duration: 300 + } + } + } + + + Component { + id: innerAlbumView + + GridBrowserView { + id: innerAlbumGridView + property var stackView + + contentModel: elisa.singleArtistProxyModel + + isSubPage: true + + onOpen: { + elisa.singleAlbumProxyModel.loadAlbumData(databaseId) + localArtists.stackView.push(albumView, { + stackView: localArtists.stackView, + albumName: innerMainTitle, + artistName: innerSecondaryTitle, + albumArtUrl: innerImage, + }) + } + onGoBack: stackView.pop() + + Binding { + target: innerAlbumGridView + property: 'filterState' + value: persistentSettings.filterState + } + + onFilterViewChanged: persistentSettings.filterState = filterState + } + } + + Component { + id: albumView + + MediaAlbumView { + id: albumGridView + property var stackView + + contentModel: elisa.singleAlbumProxyModel + + 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() + + Binding { + target: albumGridView + property: 'filterState' + value: persistentSettings.filterState + } + + onFilterViewChanged: persistentSettings.filterState = filterState + } + } +} diff --git a/src/qml/ElisaMainWindow.qml b/src/qml/ElisaMainWindow.qml index df190fe8..92da6fc8 100644 --- a/src/qml/ElisaMainWindow.qml +++ b/src/qml/ElisaMainWindow.qml @@ -1,941 +1,361 @@ /* * 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.Controls 1.4 as Controls1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import org.kde.elisa 1.0 import Qt.labs.settings 1.0 ApplicationWindow { id: mainWindow visible: true minimumWidth: 1000 minimumHeight: 600 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true x: persistentSettings.x y: persistentSettings.y width: persistentSettings.width height: persistentSettings.height title: i18n("Elisa") property var goBackAction: elisa.action("go_back") Controls1.Action { shortcut: goBackAction.shortcut - onTriggered: { - localAlbums.goBack() - localArtists.goBack() - } + onTriggered: contentView.goBack() } Controls1.Action { id: applicationMenuAction text: i18nc("open application menu", "Application Menu") iconName: "application-menu" onTriggered: applicationMenu.popup() } ApplicationMenu { id: applicationMenu } SystemPalette { id: myPalette colorGroup: SystemPalette.Active } Theme { id: elisaTheme } Settings { id: persistentSettings property int x property int y property int width : 1000 property int height : 600 property var playListState property var playListControlerState property var audioPlayerState property double playControlItemVolume : 100.0 property bool playControlItemMuted : false property string filterState } Connections { target: Qt.application onAboutToQuit: { persistentSettings.x = mainWindow.x; persistentSettings.y = mainWindow.y; persistentSettings.width = mainWindow.width; persistentSettings.height = mainWindow.height; persistentSettings.playListState = elisa.mediaPlayList.persistentState; persistentSettings.playListControlerState = elisa.mediaPlayList.persistentState; persistentSettings.audioPlayerState = manageAudioPlayer.persistentState persistentSettings.playControlItemVolume = headerBar.playerControl.volume persistentSettings.playControlItemMuted = headerBar.playerControl.muted } } PlatformIntegration { id: platformInterface playListModel: elisa.mediaPlayList playListControler: elisa.mediaPlayList audioPlayerManager: manageAudioPlayer headerBarManager: myHeaderBarManager manageMediaPlayerControl: myPlayControlManager player: elisa.audioPlayer onRaisePlayer: { mainWindow.show() mainWindow.raise() mainWindow.requestActivate() } } Connections { target: elisa.audioPlayer onVolumeChanged: headerBar.playerControl.volume = volume onMutedChanged: headerBar.playerControl.muted = muted onPlaying: { myPlayControlManager.playerPlaying() } onPaused: { myPlayControlManager.playerPaused() } onStopped: { myPlayControlManager.playerStopped() } } Connections { target: elisa.mediaPlayList onPlayListLoadFailed: { messageNotification.showNotification(i18nc("message of passive notification when playlist load failed", "Load of playlist failed"), 3000) } onEnsurePlay: manageAudioPlayer.ensurePlay() onPlayListFinished: manageAudioPlayer.playListFinished() } ManageHeaderBar { id: myHeaderBarManager playListModel: elisa.mediaPlayList currentTrack: elisa.mediaPlayList.currentTrack artistRole: MediaPlayList.ArtistRole titleRole: MediaPlayList.TitleRole albumRole: MediaPlayList.AlbumRole imageRole: MediaPlayList.ImageRole isValidRole: MediaPlayList.IsValidRole } ManageAudioPlayer { id: manageAudioPlayer currentTrack: elisa.mediaPlayList.currentTrack playListModel: elisa.mediaPlayList urlRole: MediaPlayList.ResourceRole isPlayingRole: MediaPlayList.IsPlayingRole titleRole: MediaPlayList.TitleRole artistNameRole: MediaPlayList.ArtistRole albumNameRole: MediaPlayList.AlbumRole playerStatus: elisa.audioPlayer.status playerPlaybackState: elisa.audioPlayer.playbackState playerError: elisa.audioPlayer.error audioDuration: elisa.audioPlayer.duration playerIsSeekable: elisa.audioPlayer.seekable playerPosition: elisa.audioPlayer.position persistentState: persistentSettings.audioPlayerState onPlayerPlay: elisa.audioPlayer.play() onPlayerPause: elisa.audioPlayer.pause() onPlayerStop: elisa.audioPlayer.stop() onSkipNextTrack: elisa.mediaPlayList.skipNextTrack() onSeek: elisa.audioPlayer.seek(position) onSourceInError: { elisa.mediaPlayList.trackInError(source, playerError) elisa.musicManager.playBackError(source, playerError) } onDisplayTrackError: messageNotification.showNotification(i18n("Error when playing %1", "" + fileName), 3000) } ManageMediaPlayerControl { id: myPlayControlManager playListModel: elisa.mediaPlayList currentTrack: elisa.mediaPlayList.currentTrack } PassiveNotification { id: messageNotification } Rectangle { color: myPalette.base anchors.fill: parent ColumnLayout { anchors.fill: parent spacing: 0 Item { Layout.preferredHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.minimumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.maximumHeight: mainWindow.height * 0.2 + elisaTheme.mediaPlayerControlHeight Layout.fillWidth: true HeaderBar { id: headerBar focus: true anchors.fill: parent tracksCount: myHeaderBarManager.remainingTracks album: myHeaderBarManager.album title: myHeaderBarManager.title artist: myHeaderBarManager.artist image: myHeaderBarManager.image ratingVisible: false playerControl.duration: elisa.audioPlayer.duration playerControl.seekable: elisa.audioPlayer.seekable playerControl.volume: persistentSettings.playControlItemVolume playerControl.muted: persistentSettings.playControlItemMuted playerControl.position: elisa.audioPlayer.position playerControl.skipBackwardEnabled: myPlayControlManager.skipBackwardControlEnabled playerControl.skipForwardEnabled: myPlayControlManager.skipForwardControlEnabled playerControl.playEnabled: myPlayControlManager.playControlEnabled playerControl.isPlaying: myPlayControlManager.musicPlaying playerControl.onSeek: elisa.audioPlayer.seek(position) playerControl.onPlay: manageAudioPlayer.playPause() playerControl.onPause: manageAudioPlayer.playPause() playerControl.onPlayPrevious: elisa.mediaPlayList.skipPreviousTrack() playerControl.onPlayNext: elisa.mediaPlayList.skipNextTrack() Controls1.ToolButton { id: menuButton action: applicationMenuAction z: 2 anchors { right: parent.right top: parent.top rightMargin: elisaTheme.layoutHorizontalMargin * 3 topMargin: elisaTheme.layoutHorizontalMargin * 3 } } Rectangle { anchors.fill: menuButton z: 1 radius: width / 2 color: myPalette.window } TrackImportNotification { id: importedTracksCountNotification anchors { right: menuButton.left top: menuButton.top bottom: menuButton.bottom rightMargin: elisaTheme.layoutHorizontalMargin * 3 } } Binding { target: importedTracksCountNotification property: 'musicManager' value: elisa.musicManager when: elisa.musicManager !== undefined } Loader { sourceComponent: Binding { target: importedTracksCountNotification property: 'indexingRunning' value: elisa.musicManager.indexingRunning } active: elisa.musicManager !== undefined } Loader { sourceComponent: Binding { target: importedTracksCountNotification property: 'importedTracksCount' value: elisa.musicManager.importedTracksCount } active: elisa.musicManager !== undefined } } } - RowLayout { + ContentView { + id: contentView Layout.fillHeight: true Layout.fillWidth: true - spacing: 0 - - ViewSelector { - id: listViews - - Layout.fillHeight: true - Layout.preferredWidth: mainWindow.width * 0.15 - Layout.maximumWidth: mainWindow.width * 0.15 - } - - 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 - - BusyIndicator { - id: busyScanningMusic - hoverEnabled: false - anchors.fill: parent - - anchors.leftMargin: parent.width / 3 - anchors.rightMargin: parent.width / 3 - anchors.topMargin: parent.height / 3 - anchors.bottomMargin: parent.height / 3 - - opacity: 0.8 - - visible: running - - z: 2 - } - - Loader { - sourceComponent: Binding { - target: busyScanningMusic - property: 'running' - value: elisa.musicManager.indexerBusy - } - - active: elisa.musicManager !== undefined - } - - 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, { - stackView: localAlbums.stackView, - albumName: innerMainTitle, - artistName: innerSecondaryTitle, - albumArtUrl: innerImage, - }) - } - onGoBack: localAlbums.stackView.pop() - - Binding { - target: allAlbumsView - property: 'filterState' - value: persistentSettings.filterState - } - - onFilterViewChanged: persistentSettings.filterState = filterState - } - - visible: opacity > 0 - } - - 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 - }) - - } - onGoBack: localArtists.stackView.pop() - - Binding { - target: allArtistsView - property: 'filterState' - value: persistentSettings.filterState - } - - onFilterViewChanged: persistentSettings.filterState = filterState - } - - visible: opacity > 0 - } - - MediaBrowser { - id: localTracks - - focus: true - - anchors { - fill: parent - - leftMargin: elisaTheme.layoutHorizontalMargin - rightMargin: elisaTheme.layoutHorizontalMargin - } - - firstPage: MediaAllTracksView { - id: allTracksView - focus: true - stackView: localTracks.stackView - - contentModel: elisa.allTracksProxyModel - - Binding { - target: allTracksView - property: 'filterState' - value: persistentSettings.filterState - } - - onFilterViewChanged: persistentSettings.filterState = filterState - } - - visible: opacity > 0 - } - - 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 - - randomPlayChecked: elisa.mediaPlayList.randomPlay - repeatPlayChecked: elisa.mediaPlayList.repeatPlay - - Layout.fillHeight: true - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.rightMargin: elisaTheme.layoutHorizontalMargin - - Layout.minimumWidth: contentZone.width - Layout.maximumWidth: contentZone.width - Layout.preferredWidth: contentZone.width - - onStartPlayback: manageAudioPlayer.ensurePlay() - - onPausePlayback: manageAudioPlayer.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: myHeaderBarManager.artist - albumName: myHeaderBarManager.album - albumArtUrl: myHeaderBarManager.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 - } - }, - 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 - } - }, - 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 - } - }, - 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 - } - } - ] - transitions: Transition { - NumberAnimation { - properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" - easing.type: Easing.InOutQuad - duration: 300 - } - } - } - } - } - } - - Component { - id: innerAlbumView - - GridBrowserView { - id: innerAlbumGridView - property var stackView - - contentModel: elisa.singleArtistProxyModel - - isSubPage: true - - onOpen: { - elisa.singleAlbumProxyModel.loadAlbumData(databaseId) - localArtists.stackView.push(albumView, { - stackView: localArtists.stackView, - albumName: innerMainTitle, - artistName: innerSecondaryTitle, - albumArtUrl: innerImage, - }) - } - onGoBack: stackView.pop() - - Binding { - target: innerAlbumGridView - property: 'filterState' - value: persistentSettings.filterState } - - onFilterViewChanged: persistentSettings.filterState = filterState - } - } - - Component { - id: albumView - - MediaAlbumView { - id: albumGridView - property var stackView - - contentModel: elisa.singleAlbumProxyModel - - 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() - - Binding { - target: albumGridView - property: 'filterState' - value: persistentSettings.filterState - } - - onFilterViewChanged: persistentSettings.filterState = filterState } } Component.onCompleted: { elisa.initialize() - elisa.mediaPlayList.randomPlay = Qt.binding(function() { return playList.randomPlayChecked }) - elisa.mediaPlayList.repeatPlay = Qt.binding(function() { return playList.repeatPlayChecked }) - myPlayControlManager.randomOrContinuePlay = Qt.binding(function() { return playList.randomPlayChecked || playList.repeatPlayChecked }) + elisa.mediaPlayList.randomPlay = Qt.binding(function() { return contentView.playList.randomPlayChecked }) + elisa.mediaPlayList.repeatPlay = Qt.binding(function() { return contentView.playList.repeatPlayChecked }) + myPlayControlManager.randomOrContinuePlay = Qt.binding(function() { return contentView.playList.randomPlayChecked || contentView.playList.repeatPlayChecked }) if (persistentSettings.playListState) { elisa.mediaPlayList.persistentState = persistentSettings.playListState } elisa.audioPlayer.muted = Qt.binding(function() { return headerBar.playerControl.muted }) elisa.audioPlayer.volume = Qt.binding(function() { return headerBar.playerControl.volume }) elisa.audioPlayer.source = Qt.binding(function() { return manageAudioPlayer.playerSource }) elisa.mediaPlayList.enqueue(elisa.arguments) volume: headerBar.playerControl.volume } } diff --git a/src/resources.qrc b/src/resources.qrc index a920a161..2e837519 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,42 +1,43 @@ qml/MediaPlayerControl.qml qml/RatingStar.qml qml/MediaPlayListView.qml qml/MediaAlbumView.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/MediaAllTracksView.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 qtquickcontrols2.conf background.png windows/WindowsTheme.qml windows/PlatformIntegration.qml windows/LabelWithToolTip.qml android/ElisaMainWindow.qml android/AndroidTheme.qml android/PlatformIntegration.qml